diff --git a/.env b/.env new file mode 100644 index 00000000..79631b52 --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ + PRIVATE_KEY=011d4ba8e7b549ee9cd718df4c25abed + RPC_URL_SEPOLIA=https://eth-mainnet.g.alchemy.com/v2/MBGDHRRT3_qjtqKzM2DZk + RPC_URL_FUJI=https://avax-fuji.g.alchemy.com/v2/MBGDHRRT3_qjtqKzM2DZk + ETHERSCAN_KEY=WCU9BD1WA1835GAU9VBTGSPA2DGEPQHYSF \ No newline at end of file diff --git a/blockchain/.env.example b/blockchain/.env.example deleted file mode 100644 index d26c2bc2..00000000 --- a/blockchain/.env.example +++ /dev/null @@ -1,6 +0,0 @@ -PRIVATE_KEY = ENTER_YOUR_KEY_VALUE -RPC_URL_SEPOLIA = ENTER_YOUR_KEY_VALUE -RPC_URL_FUJI = ENTER_YOUR_KEY_VALUE -RPC_URL_AMOY = ENTER_YOUR_KEY_VALUE -RPC_URL_BSC = ENTER_YOUR_KEY_VALUE -ETHERSCAN_KEY = ENTER_YOUR_KEY_VALUE diff --git a/client/.env.example b/client/.env.example deleted file mode 100644 index 16d49007..00000000 --- a/client/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -NEXT_PUBLIC_SEPOLIA_RPC_URL = ENTER_RPC_URL -NEXT_PUBLIC_AMOY_RPC_URL = ENTER_RPC_URL -NEXT_PUBLIC_FUJI_RPC_URL = ENTER_RPC_URL -NEXT_PUBLIC_PINATA_JWT = ENTER_PINATA_API_KEY \ No newline at end of file diff --git a/client/.env.lc b/client/.env.lc new file mode 100644 index 00000000..045ace23 --- /dev/null +++ b/client/.env.lc @@ -0,0 +1,4 @@ +NEXT_PUBLIC_SEPOLIA_RPC_URL = https://eth-mainnet.g.alchemy.com/v2/MBGDHRRT3_qjtqKzM2DZk +NEXT_PUBLIC_AMOY_RPC_URL = https://polygon-amoy.g.alchemy.com/v2/MBGDHRRT3_qjtqKzM2DZk +NEXT_PUBLIC_FUJI_RPC_URL = https://avax-fuji.g.alchemy.com/v2/MBGDHRRT3_qjtqKzM2DZk +NEXT_PUBLIC_PINATA_JWT = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySW5mb3JtYXRpb24iOnsiaWQiOiI3Zjc0NGIzYi03NGZlLTQyOGMtYTY2NS1kMGFjODkwNTE0N2YiLCJlbWFpbCI6ImhlbWFudC5rQGFkeXB1LmVkdS5pbiIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwaW5fcG9saWN5Ijp7InJlZ2lvbnMiOlt7ImRlc2lyZWRSZXBsaWNhdGlvbkNvdW50IjoxLCJpZCI6IkZSQTEifSx7ImRlc2lyZWRSZXBsaWNhdGlvbkNvdW50IjoxLCJpZCI6Ik5ZQzEifV0sInZlcnNpb24iOjF9LCJtZmFfZW5hYmxlZCI6ZmFsc2UsInN0YXR1cyI6IkFDVElWRSJ9LCJhdXRoZW50aWNhdGlvblR5cGUiOiJzY29wZWRLZXkiLCJzY29wZWRLZXlLZXkiOiIzNDRlMDU0ZTRiMDhkZWZjOTlmNiIsInNjb3BlZEtleVNlY3JldCI6IjE2MTcyZWM2YTMyYjVkMTlkZjUzNWE4ZjM2OTZlNzg5NGExMjY4MjViMTBmNTA2OWYzZDM4MWY4ZDBhMWZjNzQiLCJleHAiOjE3OTEzMDUwOTB9.c8lfSqJIUNh4F4DOvys5qI07pMwUc1H91dUBwt8emfI \ No newline at end of file diff --git a/client/app/components/Header/Header.tsx b/client/app/components/Header/Header.tsx index 2fd84166..2cc66e38 100644 --- a/client/app/components/Header/Header.tsx +++ b/client/app/components/Header/Header.tsx @@ -13,6 +13,7 @@ import { } from "@heroicons/react/24/outline"; import Web3Connect from "../Helper/Web3Connect"; import Image from "next/image"; +import ThemeToggle from "../ThemeToggle/ThemeToggle"; const menuItems = [ { name: "Home", href: "/", icon: HomeIcon }, @@ -29,7 +30,7 @@ const Header = () => { return ( <> { src="/aossie.png" alt="Agora Blockchain" /> -

