commit dc69f693f09cf7bb7faa9aa5935887ab6c307bf4 Author: Azmat-7860 Date: Wed Oct 7 09:07:57 2026 +0530 adding base project diff --git a/.env b/.env new file mode 100644 index 0000000..acdb86d --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +# AuthSec Backend Configuration +# Points to Spring Boot backend service +VITE_API_URL=http://localhost:9292 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e635817 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +# AuthSec Backend Configuration +VITE_API_URL=http://localhost:9292 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..1255078 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["react", "oxc"], + "rules": { + "react/rules-of-hooks": "error", + "react/only-export-components": ["warn", { "allowConstantExport": true }] + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..d937833 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some Oxlint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the Oxlint configuration + +If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and Oxlint's TypeScript related rules in your project. diff --git a/index.html b/index.html new file mode 100644 index 0000000..13b3ac4 --- /dev/null +++ b/index.html @@ -0,0 +1,18 @@ + + + + + + + AuthSec - Identity & Access Security Platform + + + + + + +
+ + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2376cef --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1336 @@ +{ + "name": "authsec_react", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "authsec_react", + "version": "0.0.0", + "dependencies": { + "lucide-react": "^1.52.0", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "react-router-dom": "^7.18.4" + }, + "devDependencies": { + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.7", + "@vitejs/plugin-react": "^6.1.1", + "oxlint": "^1.81.0", + "vite": "^8.3.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.152.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.152.0.tgz", + "integrity": "sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.87.0.tgz", + "integrity": "sha512-22tVIHlB9898OYsBKmYB6He1MCSqdTmK1BVWuSNUTVrpS0KWNege+U9uEYqGJ+KatQag0r/oU44i3OOs46z7HQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.87.0.tgz", + "integrity": "sha512-JfZQ3MWf74n8ov0DojZpkOduIubYJvZL5tbTMMhDutsal6u1xH1QuNpN+lK+EQwF8AeAfgwTR2fgh5U2br27XA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.87.0.tgz", + "integrity": "sha512-rkx6Xvvw93z2GImVOyvbZwAZcoY0Z6MJW177XNsSB+i/CBsH7ANyelTp01VZyIkM3iSXtqKVb4+ptBgPHap5pQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.87.0.tgz", + "integrity": "sha512-cw5nYw7L/pQ2SgGBgOcg/Qo89llQre3vIc/kuBLhSICzu1r5s8L76H3ug6AIFsD98WpROj8/z5JVzkf4daxHaQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.87.0.tgz", + "integrity": "sha512-x/SuznE6loRZK9gz9AS4ZmzNzSu+5KdeoCS+sVHhnrE8HM6AD8UyTjwldQTTqlXGvkwHASgB0NiTNOc3o3PdAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.87.0.tgz", + "integrity": "sha512-+JEDmCAXiiFmKhAjZhgAOwaHWY6WENOQ5QA+lFuY59tgsUCDOTRi46B+lENK6CWvtxsXpwkZXNOgpjAQcpIpnA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.87.0.tgz", + "integrity": "sha512-/uC9XqXLAC9AHFLNycwmSQEvKqGrOCgU+uXALI7gNW0DG81TJ/3T2eHnKdRvcb5R1gFEfjxDwI4e4zIa29rICQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.87.0.tgz", + "integrity": "sha512-FTRYPsBy/M728vnUv7l+KAwSAa3Qi4BQbhVvy7NYuMTZ0sUa/OWsbMx79HVFw6u/5KWcaYayDsoOiv7xbevzDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.87.0.tgz", + "integrity": "sha512-6sC+N7IY0h1egd8SrYUGNN7CUyKJZ4bIZakKa7V7hfinf4jfi8Kq4+klJvFp5DqXcwMn44I5MeBwmJluO1odFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.87.0.tgz", + "integrity": "sha512-d5Mnn+ICbhoJPi37Ccjy+86T5qfH2+mSTrTMBXNPfB+wqSWOayBBLLIrCXdUy1YssluCVdGkl+DO9w5r5ODmUA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.87.0.tgz", + "integrity": "sha512-sf5YWuUo1Dx9loqWCgF8P278MycfcD4JHjDC/LV5j11L3Lqa4PFiwCjD6B8kP4AjsgZZkLn3xbpJEtZBaccs4Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.87.0.tgz", + "integrity": "sha512-+aCraYbmK3uU3P0rFojjeaK9pGY6JvBDXh/0CM+gPyWKmNmXmnFe0qj4qj02U4ibFpbd9Pa9CmDvIdjJUd29rA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.87.0.tgz", + "integrity": "sha512-7rzbZQIljo6VLWVPqUyLa2P/WV3VFsJB4M52R4NCUJn1A50yjtUsHxiMHFL9opEKGFIiTbZ3VQzmtcJOPUq4NQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.87.0.tgz", + "integrity": "sha512-Dry46Fa+AXE/xt9J/YriujU1pU9mzfNWHGQwtOYibAFH8HLrKWCi93AhIGnuUMiwn2JbuBkRyz51f50Ly4r4ow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.87.0.tgz", + "integrity": "sha512-f/HMZez74gJvnRpGz+b5sdGyWrEgRLmFTXg9sNoqjPWogGxYHUhXKTKyMiYsu9rUGR2ciYqL3fk+Ua4JhMJbRA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.87.0.tgz", + "integrity": "sha512-lEC6ZfPZm/07JlB7FsOWX9o1RiHlXiBW594LSUGuXmekC8LZmvUPP9XzE2WZcNxJi5cHA/Fw56o02c3JxgMA5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.87.0.tgz", + "integrity": "sha512-afBWKOD36dUiA9AenaiWpCg8xfyUVPYznyAAVXu413brK4RA5UI/Xoc4yfeGIT8G1a44MV+JR7hioryerBDzGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.87.0.tgz", + "integrity": "sha512-daptNsEOuru4/nJTxV4DBHNJ5GepzmaBVQf+jjgKQGi9NRlZKik+RFq4hUL7ewsF1pwjKM+SkYaMbF82UwFGnw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.87.0.tgz", + "integrity": "sha512-k+m/EShyXE29J/r1fjk0HlAzhN5JhIvj044oSJkXTEkCLrWZQgXb/3r4q5LTw/eaKW/4GuMXG2xmiJs3s5rPNw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.12.tgz", + "integrity": "sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.12.tgz", + "integrity": "sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.12.tgz", + "integrity": "sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.12.tgz", + "integrity": "sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.12.tgz", + "integrity": "sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.12.tgz", + "integrity": "sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.12.tgz", + "integrity": "sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.12.tgz", + "integrity": "sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.12.tgz", + "integrity": "sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.12.tgz", + "integrity": "sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.12.tgz", + "integrity": "sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.12.tgz", + "integrity": "sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.12.tgz", + "integrity": "sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.12.tgz", + "integrity": "sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.12.tgz", + "integrity": "sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.2.tgz", + "integrity": "sha512-fhJNGUdscQa4HkI0xFxwonWyhnVReCPDbXI7FH7jmzpnVCrFQI7PRrhWecO263610vOrs9tr2Y4xcf8uAdFxbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.152.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "oxc-transform-react": { + "optional": true + } + } + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "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, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lucide-react": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.52.0.tgz", + "integrity": "sha512-TUgPtl5ZI9WgxOcbu4ckaC5B3l1qxPQrCMgb6OfUB9owx/OvSJfcf93SMgUWAxRw5/1XDfh9uI5F5iNGXCcGDQ==", + "license": "ISC", + "peerDependencies": { + "@types/react": "*", + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oxlint": { + "version": "1.87.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.87.0.tgz", + "integrity": "sha512-drxVlaW7Oqq7wSy1CN25znRu00aRq8n57Q2wniJRxCmtYVz07ufCm7EVDPQFZURrTgQIjzKrVEHAD/HcvJXObg==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.87.0", + "@oxlint/binding-android-arm64": "1.87.0", + "@oxlint/binding-darwin-arm64": "1.87.0", + "@oxlint/binding-darwin-x64": "1.87.0", + "@oxlint/binding-freebsd-x64": "1.87.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.87.0", + "@oxlint/binding-linux-arm-musleabihf": "1.87.0", + "@oxlint/binding-linux-arm64-gnu": "1.87.0", + "@oxlint/binding-linux-arm64-musl": "1.87.0", + "@oxlint/binding-linux-ppc64-gnu": "1.87.0", + "@oxlint/binding-linux-riscv64-gnu": "1.87.0", + "@oxlint/binding-linux-riscv64-musl": "1.87.0", + "@oxlint/binding-linux-s390x-gnu": "1.87.0", + "@oxlint/binding-linux-x64-gnu": "1.87.0", + "@oxlint/binding-linux-x64-musl": "1.87.0", + "@oxlint/binding-openharmony-arm64": "1.87.0", + "@oxlint/binding-win32-arm64-msvc": "1.87.0", + "@oxlint/binding-win32-ia32-msvc": "1.87.0", + "@oxlint/binding-win32-x64-msvc": "1.87.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=7.0.2003", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.19", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.0" + } + }, + "node_modules/react-router": { + "version": "7.18.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.4.tgz", + "integrity": "sha512-PUPQcMhMGRAslLcvtlPz/kmzBEWPhLdgLFrL7pLNepBL6dX0lWj4WD2cUYVgYCuT3jxvghYFg81cDTj44DhetQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.18.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.4.tgz", + "integrity": "sha512-yrfmJHIpDG7taCpqKjT1G5B6q3O2K+RN8/fgNf0lTjCwiPbQ0ei6vXX9ZjQR+7ld8Tr7Z5xmyMnZ8YJrphWQUw==", + "license": "MIT", + "dependencies": { + "react-router": "7.18.4" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/rolldown": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.12.tgz", + "integrity": "sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.152.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.12", + "@rolldown/binding-android-arm64": "1.2.12", + "@rolldown/binding-darwin-arm64": "1.2.12", + "@rolldown/binding-darwin-x64": "1.2.12", + "@rolldown/binding-freebsd-x64": "1.2.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.12", + "@rolldown/binding-linux-arm64-gnu": "1.2.12", + "@rolldown/binding-linux-arm64-musl": "1.2.12", + "@rolldown/binding-linux-ppc64-gnu": "1.2.12", + "@rolldown/binding-linux-s390x-gnu": "1.2.12", + "@rolldown/binding-linux-x64-gnu": "1.2.12", + "@rolldown/binding-linux-x64-musl": "1.2.12", + "@rolldown/binding-openharmony-arm64": "1.2.12", + "@rolldown/binding-win32-arm64-msvc": "1.2.12", + "@rolldown/binding-win32-x64-msvc": "1.2.12" + } + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.3.tgz", + "integrity": "sha512-cTAldKPImjg6c+gk48U19POPn3GCBzZwpdsN8ZMEEcbpes+6/wvfqUd0C2y3qYY4wsj8PwgFwrZ/1jBPVttMSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.11", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..79ebab9 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "authsec_react", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "oxlint", + "preview": "vite preview" + }, + "dependencies": { + "lucide-react": "^1.52.0", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "react-router-dom": "^7.18.4" + }, + "devDependencies": { + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.7", + "@vitejs/plugin-react": "^6.1.1", + "oxlint": "^1.81.0", + "vite": "^8.3.0" + } +} diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons.svg b/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..436fe51 --- /dev/null +++ b/src/App.css @@ -0,0 +1 @@ +/* App styles managed by global and module css */ diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..c4ed9a6 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; +import { ThemeProvider } from './context/ThemeContext'; +import { AuthProvider, useAuth } from './context/AuthContext'; +import { Login } from './pages/Login'; +import { Register } from './pages/Register'; +import { Dashboard } from './pages/Dashboard'; +import { NotFound } from './pages/NotFound'; +import { ProtectedRoute } from './components/auth/ProtectedRoute'; +import './styles/global.css'; + +// Root redirector based on authentication state +function HomeRedirect() { + const { isAuthenticated, loading } = useAuth(); + if (loading) return null; + return isAuthenticated ? : ; +} + +// Redirect away from login/register if already logged in +function PublicOnlyRoute({ children }) { + const { isAuthenticated, loading } = useAuth(); + if (loading) return null; + return isAuthenticated ? : children; +} + +export function App() { + return ( + + + + + } /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + } /> + + + + + ); +} + +export default App; diff --git a/src/assets/hero.png b/src/assets/hero.png new file mode 100644 index 0000000..02251f4 Binary files /dev/null and b/src/assets/hero.png differ diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/vite.svg b/src/assets/vite.svg new file mode 100644 index 0000000..5101b67 --- /dev/null +++ b/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/src/components/auth/ProtectedRoute.jsx b/src/components/auth/ProtectedRoute.jsx new file mode 100644 index 0000000..bf9b225 --- /dev/null +++ b/src/components/auth/ProtectedRoute.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { Navigate, useLocation } from 'react-router-dom'; +import { useAuth } from '../../context/AuthContext'; + +export function ProtectedRoute({ children }) { + const { isAuthenticated, loading } = useAuth(); + const location = useLocation(); + + if (loading) { + return ( +
+
+ + Validating Security Session... + +
+ ); + } + + if (!isAuthenticated) { + return ; + } + + return children; +} diff --git a/src/components/common/Badge.jsx b/src/components/common/Badge.jsx new file mode 100644 index 0000000..d1f8eea --- /dev/null +++ b/src/components/common/Badge.jsx @@ -0,0 +1,18 @@ +import React from 'react'; +import './common.css'; + +export function Badge({ children, variant = 'primary', size = 'sm', className = '' }) { + return ( + + {children} + + ); +} + +export function Card({ children, className = '', hoverable = false, ...props }) { + return ( +
+ {children} +
+ ); +} diff --git a/src/components/common/Button.jsx b/src/components/common/Button.jsx new file mode 100644 index 0000000..f0c8873 --- /dev/null +++ b/src/components/common/Button.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import './common.css'; + +export function Button({ + children, + variant = 'primary', + size = 'md', + isLoading = false, + icon: Icon, + fullWidth = false, + className = '', + disabled, + ...props +}) { + return ( + + ); +} diff --git a/src/components/common/Card.jsx b/src/components/common/Card.jsx new file mode 100644 index 0000000..7910451 --- /dev/null +++ b/src/components/common/Card.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import './common.css'; + +export function Card({ children, className = '', hoverable = false, ...props }) { + return ( +
+ {children} +
+ ); +} diff --git a/src/components/common/Input.jsx b/src/components/common/Input.jsx new file mode 100644 index 0000000..663a2d0 --- /dev/null +++ b/src/components/common/Input.jsx @@ -0,0 +1,65 @@ +import React, { useState } from 'react'; +import { Eye, EyeOff } from 'lucide-react'; +import './common.css'; + +export function Input({ + label, + type = 'text', + icon: Icon, + error, + helperText, + id, + className = '', + required = false, + ...props +}) { + const [showPassword, setShowPassword] = useState(false); + const isPasswordType = type === 'password'; + const effectiveType = isPasswordType ? (showPassword ? 'text' : 'password') : type; + + const inputId = id || (label ? label.toLowerCase().replace(/\s+/g, '-') : undefined); + + return ( +
+ {label && ( + + )} + +
+ {Icon && ( + + + + )} + + + + {isPasswordType && ( + + )} +
+ + {error ? ( +

{error}

+ ) : helperText ? ( +

{helperText}

+ ) : null} +
+ ); +} diff --git a/src/components/common/Toast.jsx b/src/components/common/Toast.jsx new file mode 100644 index 0000000..7232285 --- /dev/null +++ b/src/components/common/Toast.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { AlertCircle, CheckCircle2, Info, AlertTriangle, X } from 'lucide-react'; +import './common.css'; + +export function Toast({ type = 'info', message, onClose, className = '' }) { + if (!message) return null; + + const icons = { + error: AlertCircle, + success: CheckCircle2, + warning: AlertTriangle, + info: Info, + }; + + const IconComponent = icons[type] || Info; + + return ( +
+
+ +
+
{message}
+ {onClose && ( + + )} +
+ ); +} diff --git a/src/components/common/common.css b/src/components/common/common.css new file mode 100644 index 0000000..7757029 --- /dev/null +++ b/src/components/common/common.css @@ -0,0 +1,344 @@ +/* Common Components CSS */ + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + font-weight: 600; + border-radius: var(--radius-md); + transition: all var(--transition-normal); + user-select: none; + white-space: nowrap; + gap: 8px; + position: relative; + overflow: hidden; +} + +.btn:active:not(:disabled) { + transform: scale(0.98); +} + +.btn-block { + width: 100%; +} + +.btn-sm { + padding: 8px 14px; + font-size: 0.825rem; +} + +.btn-md { + padding: 11px 20px; + font-size: 0.925rem; +} + +.btn-lg { + padding: 14px 28px; + font-size: 1.05rem; +} + +.btn-primary { + background: var(--gradient-primary); + color: #ffffff; + box-shadow: 0 4px 14px rgba(99, 102, 241, 0.35); + border: 1px solid rgba(255, 255, 255, 0.15); +} + +.btn-primary:hover:not(:disabled) { + box-shadow: 0 6px 20px rgba(99, 102, 241, 0.5); + filter: brightness(1.08); +} + +.btn-secondary { + background: var(--bg-surface-elevated); + color: var(--text-main); + border: 1px solid var(--border-subtle); +} + +.btn-secondary:hover:not(:disabled) { + background: var(--bg-surface); + border-color: var(--border-glow); +} + +.btn-outline { + background: transparent; + color: var(--text-main); + border: 1px solid var(--border-subtle); +} + +.btn-outline:hover:not(:disabled) { + border-color: var(--color-primary); + color: var(--color-primary-light); + background: rgba(99, 102, 241, 0.08); +} + +.btn-ghost { + background: transparent; + color: var(--text-secondary); +} + +.btn-ghost:hover:not(:disabled) { + background: rgba(255, 255, 255, 0.06); + color: var(--text-main); +} + +.btn:disabled { + opacity: 0.55; + cursor: not-allowed; + filter: grayscale(0.4); +} + +.btn-spinner { + width: 18px; + height: 18px; + border: 2px solid rgba(255, 255, 255, 0.3); + border-top-color: #ffffff; + border-radius: 50%; + animation: spin 0.7s linear infinite; +} + +/* Inputs */ +.input-group { + display: flex; + flex-direction: column; + gap: 6px; + width: 100%; +} + +.input-label { + font-size: 0.85rem; + font-weight: 600; + color: var(--text-secondary); + display: flex; + align-items: center; + gap: 4px; +} + +.input-required { + color: var(--color-danger); +} + +.input-wrapper { + position: relative; + display: flex; + align-items: center; + width: 100%; +} + +.input-field { + width: 100%; + padding: 12px 16px; + font-size: 0.95rem; + background: var(--bg-input); + color: var(--text-main); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + outline: none; + transition: all var(--transition-normal); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.input-field::placeholder { + color: var(--text-muted); +} + +.input-field:focus { + border-color: var(--border-focus); + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2); +} + +.input-field.with-prefix { + padding-left: 44px; +} + +.input-field.with-suffix { + padding-right: 44px; +} + +.input-prefix-icon { + position: absolute; + left: 14px; + color: var(--text-muted); + display: flex; + align-items: center; + pointer-events: none; + transition: color var(--transition-fast); +} + +.input-wrapper:focus-within .input-prefix-icon { + color: var(--color-primary-light); +} + +.input-suffix-btn { + position: absolute; + right: 12px; + color: var(--text-muted); + display: flex; + align-items: center; + padding: 4px; + border-radius: var(--radius-sm); + transition: color var(--transition-fast); +} + +.input-suffix-btn:hover { + color: var(--text-main); +} + +.input-group.has-error .input-field { + border-color: var(--color-danger); + box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.15); +} + +.input-error-text { + font-size: 0.8rem; + color: var(--color-danger); + margin-top: 2px; +} + +.input-helper-text { + font-size: 0.8rem; + color: var(--text-muted); +} + +/* Badges */ +.badge { + display: inline-flex; + align-items: center; + gap: 5px; + font-weight: 600; + border-radius: var(--radius-full); + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.badge-sm { + padding: 3px 8px; + font-size: 0.7rem; +} + +.badge-md { + padding: 5px 12px; + font-size: 0.775rem; +} + +.badge-primary { + background: rgba(99, 102, 241, 0.15); + color: var(--color-primary-light); + border: 1px solid rgba(99, 102, 241, 0.3); +} + +.badge-success { + background: var(--color-success-bg); + color: var(--color-success); + border: 1px solid rgba(16, 185, 129, 0.3); +} + +.badge-warning { + background: var(--color-warning-bg); + color: var(--color-warning); + border: 1px solid rgba(245, 158, 11, 0.3); +} + +.badge-danger { + background: var(--color-danger-bg); + color: var(--color-danger); + border: 1px solid rgba(239, 68, 68, 0.3); +} + +.badge-info { + background: var(--color-info-bg); + color: var(--color-info); + border: 1px solid rgba(59, 130, 246, 0.3); +} + +/* Custom Card */ +.custom-card { + background: var(--bg-glass-card); + backdrop-filter: blur(14px); + -webkit-backdrop-filter: blur(14px); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + padding: 24px; + transition: all var(--transition-normal); +} + +.custom-card.card-hoverable:hover { + transform: translateY(-3px); + border-color: rgba(99, 102, 241, 0.3); + box-shadow: var(--shadow-lg), var(--shadow-glow); +} + +/* Toast Notification */ +.toast-notification { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 14px 18px; + border-radius: var(--radius-md); + margin-bottom: 18px; + width: 100%; +} + +.toast-icon { + flex-shrink: 0; + display: flex; + align-items: center; +} + +.toast-message { + flex: 1; + font-size: 0.9rem; + line-height: 1.4; + word-break: break-word; +} + +.toast-close { + flex-shrink: 0; + opacity: 0.7; + transition: opacity var(--transition-fast); +} + +.toast-close:hover { + opacity: 1; +} + +.toast-error { + background: var(--color-danger-bg); + color: #fca5a5; + border: 1px solid rgba(239, 68, 68, 0.3); +} + +[data-theme='light'] .toast-error { + color: #b91c1c; +} + +.toast-success { + background: var(--color-success-bg); + color: #6ee7b7; + border: 1px solid rgba(16, 185, 129, 0.3); +} + +[data-theme='light'] .toast-success { + color: #047857; +} + +.toast-warning { + background: var(--color-warning-bg); + color: #fcd34d; + border: 1px solid rgba(245, 158, 11, 0.3); +} + +[data-theme='light'] .toast-warning { + color: #b45309; +} + +.toast-info { + background: var(--color-info-bg); + color: #93c5fd; + border: 1px solid rgba(59, 130, 246, 0.3); +} + +[data-theme='light'] .toast-info { + color: #1d4ed8; +} diff --git a/src/components/layout/DashboardLayout.jsx b/src/components/layout/DashboardLayout.jsx new file mode 100644 index 0000000..b5a3653 --- /dev/null +++ b/src/components/layout/DashboardLayout.jsx @@ -0,0 +1,26 @@ +import React, { useState } from 'react'; +import { Sidebar } from './Sidebar'; +import { Header } from './Header'; +import './layout.css'; + +export function DashboardLayout({ children, activeTab, setActiveTab }) { + const [collapsed, setCollapsed] = useState(false); + const [mobileOpen, setMobileOpen] = useState(false); + + return ( +
+ +
+
+
{children}
+
+
+ ); +} diff --git a/src/components/layout/Header.jsx b/src/components/layout/Header.jsx new file mode 100644 index 0000000..ce0dd45 --- /dev/null +++ b/src/components/layout/Header.jsx @@ -0,0 +1,129 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Menu, Search, Bell, User as UserIcon, LogOut, ShieldCheck, ChevronDown } from 'lucide-react'; +import { useAuth } from '../../context/AuthContext'; +import { ThemeToggle } from './ThemeToggle'; +import './layout.css'; + +export function Header({ setMobileOpen }) { + const { user, logout, isDemoMode } = useAuth(); + const [dropdownOpen, setDropdownOpen] = useState(false); + const dropdownRef = useRef(null); + + // Close dropdown on click outside + useEffect(() => { + function handleClickOutside(event) { + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { + setDropdownOpen(false); + } + } + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + return ( +
+ {/* Left: Mobile hamburger & Search */} +
+ + +
+ + +
+
+ + {/* Right: Actions, Theme, User */} +
+ {/* Backend Connectivity Status Badge */} +
+ + {isDemoMode ? 'Demo Sandbox' : 'Backend Live'} +
+ + {/* Theme Switcher */} + + + {/* Notifications */} + + + {/* User Dropdown */} +
+ + + {dropdownOpen && ( +
+
+

{user?.fullname || user?.firstName}

+

{user?.email}

+
+ +
setDropdownOpen(false)}> + + Profile Settings +
+
setDropdownOpen(false)}> + + Security Token Info +
+
{ + setDropdownOpen(false); + logout(); + }} + > + + Sign Out +
+
+ )} +
+
+
+ ); +} diff --git a/src/components/layout/Sidebar.jsx b/src/components/layout/Sidebar.jsx new file mode 100644 index 0000000..9ff6478 --- /dev/null +++ b/src/components/layout/Sidebar.jsx @@ -0,0 +1,122 @@ +import React from 'react'; +import { + Shield, + LayoutDashboard, + Layers, + ShieldAlert, + Sliders, + Users, + Settings, + ChevronLeft, + ChevronRight, + LogOut, +} from 'lucide-react'; +import { useAuth } from '../../context/AuthContext'; +import './layout.css'; + +export function Sidebar({ collapsed, setCollapsed, mobileOpen, setMobileOpen, activeTab, setActiveTab }) { + const { user, logout } = useAuth(); + + const navItems = [ + { id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard }, + { id: 'workspaces', label: 'Workspaces', icon: Layers }, + { id: 'security', label: 'Security Audit', icon: ShieldAlert }, + { id: 'system', label: 'System Parameters', icon: Sliders }, + { id: 'users', label: 'User Directory', icon: Users }, + { id: 'settings', label: 'Settings', icon: Settings }, + ]; + + const handleItemClick = (id) => { + setActiveTab(id); + if (mobileOpen) { + setMobileOpen(false); + } + }; + + const getInitials = (name) => { + if (!name) return 'AU'; + return name + .split(' ') + .map((n) => n[0]) + .join('') + .slice(0, 2) + .toUpperCase(); + }; + + return ( + <> + {/* Mobile Backdrop */} + {mobileOpen && ( +
setMobileOpen(false)} /> + )} + + + + ); +} diff --git a/src/components/layout/ThemeToggle.jsx b/src/components/layout/ThemeToggle.jsx new file mode 100644 index 0000000..12e97af --- /dev/null +++ b/src/components/layout/ThemeToggle.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { Sun, Moon } from 'lucide-react'; +import { useTheme } from '../../context/ThemeContext'; +import './layout.css'; + +export function ThemeToggle({ className = '' }) { + const { theme, toggleTheme, isDark } = useTheme(); + + return ( + + ); +} diff --git a/src/components/layout/layout.css b/src/components/layout/layout.css new file mode 100644 index 0000000..c830cac --- /dev/null +++ b/src/components/layout/layout.css @@ -0,0 +1,424 @@ +/* Layout CSS (Header, Sidebar, Navigation) */ + +/* Theme Toggle Button */ +.theme-toggle-btn { + width: 38px; + height: 38px; + border-radius: var(--radius-md); + background: var(--bg-surface-elevated); + border: 1px solid var(--border-subtle); + display: flex; + align-items: center; + justify-content: center; + color: var(--text-secondary); + transition: all var(--transition-normal); +} + +.theme-toggle-btn:hover { + background: var(--bg-surface); + color: var(--color-primary-light); + border-color: var(--border-glow); +} + +.theme-toggle-inner { + display: flex; + align-items: center; + justify-content: center; + transition: transform var(--transition-smooth); +} + +/* Dashboard Layout Structure */ +.dashboard-layout { + display: flex; + min-height: 100vh; + background-color: var(--bg-app); +} + +/* Sidebar */ +.dashboard-sidebar { + width: 260px; + background: var(--bg-surface); + border-right: 1px solid var(--border-subtle); + display: flex; + flex-direction: column; + position: sticky; + top: 0; + height: 100vh; + z-index: 50; + transition: width var(--transition-smooth), transform var(--transition-smooth); +} + +.dashboard-sidebar.collapsed { + width: 78px; +} + +.sidebar-header { + height: 72px; + padding: 0 20px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--border-subtle); +} + +.sidebar-brand { + display: flex; + align-items: center; + gap: 12px; + overflow: hidden; +} + +.sidebar-brand-icon { + width: 38px; + height: 38px; + border-radius: var(--radius-md); + background: var(--gradient-primary); + display: flex; + align-items: center; + justify-content: center; + color: #ffffff; + flex-shrink: 0; + box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4); +} + +.sidebar-brand-title { + font-size: 1.25rem; + font-weight: 800; + color: var(--text-main); + white-space: nowrap; +} + +.sidebar-collapse-btn { + width: 32px; + height: 32px; + border-radius: var(--radius-sm); + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + transition: all var(--transition-fast); +} + +.sidebar-collapse-btn:hover { + background: var(--bg-surface-elevated); + color: var(--text-main); +} + +/* Sidebar Nav */ +.sidebar-nav { + flex: 1; + padding: 20px 14px; + display: flex; + flex-direction: column; + gap: 6px; + overflow-y: auto; +} + +.sidebar-section-title { + font-size: 0.7rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--text-muted); + padding: 8px 10px; + margin-top: 8px; +} + +.sidebar-item { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 14px; + border-radius: var(--radius-md); + color: var(--text-secondary); + font-weight: 500; + font-size: 0.925rem; + transition: all var(--transition-fast); + cursor: pointer; + white-space: nowrap; + position: relative; +} + +.sidebar-item:hover { + background: var(--bg-surface-elevated); + color: var(--text-main); +} + +.sidebar-item.active { + background: rgba(99, 102, 241, 0.12); + color: var(--color-primary-light); + font-weight: 600; +} + +.sidebar-item.active::before { + content: ''; + position: absolute; + left: 0; + top: 15%; + bottom: 15%; + width: 3px; + background: var(--color-primary); + border-radius: 0 var(--radius-xs) var(--radius-xs) 0; +} + +.sidebar-item-icon { + flex-shrink: 0; +} + +/* Sidebar Footer User Card */ +.sidebar-footer { + padding: 16px; + border-top: 1px solid var(--border-subtle); +} + +.sidebar-user-pill { + display: flex; + align-items: center; + gap: 12px; + padding: 8px 10px; + border-radius: var(--radius-md); + background: var(--bg-surface-elevated); + cursor: pointer; + transition: all var(--transition-fast); + overflow: hidden; +} + +.sidebar-user-pill:hover { + background: rgba(99, 102, 241, 0.08); +} + +.sidebar-user-avatar { + width: 36px; + height: 36px; + border-radius: 50%; + background: var(--gradient-primary); + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + color: #ffffff; + flex-shrink: 0; +} + +.sidebar-user-details { + display: flex; + flex-direction: column; + overflow: hidden; +} + +.sidebar-user-name { + font-size: 0.85rem; + font-weight: 600; + color: var(--text-main); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.sidebar-user-role { + font-size: 0.725rem; + color: var(--color-primary-light); + white-space: nowrap; +} + +/* Main Content Area */ +.dashboard-main { + flex: 1; + display: flex; + flex-direction: column; + min-width: 0; +} + +/* Header */ +.dashboard-header { + height: 72px; + padding: 0 32px; + display: flex; + align-items: center; + justify-content: space-between; + background: var(--bg-glass); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border-bottom: 1px solid var(--border-subtle); + position: sticky; + top: 0; + z-index: 40; +} + +.header-left { + display: flex; + align-items: center; + gap: 16px; +} + +.mobile-menu-btn { + display: none; + width: 36px; + height: 36px; + align-items: center; + justify-content: center; + border-radius: var(--radius-sm); + color: var(--text-secondary); +} + +.header-search { + position: relative; + display: flex; + align-items: center; + width: 300px; +} + +.header-search-input { + width: 100%; + padding: 8px 14px 8px 38px; + background: var(--bg-surface-elevated); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-full); + color: var(--text-main); + font-size: 0.875rem; + outline: none; + transition: all var(--transition-fast); +} + +.header-search-input:focus { + border-color: var(--border-focus); + box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2); +} + +.header-search-icon { + position: absolute; + left: 12px; + color: var(--text-muted); + pointer-events: none; +} + +.header-right { + display: flex; + align-items: center; + gap: 14px; +} + +.header-btn { + width: 38px; + height: 38px; + border-radius: var(--radius-md); + background: var(--bg-surface-elevated); + border: 1px solid var(--border-subtle); + display: flex; + align-items: center; + justify-content: center; + color: var(--text-secondary); + transition: all var(--transition-fast); + position: relative; +} + +.header-btn:hover { + color: var(--text-main); + border-color: var(--border-glow); +} + +.header-badge-dot { + position: absolute; + top: 8px; + right: 8px; + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--color-secondary); +} + +/* Profile Menu Dropdown */ +.profile-dropdown-wrapper { + position: relative; +} + +.profile-trigger-btn { + display: flex; + align-items: center; + gap: 10px; + padding: 4px 10px 4px 4px; + border-radius: var(--radius-full); + background: var(--bg-surface-elevated); + border: 1px solid var(--border-subtle); + cursor: pointer; + transition: all var(--transition-fast); +} + +.profile-trigger-btn:hover { + border-color: var(--border-glow); +} + +.profile-dropdown-menu { + position: absolute; + right: 0; + top: calc(100% + 8px); + width: 220px; + background: var(--bg-surface); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + box-shadow: var(--shadow-lg); + padding: 8px; + z-index: 100; + display: flex; + flex-direction: column; + gap: 4px; +} + +.dropdown-item { + display: flex; + align-items: center; + gap: 10px; + padding: 9px 12px; + border-radius: var(--radius-sm); + color: var(--text-secondary); + font-size: 0.875rem; + cursor: pointer; + transition: all var(--transition-fast); +} + +.dropdown-item:hover { + background: var(--bg-surface-elevated); + color: var(--text-main); +} + +.dropdown-item.danger:hover { + background: var(--color-danger-bg); + color: var(--color-danger); +} + +/* Responsive Mobile Sidebar overlay */ +.sidebar-overlay { + display: none; +} + +@media (max-width: 900px) { + .dashboard-header { + padding: 0 16px; + } + .header-search { + display: none; + } + .mobile-menu-btn { + display: flex; + } + .dashboard-sidebar { + position: fixed; + left: -280px; + top: 0; + bottom: 0; + width: 280px !important; + transition: left var(--transition-smooth); + box-shadow: var(--shadow-lg); + } + .dashboard-sidebar.mobile-open { + left: 0; + } + .sidebar-overlay { + display: block; + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(4px); + z-index: 45; + } +} diff --git a/src/context/AuthContext.jsx b/src/context/AuthContext.jsx new file mode 100644 index 0000000..75620fe --- /dev/null +++ b/src/context/AuthContext.jsx @@ -0,0 +1,84 @@ +import React, { createContext, useContext, useState, useEffect } from 'react'; +import { authService } from '../services/authService'; +import { DEMO_USER } from '../services/mockData'; + +const AuthContext = createContext(); + +export function AuthProvider({ children }) { + const [user, setUser] = useState(null); + const [token, setToken] = useState(null); + const [loading, setLoading] = useState(true); + const [isDemoMode, setIsDemoMode] = useState(false); + + useEffect(() => { + // Restore session on mount + try { + const storedToken = localStorage.getItem('authsec_token'); + const storedUser = localStorage.getItem('authsec_user'); + if (storedToken && storedUser) { + setToken(storedToken); + setUser(JSON.parse(storedUser)); + } + } catch (e) { + console.error('Failed to parse stored session:', e); + localStorage.removeItem('authsec_token'); + localStorage.removeItem('authsec_user'); + } finally { + setLoading(false); + } + }, []); + + const login = async (email, password, forceMock = false) => { + const res = await authService.login(email, password, forceMock); + const item = res.item || res; + const authToken = item.token || 'demo-token-' + Date.now(); + + // Store in localStorage + localStorage.setItem('authsec_token', authToken); + localStorage.setItem('authsec_user', JSON.stringify(item)); + + setToken(authToken); + setUser(item); + if (forceMock || res.operationMessage?.includes('Demo')) { + setIsDemoMode(true); + } + return item; + }; + + const loginAsDemo = async () => { + return login(DEMO_USER.email, 'admin123', true); + }; + + const logout = async () => { + await authService.logout(); + setUser(null); + setToken(null); + setIsDemoMode(false); + }; + + const register = async (userData) => { + return authService.registerUser(userData); + }; + + const value = { + user, + token, + loading, + isAuthenticated: !!token && !!user, + isDemoMode, + login, + loginAsDemo, + logout, + register, + }; + + return {children}; +} + +export function useAuth() { + const context = useContext(AuthContext); + if (!context) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +} diff --git a/src/context/ThemeContext.jsx b/src/context/ThemeContext.jsx new file mode 100644 index 0000000..b6f3534 --- /dev/null +++ b/src/context/ThemeContext.jsx @@ -0,0 +1,36 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; + +const ThemeContext = createContext(); + +export function ThemeProvider({ children }) { + const [theme, setTheme] = useState(() => { + const saved = localStorage.getItem('authsec_theme'); + if (saved) return saved; + return window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches + ? 'light' + : 'dark'; + }); + + useEffect(() => { + document.documentElement.setAttribute('data-theme', theme); + localStorage.setItem('authsec_theme', theme); + }, [theme]); + + const toggleTheme = () => { + setTheme((prev) => (prev === 'dark' ? 'light' : 'dark')); + }; + + return ( + + {children} + + ); +} + +export function useTheme() { + const context = useContext(ThemeContext); + if (!context) { + throw new Error('useTheme must be used within a ThemeProvider'); + } + return context; +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..c2126dc --- /dev/null +++ b/src/index.css @@ -0,0 +1 @@ +@import './styles/global.css'; diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..b9a1a6d --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.jsx' + +createRoot(document.getElementById('root')).render( + + + , +) diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx new file mode 100644 index 0000000..aba162a --- /dev/null +++ b/src/pages/Dashboard.jsx @@ -0,0 +1,330 @@ +import React, { useState } from 'react'; +import { + ShieldCheck, + Lock, + LayoutGrid, + Activity, + Key, + ShieldAlert, + CheckCircle, + MailCheck, + User, + Sliders, + FileText, + Copy, + ExternalLink, + Info, +} from 'lucide-react'; +import { useAuth } from '../context/AuthContext'; +import { DashboardLayout } from '../components/layout/DashboardLayout'; +import { Badge } from '../components/common/Badge'; +import { Button } from '../components/common/Button'; +import { Card } from '../components/common/Card'; +import { MOCK_STATS, MOCK_ACTIVITIES } from '../services/mockData'; +import '../styles/dashboard.css'; + +export function Dashboard() { + const { user, token, isDemoMode, logout } = useAuth(); + const [activeTab, setActiveTab] = useState('dashboard'); + const [showTokenModal, setShowTokenModal] = useState(false); + const [copied, setCopied] = useState(false); + + // Icon mapping for stats + const statIcons = { + ShieldCheck, + Lock, + LayoutGrid, + Activity, + }; + + // Icon mapping for activities + const activityIcons = { + Key, + ShieldAlert, + CheckCircle, + MailCheck, + }; + + const handleCopyToken = () => { + if (token) { + navigator.clipboard.writeText(token); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } + }; + + return ( + + {/* Hero Welcome Banner */} +
+
+
+ + Identity & Access Controller +
+

+ Welcome back, {user?.firstName || 'Alex'} +

+

+ AuthSec base project is operational. Your session is protected with Spring Boot JWT + authentication and granular role-based security boundaries. +

+
+ +
+
+ + JWT: {token ? `${token.slice(0, 14)}...` : 'demo-token'} +
+
+ + +
+
+
+ + {/* Metric Stats Cards */} +
+ {MOCK_STATS.map((stat) => { + const Icon = statIcons[stat.icon] || Activity; + return ( +
+
+ {stat.title} +
+ +
+
+ +
{stat.value}
+ +
+ {stat.subtitle} + {stat.trend} +
+
+ ); + })} +
+ + {/* Quick Action Buttons */} +
+
+

Quick Actions

+
+ +
+
setShowTokenModal(true)}> +
+ +
+
+ Security Token + View JWT payload & auth headers +
+
+ +
alert(`User Profile:\nName: ${user?.fullname || user?.firstName}\nEmail: ${user?.email}\nRoles: ${user?.roles?.join(', ')}`)} + > +
+ +
+
+ User Profile + Account ID #{user?.userId || '101'} +
+
+ +
alert('System Parameters: Spring Boot Service Port 9292\nCORS: Enabled\nToken Lifetime: 24h')} + > +
+ +
+
+ System Parameters + Endpoints & security policies +
+
+ +
setActiveTab('security')} + > +
+ +
+
+ Audit Reports + Inspect security logs +
+
+
+
+ + {/* Live Security Activity Stream */} +
+
+
+

Recent Security Events

+

+ Audited session entries recorded for this workspace +

+
+ + Live Stream + +
+ +
+ {MOCK_ACTIVITIES.map((act) => { + const Icon = activityIcons[act.icon] || Key; + return ( +
+
+
+ +
+
+ {act.title} + + {act.description} • IP: {act.ip} + +
+
+ +
+ + {act.badge} + + {act.timestamp} +
+
+ ); + })} +
+
+ + {/* Token Inspector Modal */} + {showTokenModal && ( +
setShowTokenModal(false)} + > +
e.stopPropagation()} + > +
+
+ +

Session Token Inspector

+
+ +
+ +

+ This Bearer token is automatically attached to API requests in the Authorization header. +

+ +
+
+ Account Email: + {user?.email || 'N/A'} +
+
+ User ID: + {user?.userId || '101'} +
+
+ Assigned Roles: + {Array.isArray(user?.roles) ? user.roles.join(', ') : 'ROLE_ADMIN'} +
+
+ Mode: + {isDemoMode ? 'Sandbox Demo Mode' : 'Live Spring Boot'} +
+
+ +
+ +