Skip to content
This repository has been archived by the owner on Dec 6, 2024. It is now read-only.

added dark mode and light mode #58

Merged
merged 13 commits into from
Dec 2, 2024
8 changes: 4 additions & 4 deletions epress_backend/src/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const pinToIPFS = async (product: Product) => {
const url = "https://api.pinata.cloud/pinning/pinFileToIPFS";

const blob = new Blob([JSON.stringify(product, null, 2)], {
type: "application/json"
type: "application/json",
});

const file = new File([blob], `${product.product_id}.txt`);
Expand All @@ -29,9 +29,9 @@ const pinToIPFS = async (product: Product) => {
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${PINATA_JWT}`
Authorization: `Bearer ${PINATA_JWT}`,
},
body: data
body: data,
});

return await response.json();
Expand All @@ -52,7 +52,7 @@ export const submitProduct = async (req: Request, res: Response) => {
image,
manufacturer,
manufactureDate,
expiryDate
expiryDate,
};

try {
Expand Down
16 changes: 8 additions & 8 deletions epress_backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */

/* Language and Environment */
"target": "ES2015", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"target": "ES2015" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
Expand All @@ -25,9 +25,9 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */

/* Modules */
"module": "ES6", /* Specify what module code is generated. */
"module": "ES6" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
Expand Down Expand Up @@ -55,7 +55,7 @@
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
// "removeComments": true, /* Disable emitting comments. */
//"noEmit": false, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
Expand All @@ -77,12 +77,12 @@
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
Expand All @@ -104,7 +104,7 @@

/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["**/*", "./**/*.d.ts"],
"exclude": ["node_modules"]
Expand Down
12 changes: 9 additions & 3 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { bowlby_one, poppins, roboto } from './fonts';
import { StarknetProvider } from '@/components/StarknetProvider';
import NavBar from '@/components/Navbar';
import Footer from '@/components/Footer';
import { themeScript } from './theme-script';

const inter = Inter({ subsets: ['latin'] });

Expand All @@ -19,15 +20,20 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
<html lang="en">
<html lang="en" suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html: themeScript }} />
</head>
<body
className={`${inter.className} ${poppins.variable}
${bowlby_one.variable} ${roboto.variable} bg-secondary`}
${bowlby_one.variable} ${roboto.variable}
bg-secondary-light dark:bg-secondary
text-textPrimary-light dark:text-textPrimary
transition-colors duration-200`}
>
<StarknetProvider>
<NavBar />
{children}

<Footer />
</StarknetProvider>
</body>
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/app/theme-script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const themeScript = `
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
`;
27 changes: 14 additions & 13 deletions frontend/src/components/ContentSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ const ContentSection = () => (

const ContentHeader = () => (
<div className="my-28 text-center px-4 md:px-0">
<div className={`${poppins.variable} text-center text-textPrimary mb-6`}>
<div
className={`${poppins.variable} text-center text-secondary dark:text-textPrimary mb-6`}
>
<h2
className={`${poppins.variable} hidden md:block md:text-2xl lg:text-4xl
font-semibold font-poppins`}
Expand All @@ -41,7 +43,7 @@ const ContentHeader = () => (
</div>
<div>
<p
className={`${roboto.variable} hidden md:block text-textFaded text-base font-normal font-roboto`}
className={`${roboto.variable} hidden md:block text-secondary/70 dark:text-textFaded text-base font-normal font-roboto`}
>
SCANGUARD is a project aiming to protect consumers from counterfeit
products by allowing easy authentication with a{' '}
Expand Down Expand Up @@ -74,14 +76,13 @@ const ContentCTA = () => (
className={`flex flex-col items-center justify-center gap-6
text-textFaded ${roboto.variable} text-base text-center font-roboto`}
>
<div>
<h3
className={`${poppins.variable} text-textPrimary text-2xl text-center
font-semibold mb-1 font-poppins`}
>
HOW IT WORKS
</h3>
<p>Super easy steps to use scanguard</p>
<div className="grid gap-8">
<h2 className="text-3xl font-bold text-textPrimary-light dark:text-textPrimary">
How It Works
</h2>
<p className="text-textFaded-light dark:text-textFaded">
Simple steps to verify product authenticity
</p>
</div>
<AuthenticityParagraph />
<ScanButton />
Expand Down Expand Up @@ -161,7 +162,7 @@ const GuideContent = () => (
const GuideContentHeader = () => (
<div className="hidden lg:block">
<h4
className={`${poppins.variable} text-2xl lg:text-[2.5rem] text-textPrimary
className={`${poppins.variable} text-2xl lg:text-[2.5rem] text-textPrimary-light dark:text-textPrimary
capitalize font-semibold`}
>
The edge ScanGuard offers
Expand All @@ -174,8 +175,8 @@ const GuideContentHeaderMobile = () => (
<div className="grid place-items-center lg:hidden">
<div>
<h4
className={`${poppins.variable} text-2xl lg:text-[2.5rem] text-textPrimary
capitalize font-semibold text-center`}
className={`${poppins.variable} text-2xl lg:text-[2.5rem] text-textPrimary-light dark:text-textPrimary
capitalize font-semibold text-center`}
>
The edge <br /> ScanGuard offers
</h4>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/HeroSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const HeroSection = () => (
const HeroCTA = (props: any) => (
<div className="flex flex-col justify-center items-center gap-6 max-w-lg">
<div
className={`${poppins.variable} text-textPrimary
className={`${poppins.variable} text-secondary dark:text-textPrimary
text-center text-5xl font-medium font-poppins`}
>
<h1 className="hidden md:block">
Expand All @@ -26,7 +26,7 @@ const HeroCTA = (props: any) => (
</h1>
</div>
<div
className={`${roboto.variable} text-textFaded text-center
className={`${roboto.variable} text-secondary/70 dark:text-textFaded text-center
text-base font-normal leading-7 font-roboto`}
>
<p className="hidden md:block">
Expand Down
19 changes: 12 additions & 7 deletions frontend/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
'use client';

import { BrandImage } from '@/assets/landing-page';
import Link from 'next/link';
import ConnectWallet from './ConnectWallet';
import { roboto } from '@/app/fonts';
import ThemeToggle from './ThemeToggle';
import { ScanIcon } from '@/assets/icons';

const NavBar = (props: any) => (
<nav
className={`flex items-center justify-between px-4 lg:px-20
xl:px-0 2xl:px-0 py-4 mb-12 container mx-auto font-roboto
bg-[#1e1e1e]/80 backdrop-blur-sm sticky top-0 z-10
bg-white/90 dark:bg-[#1e1e1e]/80 backdrop-blur-sm sticky top-0 z-10
lg:static lg:bg-none`}
{...props}
>
Expand All @@ -17,29 +21,30 @@ const NavBar = (props: any) => (
<BrandImage />
</div>
<div className="md:hidden">
<p className="text-textPrimary text-lg font-normal font-bowlby">
<p className="text-textPrimary-light dark:text-textPrimary text-lg font-bold font-bowlby">
ScanGuard
</p>
</div>
</Link>
<NavLinks />
</div>
<div>
<div className="flex gap-5">
<ThemeToggle />
<ScanIcon className="text-textPrimary-light dark:text-textPrimary" />
<ConnectWallet />
</div>
</nav>
);

const NavLinks = () => (
<ul
className={`${roboto.variable} text-sm text-textPrimary leading-normal
className={`${roboto.variable} text-sm text-textPrimary-light dark:text-textPrimary leading-normal
font-roboto uppercase hidden py-3 gap-6 items-center lg:flex`}
>
{['home', 'anout', 'contact'].map((item, index, array) => (
{['home', 'about', 'contact'].map((item, index, array) => (
<li
key={item}
className={`${index !== array.length - 1 ? 'border-r-2 border-primary/[.12] pr-6' : ''}
`}
className={`${index !== array.length - 1 ? 'border-r-2 border-primary/[.12] pr-6' : ''}`}
>
<Link href={`${item === 'home' ? '/' : item}`}>{item}</Link>
</li>
Expand Down
45 changes: 45 additions & 0 deletions frontend/src/components/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createContext, useContext, useEffect, useState } from 'react';

type Theme = 'light' | 'dark';

type ThemeContextType = {
theme: Theme;
toggleTheme: () => void;
};

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>('dark');

useEffect(() => {
const savedTheme = localStorage.getItem('theme') as Theme;
if (savedTheme) {
setTheme(savedTheme);
document.documentElement.classList.toggle('dark', savedTheme === 'dark');
}
}, []);

const toggleTheme = () => {
const newTheme = theme === 'dark' ? 'light' : 'dark';
setTheme(newTheme);
localStorage.setItem('theme', newTheme);
document.documentElement.classList.toggle('dark');
};

return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}

// exported function

export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
37 changes: 37 additions & 0 deletions frontend/src/components/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client';

import { useState, useEffect } from 'react';
import { SunIcon, MoonIcon } from '@heroicons/react/24/outline';

export default function ThemeToggle() {
const [isDark, setIsDark] = useState(true);

useEffect(() => {
const theme = localStorage.getItem('theme');
setIsDark(theme === 'dark');
if (theme === 'dark') {
document.documentElement.classList.add('dark');
}
}, []);

const toggleTheme = () => {
const newTheme = isDark ? 'light' : 'dark';
setIsDark(!isDark);
localStorage.setItem('theme', newTheme);
document.documentElement.classList.toggle('dark');
};

return (
<button
onClick={toggleTheme}
className="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
aria-label="Toggle theme"
>
{isDark ? (
<SunIcon className="h-5 w-5 text-yellow-500" />
) : (
<MoonIcon className="h-5 w-5 text-gray-500" />
)}
</button>
);
}
Loading