+

Agora Blockchain

@@ -56,9 +57,9 @@ const Header = () => { @@ -67,7 +68,7 @@ const Header = () => { {pathname === item.href && ( { )} ))} +
@@ -83,12 +85,13 @@ const Header = () => { {/* Mobile/Tablet Menu Button */}
+
@@ -109,7 +112,7 @@ const Header = () => { onClick={toggleSidebar} /> {
@@ -130,8 +133,8 @@ const Header = () => { onClick={toggleSidebar} className={`flex items-center p-4 rounded-lg transition-colors ${ pathname === item.href - ? "bg-indigo-50 text-indigo-600" - : "text-gray-700 hover:bg-gray-50" + ? "bg-indigo-50 dark:bg-indigo-900/30 text-indigo-600 dark:text-indigo-400" + : "text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800" }`} > diff --git a/client/app/components/ThemeProvider/ThemeProvider.tsx b/client/app/components/ThemeProvider/ThemeProvider.tsx new file mode 100644 index 00000000..5ed8b7b9 --- /dev/null +++ b/client/app/components/ThemeProvider/ThemeProvider.tsx @@ -0,0 +1,26 @@ +"use client"; + +import React, { useEffect } from "react"; +import { useThemeStore } from "@/app/store/themeStore"; + +export default function ThemeProvider({ + children, +}: { + children: React.ReactNode; +}) { + const theme = useThemeStore((state) => state.theme); + + useEffect(() => { + // Apply theme class to html element + const root = document.documentElement; + if (theme === "dark") { + root.classList.add("dark"); + root.setAttribute("data-theme", "dark"); + } else { + root.classList.remove("dark"); + root.setAttribute("data-theme", "light"); + } + }, [theme]); + + return <>{children}; +} diff --git a/client/app/components/ThemeToggle/ThemeToggle.tsx b/client/app/components/ThemeToggle/ThemeToggle.tsx new file mode 100644 index 00000000..f28a397a --- /dev/null +++ b/client/app/components/ThemeToggle/ThemeToggle.tsx @@ -0,0 +1,61 @@ +"use client"; + +import React, { useEffect } from "react"; +import { motion } from "framer-motion"; +import { SunIcon, MoonIcon } from "@heroicons/react/24/outline"; +import { useThemeStore } from "@/app/store/themeStore"; + +const ThemeToggle = () => { + const { theme, toggleTheme } = useThemeStore(); + + useEffect(() => { + // Apply theme to document + if (theme === "dark") { + document.documentElement.classList.add("dark"); + document.documentElement.setAttribute("data-theme", "dark"); + } else { + document.documentElement.classList.remove("dark"); + document.documentElement.setAttribute("data-theme", "light"); + } + }, [theme]); + + return ( + + + + + + + + {/* Invisible spacer to maintain button size */} +
+ +
+
+ ); +}; + +export default ThemeToggle; diff --git a/client/app/create/page.tsx b/client/app/create/page.tsx index d8f62dad..72998ec9 100644 --- a/client/app/create/page.tsx +++ b/client/app/create/page.tsx @@ -107,15 +107,15 @@ const CreatePage: React.FC = () => { initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5 }} - className="h-screen w-full bg-gradient-to-br pt-[50px] from-gray-100 to-gray-200 flex flex-col items-center justify-start p-4 overflow-y-auto" + className="h-screen w-full bg-gradient-to-br pt-[50px] from-gray-100 to-gray-200 dark:from-gray-800 dark:to-gray-900 flex flex-col items-center justify-start p-4 overflow-y-auto" > -

+

Create New Election

@@ -132,7 +132,7 @@ const CreatePage: React.FC = () => { {/* candidate section shows placeholder if empty candidate and allows to add cnadidates*/ }
-

Candidates

+

Candidates

{
{candidates.length === 0 ? ( -

- No candidates added yet. Click "Add Candidate" to begin adding candidates. +

+ No candidates added yet. Click "Add Candidate" to begin adding candidates.

) : ( candidates.map((candidate, index) => ( @@ -155,10 +155,10 @@ const CreatePage: React.FC = () => { key={index} initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} - className="p-4 border border-gray-200 rounded-lg space-y-3" + className="p-4 border border-gray-200 dark:border-gray-700 rounded-lg space-y-3" >
-

+

Candidate {index + 1}

{ value={candidate.name} onChange={(e) => updateCandidate(index, "name", e.target.value)} placeholder="Candidate Name" - className="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" + className="block w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" required /> @@ -307,7 +307,7 @@ const DatePickerField: React.FC = ({ label, }) => (
- + = ({ onSwitch }) => ( initial={{ scale: 0.9, opacity: 0 }} animate={{ scale: 1, opacity: 1 }} transition={{ type: "spring", stiffness: 300, damping: 30 }} - className="bg-white rounded-lg p-8 shadow-xl text-center" + className="bg-white dark:bg-gray-800 rounded-lg p-8 shadow-xl text-center" > -

+

Creating Elections is supported only on Sepolia

- - - - -
- - {children} - - - - + + + + + +
+ + {children} + + + + + ); diff --git a/client/app/page.tsx b/client/app/page.tsx index ca61711b..26b99249 100644 --- a/client/app/page.tsx +++ b/client/app/page.tsx @@ -3,7 +3,7 @@ import HomePage from "./components/Pages/HomePage"; export default function Home() { return ( -
+
); diff --git a/client/app/profile/page.tsx b/client/app/profile/page.tsx index 6ebd82e8..97cad731 100644 --- a/client/app/profile/page.tsx +++ b/client/app/profile/page.tsx @@ -8,11 +8,11 @@ import Loader from "../components/Helper/Loader"; const ElectionMiniSkeleton: React.FC = () => { return ( -
-
-
-
-
+
+
+
+
+
); }; @@ -47,9 +47,9 @@ const ProfilePage: React.FC = () => { } return ( -
-
-

Profile

+
+
+

Profile

{ animate="visible" > {elections.length === 0 ? ( -
+
No elections found
) : ( diff --git a/client/app/store/themeStore.ts b/client/app/store/themeStore.ts new file mode 100644 index 00000000..8ead7360 --- /dev/null +++ b/client/app/store/themeStore.ts @@ -0,0 +1,24 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +interface ThemeStore { + theme: "light" | "dark"; + toggleTheme: () => void; + setTheme: (theme: "light" | "dark") => void; +} + +export const useThemeStore = create()( + persist( + (set) => ({ + theme: "light", + toggleTheme: () => + set((state) => ({ + theme: state.theme === "light" ? "dark" : "light", + })), + setTheme: (theme) => set({ theme }), + }), + { + name: "theme-storage", + } + ) +); diff --git a/client/package-lock.json b/client/package-lock.json index 6143ee65..f8051622 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -2740,21 +2740,6 @@ "ws": "7.4.6" } }, - "node_modules/@ethersproject/providers/node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/@ethersproject/providers/node_modules/ws": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", @@ -6229,21 +6214,6 @@ "ws": "^7.5.1" } }, - "node_modules/@walletconnect/jsonrpc-ws-connection/node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/@walletconnect/jsonrpc-ws-connection/node_modules/ws": { "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", @@ -11876,21 +11846,6 @@ "node": ">=8" } }, - "node_modules/metro/node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/metro/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -13494,21 +13449,6 @@ "ws": "^7" } }, - "node_modules/react-devtools-core/node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/react-devtools-core/node_modules/ws": { "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", @@ -16583,6 +16523,126 @@ "optional": true } } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.4.tgz", + "integrity": "sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.4.tgz", + "integrity": "sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.4.tgz", + "integrity": "sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.4.tgz", + "integrity": "sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.4.tgz", + "integrity": "sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.4.tgz", + "integrity": "sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.4.tgz", + "integrity": "sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.4.tgz", + "integrity": "sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } } } } diff --git a/client/tailwind.config.ts b/client/tailwind.config.ts index f71309a3..08763076 100644 --- a/client/tailwind.config.ts +++ b/client/tailwind.config.ts @@ -6,6 +6,7 @@ const config: Config = { "./components/**/*.{js,ts,jsx,tsx,mdx}", "./app/**/*.{js,ts,jsx,tsx,mdx}", ], + darkMode: "class", theme: { extend: { backgroundImage: { @@ -17,7 +18,7 @@ const config: Config = { }, plugins: [require("daisyui")], daisyui: { - themes: ["light"], + themes: ["light", "dark"], }, }; export default config;