diff --git a/index.html b/index.html index e4b78ea..3f3c44e 100644 --- a/index.html +++ b/index.html @@ -2,9 +2,9 @@ - + - Vite + React + TS + VoteChain
diff --git a/package-lock.json b/package-lock.json index bc1b2c6..20b245c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "votechain", "version": "0.0.0", "dependencies": { + "@hookform/resolvers": "^4.1.3", + "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-slot": "^1.1.2", "@tailwindcss/vite": "^4.0.9", "class-variance-authority": "^0.7.1", @@ -16,10 +18,13 @@ "lucide-react": "^0.477.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hook-form": "^7.54.2", + "react-router": "^7.2.0", "sonner": "^2.0.1", "tailwind-merge": "^3.0.2", "tailwindcss": "^4.0.9", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.24.2" }, "devDependencies": { "@eslint/js": "^9.21.0", @@ -320,6 +325,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "aix" @@ -335,6 +341,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -350,6 +357,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -365,6 +373,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "android" @@ -380,6 +389,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -395,6 +405,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -410,6 +421,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -425,6 +437,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -440,6 +453,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -455,6 +469,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -470,6 +485,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "linux" @@ -485,6 +501,7 @@ "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -500,6 +517,7 @@ "cpu": [ "mips64el" ], + "dev": true, "optional": true, "os": [ "linux" @@ -515,6 +533,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -530,6 +549,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -545,6 +565,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -560,6 +581,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -575,6 +597,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "netbsd" @@ -590,6 +613,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "netbsd" @@ -605,6 +629,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "openbsd" @@ -620,6 +645,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "openbsd" @@ -635,6 +661,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "sunos" @@ -650,6 +677,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -665,6 +693,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -680,6 +709,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -1520,6 +1550,18 @@ "@ethersproject/strings": "^5.7.0" } }, + "node_modules/@hookform/resolvers": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-4.1.3.tgz", + "integrity": "sha512-Jsv6UOWYTrEFJ/01ZrnwVXs7KDvP8XIo115i++5PWvNkNvkrsTfGiLS6w+eJ57CYtUtDQalUWovCZDHFJ8u1VQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1678,10 +1720,57 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz", + "integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, @@ -1702,6 +1791,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -1714,6 +1804,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -1726,6 +1817,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -1738,6 +1830,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -1750,6 +1843,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -1762,6 +1856,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -1774,6 +1869,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1786,6 +1882,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1798,6 +1895,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1810,6 +1908,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1822,6 +1921,7 @@ "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1834,6 +1934,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1846,6 +1947,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1858,6 +1960,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1870,6 +1973,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1882,6 +1986,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1894,6 +1999,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -1906,6 +2012,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -1918,11 +2025,18 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" ] }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@tailwindcss/node": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.9.tgz", @@ -2174,10 +2288,17 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -2189,7 +2310,7 @@ "version": "22.13.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.8.tgz", "integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==", - "devOptional": true, + "dev": true, "dependencies": { "undici-types": "~6.20.0" } @@ -2198,7 +2319,7 @@ "version": "19.0.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", - "devOptional": true, + "dev": true, "dependencies": { "csstype": "^3.0.2" } @@ -2675,6 +2796,15 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2693,7 +2823,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "dev": true }, "node_modules/debug": { "version": "4.4.0", @@ -2772,6 +2902,7 @@ "version": "0.25.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -3161,6 +3292,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -3727,6 +3859,7 @@ "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, "funding": [ { "type": "github", @@ -3832,7 +3965,8 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", @@ -3850,6 +3984,7 @@ "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -3945,6 +4080,22 @@ "react": "^19.0.0" } }, + "node_modules/react-hook-form": { + "version": "7.54.2", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz", + "integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -3954,6 +4105,30 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.2.0.tgz", + "integrity": "sha512-fXyqzPgCPZbqhrk7k3hPcCpYIlQ2ugIXDboHUzhJISFVy2DEPsmHgN588MyGmkIOv3jDgNfUE3kJi83L28s/LQ==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3977,6 +4152,7 @@ "version": "4.34.9", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.9.tgz", "integrity": "sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==", + "dev": true, "dependencies": { "@types/estree": "1.0.6" }, @@ -4053,6 +4229,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4088,6 +4270,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -4170,6 +4353,12 @@ "typescript": ">=4.8.4" } }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4221,7 +4410,7 @@ "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "devOptional": true + "dev": true }, "node_modules/update-browserslist-db": { "version": "1.1.3", @@ -4266,6 +4455,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz", "integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==", + "dev": true, "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", @@ -4394,6 +4584,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.24.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", + "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index eba08e0..342b0e0 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "format": "prettier --write **/*.{js,ts,tsx}" }, "dependencies": { + "@hookform/resolvers": "^4.1.3", + "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-slot": "^1.1.2", "@tailwindcss/vite": "^4.0.9", "class-variance-authority": "^0.7.1", @@ -20,10 +22,13 @@ "lucide-react": "^0.477.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hook-form": "^7.54.2", + "react-router": "^7.2.0", "sonner": "^2.0.1", "tailwind-merge": "^3.0.2", "tailwindcss": "^4.0.9", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.24.2" }, "devDependencies": { "@eslint/js": "^9.21.0", diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..b43a409 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/images/online-voting.svg b/public/images/online-voting.svg new file mode 100644 index 0000000..92f00ba --- /dev/null +++ b/public/images/online-voting.svg @@ -0,0 +1,784 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VOTE + + + + + + + VOTE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App.tsx b/src/App.tsx index 090503b..b74b61c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,11 @@ -import { Button } from '@/components/ui/button'; +import { Route, Routes } from 'react-router'; +import LoginPage from './pages/LoginPage'; function App() { return ( -
- -
+ + } /> + ); } diff --git a/src/components/shared/Form-error.tsx b/src/components/shared/Form-error.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/shared/auth/Login-form.tsx b/src/components/shared/auth/Login-form.tsx new file mode 100644 index 0000000..414fc39 --- /dev/null +++ b/src/components/shared/auth/Login-form.tsx @@ -0,0 +1,119 @@ +import { Vote } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import connectToBlockchain from '@/config'; +import { useContext, useState } from 'react'; +import AuthContext from '@/context/AuthContext'; +import { toast } from 'sonner'; + +export function LoginForm({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) { + const auth = useContext(AuthContext); + const [name, setName] = useState(''); + const [loading, setLoading] = useState(false); + + if (!auth) { + return

Loading...

; + } + + const { dispatch } = auth; + + const connectWallet = async (e: React.FormEvent) => { + e.preventDefault(); // Prevent form submission + setLoading(true); + + try { + const blockchainConnection = await connectToBlockchain(); + console.log('blockchainConnection', blockchainConnection); + if (!blockchainConnection) { + setLoading(false); + return; + } + + const { contractInstance, signer } = blockchainConnection; + console.log('contractInstacne', contractInstance); + console.log('signer', signer); + const account = await signer.getAddress(); + console.log('account', signer); + console.log('account1', account); + + console.log('contractInstance', contractInstance); + + toast.success(`Connected to ${account}`); + + let isAdmin = false; + let isVoter = true; + + try { + isAdmin = await contractInstance.isAdmin(account); + console.log('isAdmin', isAdmin); + } catch (err) { + console.error('Error checking admin status:', err); + } + + if (!isAdmin) { + try { + const voter = await contractInstance.getVoterdetails(account); + if (voter?.votername) { + isVoter = false; + } + } catch (error) { + console.error('Error fetching voter details:', error); + } + } + + dispatch({ + type: 'login', + payload: { account, instance: contractInstance, is_admin: isAdmin, flag: isVoter }, + }); + + toast.success('Wallet connected successfully!'); + } catch (error) { + console.error('MetaMask connection error:', error); + toast.error('Failed to connect wallet.'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+
+ +
+ +
+ VoteChain +
+

Welcome to VoteChain.

+
+ Don't have an account?{' '} + + Sign up + +
+
+
+
+ + setName(e.target.value)} + required + /> +
+ +
+
+
+
+ ); +} diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx new file mode 100644 index 0000000..3ddf5bb --- /dev/null +++ b/src/components/ui/form.tsx @@ -0,0 +1,165 @@ +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { Slot } from "@radix-ui/react-slot" +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, + useFormState, +} from "react-hook-form" + +import { cn } from "@/lib/utils" +import { Label } from "@/components/ui/label" + +const Form = FormProvider + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName +} + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue +) + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ) +} + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext) + const itemContext = React.useContext(FormItemContext) + const { getFieldState } = useFormContext() + const formState = useFormState({ name: fieldContext.name }) + const fieldState = getFieldState(fieldContext.name, formState) + + if (!fieldContext) { + throw new Error("useFormField should be used within ") + } + + const { id } = itemContext + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + } +} + +type FormItemContextValue = { + id: string +} + +const FormItemContext = React.createContext( + {} as FormItemContextValue +) + +function FormItem({ className, ...props }: React.ComponentProps<"div">) { + const id = React.useId() + + return ( + +
+ + ) +} + +function FormLabel({ + className, + ...props +}: React.ComponentProps) { + const { error, formItemId } = useFormField() + + return ( +