diff --git a/.env b/.env
new file mode 100644
index 0000000..907e903
--- /dev/null
+++ b/.env
@@ -0,0 +1,2 @@
+VITE_API_BASE_URL=http://localhost:8000
+VITE_API_KEY=DUMUqJrNeyuaPDSZSsMf91qjgwc5K3N7
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/index.html b/index.html
new file mode 100644
index 0000000..cf7c47c
--- /dev/null
+++ b/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ CureSelect Healthcare | Super Admin
+
+
+
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..e0afb5b
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1852 @@
+{
+ "name": "super-admin-home-care",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "super-admin-home-care",
+ "version": "1.0.0",
+ "dependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.12",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.4",
+ "vite": "^6.0.1"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz",
+ "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.8",
+ "@babel/types": "^7.29.8",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
+ "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.8"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
+ "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
+ "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz",
+ "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.8",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.8",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.8",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
+ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@napi-rs/lzma-linux-x64-gnu": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
+ "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^22.20 || ^24.12 || >=25"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.27",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz",
+ "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz",
+ "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz",
+ "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz",
+ "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz",
+ "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz",
+ "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz",
+ "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz",
+ "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz",
+ "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz",
+ "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz",
+ "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz",
+ "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz",
+ "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz",
+ "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz",
+ "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz",
+ "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz",
+ "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz",
+ "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz",
+ "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz",
+ "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz",
+ "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz",
+ "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz",
+ "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz",
+ "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz",
+ "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.15",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.31",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz",
+ "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.3.7",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
+ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^18.0.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.11.14",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.14.tgz",
+ "integrity": "sha512-JyJ954WzuIR8/FFzX0o5krdSTrBAkcCSRfWSleRsIHSWV+cZe2FI1PKggVkFke1hBldRs+LRxUczzE9iPmgZww==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.8",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
+ "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.11.12",
+ "caniuse-lite": "^1.0.30001809",
+ "electron-to-chromium": "^1.5.402",
+ "node-releases": "^2.0.53",
+ "update-browserslist-db": "^1.3.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001809",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz",
+ "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.408",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.408.tgz",
+ "integrity": "sha512-SLoprcYpJ/OH2v2ps0+N5biv9H4/KBT3+YmmDew64TwK5y9j2wv7pMOFY7IorVkyMtEyLSCRlXKLsNlakeAlPw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.18",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
+ "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
+ "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/node-releases": {
+ "version": "2.0.53",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz",
+ "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "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.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.26",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
+ "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
+ "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.17",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz",
+ "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@napi-rs/lzma-linux-x64-gnu": "1.5.1",
+ "@rollup/rollup-android-arm-eabi": "4.62.4",
+ "@rollup/rollup-android-arm64": "4.62.4",
+ "@rollup/rollup-darwin-arm64": "4.62.4",
+ "@rollup/rollup-darwin-x64": "4.62.4",
+ "@rollup/rollup-freebsd-arm64": "4.62.4",
+ "@rollup/rollup-freebsd-x64": "4.62.4",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.4",
+ "@rollup/rollup-linux-arm-musleabihf": "4.62.4",
+ "@rollup/rollup-linux-arm64-gnu": "4.62.4",
+ "@rollup/rollup-linux-arm64-musl": "4.62.4",
+ "@rollup/rollup-linux-loong64-gnu": "4.62.4",
+ "@rollup/rollup-linux-loong64-musl": "4.62.4",
+ "@rollup/rollup-linux-ppc64-gnu": "4.62.4",
+ "@rollup/rollup-linux-ppc64-musl": "4.62.4",
+ "@rollup/rollup-linux-riscv64-gnu": "4.62.4",
+ "@rollup/rollup-linux-riscv64-musl": "4.62.4",
+ "@rollup/rollup-linux-s390x-gnu": "4.62.4",
+ "@rollup/rollup-linux-x64-gnu": "4.62.4",
+ "@rollup/rollup-linux-x64-musl": "4.62.4",
+ "@rollup/rollup-openbsd-x64": "4.62.4",
+ "@rollup/rollup-openharmony-arm64": "4.62.4",
+ "@rollup/rollup-win32-arm64-msvc": "4.62.4",
+ "@rollup/rollup-win32-ia32-msvc": "4.62.4",
+ "@rollup/rollup-win32-x64-gnu": "4.62.4",
+ "@rollup/rollup-win32-x64-msvc": "4.62.4",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/source-map-js": {
+ "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,
+ "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/update-browserslist-db": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz",
+ "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.4.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
+ "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..5aa3a97
--- /dev/null
+++ b/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "super-admin-home-care",
+ "private": true,
+ "version": "1.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.12",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.4",
+ "vite": "^6.0.1"
+ }
+}
\ No newline at end of file
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/guardian-logo.png b/public/guardian-logo.png
new file mode 100644
index 0000000..055709b
Binary files /dev/null and b/public/guardian-logo.png differ
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..e46b81d
--- /dev/null
+++ b/src/App.css
@@ -0,0 +1,4 @@
+.app-root {
+ min-height: 100vh;
+ width: 100%;
+}
diff --git a/src/App.jsx b/src/App.jsx
new file mode 100644
index 0000000..293d837
--- /dev/null
+++ b/src/App.jsx
@@ -0,0 +1,48 @@
+import { useState, useEffect } from 'react'
+import LoginPage from './pages/LoginPage'
+import Dashboard from './pages/Dashboard'
+import { getToken, getStoredUser } from './services/api'
+import { adminService } from './services/adminService'
+import './App.css'
+
+function App() {
+ const [isAuthenticated, setIsAuthenticated] = useState(() => {
+ return !!getToken() && !!getStoredUser()
+ })
+ const [adminUser, setAdminUser] = useState(() => {
+ return getStoredUser()
+ })
+
+ useEffect(() => {
+ const handleUnauthorized = () => {
+ setIsAuthenticated(false)
+ setAdminUser(null)
+ }
+
+ window.addEventListener('auth:unauthorized', handleUnauthorized)
+ return () => window.removeEventListener('auth:unauthorized', handleUnauthorized)
+ }, [])
+
+ const handleLoginSuccess = (userData) => {
+ setAdminUser(userData)
+ setIsAuthenticated(true)
+ }
+
+ const handleLogout = () => {
+ adminService.auth.logout()
+ setAdminUser(null)
+ setIsAuthenticated(false)
+ }
+
+ return (
+
+ {isAuthenticated ? (
+
+ ) : (
+
+ )}
+
+ )
+}
+
+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 @@
+
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 0000000..b00c770
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,106 @@
+/* ============================================================
+ Guardian HomeCare — Super Admin Global Styles
+ ============================================================ */
+
+*, *::before, *::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+:root {
+ /* Brand Colors */
+ --primary-50: #eef6ff;
+ --primary-100: #d9ecff;
+ --primary-200: #bcdcff;
+ --primary-300: #8ec5fe;
+ --primary-400: #5aa5fb;
+ --primary-500: #3b82f6;
+ --primary-600: #2563eb;
+ --primary-700: #1d4ed8;
+ --primary-800: #1e40af;
+ --primary-900: #1e3a8a;
+
+ --teal-400: #2dd4bf;
+ --teal-500: #14b8a6;
+ --teal-600: #0d9488;
+
+ /* Dark palette */
+ --dark-900: #050d1a;
+ --dark-800: #0a1628;
+ --dark-700: #0f2040;
+ --dark-600: #162a52;
+ --dark-500: #1e3a6e;
+
+ /* Neutrals */
+ --gray-50: #f8fafc;
+ --gray-100: #f1f5f9;
+ --gray-200: #e2e8f0;
+ --gray-300: #cbd5e1;
+ --gray-400: #94a3b8;
+ --gray-500: #64748b;
+ --gray-600: #475569;
+ --gray-700: #334155;
+ --gray-800: #1e293b;
+ --gray-900: #0f172a;
+
+ /* Semantic */
+ --danger: #ef4444;
+ --success: #22c55e;
+ --warning: #f59e0b;
+
+ /* Typography */
+ --font-primary: 'Plus Jakarta Sans', 'Inter', system-ui, sans-serif;
+ --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
+
+ /* Spacing */
+ --radius-sm: 6px;
+ --radius-md: 12px;
+ --radius-lg: 16px;
+ --radius-xl: 24px;
+ --radius-full: 9999px;
+
+ /* Shadows */
+ --shadow-sm: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
+ --shadow-md: 0 4px 16px rgba(0,0,0,0.25);
+ --shadow-lg: 0 10px 40px rgba(0,0,0,0.35);
+ --shadow-glow: 0 0 40px rgba(59, 130, 246, 0.4);
+ --shadow-teal: 0 0 30px rgba(20, 184, 166, 0.3);
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ font-family: var(--font-primary);
+ background: var(--dark-900);
+ color: var(--gray-50);
+ line-height: 1.6;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ min-height: 100vh;
+ overflow-x: hidden;
+}
+
+#root {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+/* ── Scrollbar ── */
+::-webkit-scrollbar { width: 6px; height: 6px; }
+::-webkit-scrollbar-track { background: var(--dark-800); }
+::-webkit-scrollbar-thumb { background: var(--dark-500); border-radius: 99px; }
+::-webkit-scrollbar-thumb:hover { background: var(--primary-600); }
+
+/* ── Selection ── */
+::selection { background: var(--primary-700); color: #fff; }
+
+/* ── Focus ring ── */
+:focus-visible {
+ outline: 2px solid var(--primary-500);
+ outline-offset: 3px;
+ border-radius: var(--radius-sm);
+}
diff --git a/src/main.jsx b/src/main.jsx
new file mode 100644
index 0000000..54b39dd
--- /dev/null
+++ b/src/main.jsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './App.jsx'
+import './index.css'
+
+ReactDOM.createRoot(document.getElementById('root')).render(
+
+
+ ,
+)
diff --git a/src/pages/Dashboard.css b/src/pages/Dashboard.css
new file mode 100644
index 0000000..3826b1f
--- /dev/null
+++ b/src/pages/Dashboard.css
@@ -0,0 +1,1028 @@
+/* ============================================================
+ Dashboard.css — CureSelect Healthcare LLP Super Admin
+ ============================================================ */
+
+.dashboard-container {
+ display: flex;
+ min-height: 100vh;
+ width: 100%;
+ background: var(--dark-900);
+ color: var(--gray-100);
+ overflow: hidden;
+}
+
+/* ── Sidebar ── */
+.sidebar {
+ width: 280px;
+ background: var(--dark-800);
+ border-right: 1px solid rgba(255, 255, 255, 0.05);
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding: 24px 16px;
+ flex-shrink: 0;
+ z-index: 10;
+}
+
+.sidebar-header {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 8px 12px 24px 12px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.logo-icon-wrapper {
+ background: rgba(59, 130, 246, 0.1);
+ border: 1px solid rgba(59, 130, 246, 0.2);
+ border-radius: var(--radius-md);
+ width: 42px;
+ height: 42px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.logo-text-block {
+ display: flex;
+ flex-direction: column;
+}
+
+.logo-brand {
+ font-size: 18px;
+ font-weight: 800;
+ color: #fff;
+ letter-spacing: -0.02em;
+}
+
+.logo-portal {
+ font-size: 11px;
+ color: var(--teal-400);
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin-top: -2px;
+}
+
+.nav-menu {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin-top: 20px;
+ flex-grow: 1;
+}
+
+.nav-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ background: none;
+ border: none;
+ color: var(--gray-400);
+ padding: 11px 14px;
+ border-radius: var(--radius-md);
+ font-size: 13.5px;
+ font-weight: 600;
+ cursor: pointer;
+ width: 100%;
+ text-align: left;
+ transition: all 0.2s ease;
+ border: 1px solid transparent;
+}
+
+.nav-item svg {
+ opacity: 0.7;
+ transition: transform 0.2s;
+ flex-shrink: 0;
+}
+
+.nav-item:hover {
+ color: #fff;
+ background: rgba(255, 255, 255, 0.03);
+}
+
+.nav-item:hover svg {
+ transform: translateX(2px);
+}
+
+.nav-item.active {
+ color: #fff;
+ background: linear-gradient(135deg, rgba(37, 99, 235, 0.15) 0%, rgba(20, 184, 166, 0.05) 100%);
+ border: 1px solid rgba(37, 99, 235, 0.25);
+ box-shadow: 0 4px 12px rgba(37, 99, 235, 0.1);
+}
+
+.nav-item.active svg {
+ color: var(--primary-400);
+ opacity: 1;
+}
+
+.sidebar-footer {
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
+ padding-top: 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.user-profile-badge {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 8px 12px;
+}
+
+.avatar-placeholder {
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, var(--primary-600), var(--teal-600));
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+ font-size: 12px;
+ color: #fff;
+}
+
+.user-info {
+ display: flex;
+ flex-direction: column;
+}
+
+.user-name {
+ font-size: 14px;
+ font-weight: 700;
+ color: #fff;
+}
+
+.user-role {
+ font-size: 11px;
+ color: var(--gray-500);
+}
+
+.logout-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ background: rgba(239, 68, 68, 0.08);
+ border: 1px solid rgba(239, 68, 68, 0.15);
+ color: #fca5a5;
+ padding: 10px;
+ border-radius: var(--radius-md);
+ font-size: 13px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.logout-button:hover {
+ background: var(--danger);
+ color: #fff;
+ border-color: var(--danger);
+ box-shadow: 0 4px 12px rgba(239, 68, 68, 0.2);
+}
+
+/* ── Main Viewport ── */
+.main-viewport {
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+ overflow-y: auto;
+ position: relative;
+ z-index: 1;
+ height: 100vh;
+}
+
+/* ── Topbar ── */
+.topbar {
+ height: 70px;
+ background: rgba(10, 22, 40, 0.5);
+ backdrop-filter: blur(8px);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 32px;
+ flex-shrink: 0;
+}
+
+.session-stats-bar {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+}
+
+.session-kpi {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 12.5px;
+ color: var(--gray-400);
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ padding: 6px 12px;
+ border-radius: var(--radius-full);
+}
+
+.session-kpi-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--teal-400);
+}
+
+.red-pulse {
+ background: var(--danger);
+ box-shadow: 0 0 8px var(--danger);
+ animation: pulse-dot 2s ease-in-out infinite;
+}
+
+@keyframes pulse-dot {
+ 0%, 100% { opacity: 1; transform: scale(1); }
+ 50% { opacity: 0.6; transform: scale(0.85); }
+}
+
+.topbar-actions {
+ display: flex;
+ align-items: center;
+ gap: 20px;
+}
+
+.system-indicator {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 12px;
+ color: var(--gray-400);
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ padding: 6px 12px;
+ border-radius: var(--radius-full);
+}
+
+.system-status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--success);
+ box-shadow: 0 0 8px var(--success);
+}
+
+/* ── Content Pane ── */
+.content-pane {
+ padding: 32px;
+ flex-grow: 1;
+}
+
+.tab-header-block {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 32px;
+}
+
+.pane-title {
+ font-size: 26px;
+ font-weight: 800;
+ color: #fff;
+ letter-spacing: -0.02em;
+}
+
+.pane-subtitle {
+ font-size: 14px;
+ color: var(--gray-400);
+ margin-top: 4px;
+}
+
+.action-btn {
+ background: linear-gradient(135deg, var(--primary-600), var(--teal-600));
+ border: none;
+ color: #fff;
+ padding: 10px 20px;
+ border-radius: var(--radius-md);
+ font-weight: 700;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+ box-shadow: var(--shadow-glow);
+ transition: transform 0.2s, box-shadow 0.2s;
+}
+
+.action-btn:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 8px 24px rgba(37, 99, 235, 0.5);
+}
+
+/* ── Metrics Grid ── */
+.metrics-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+ gap: 24px;
+ margin-bottom: 32px;
+}
+
+.metric-card {
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ border-radius: var(--radius-lg);
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ overflow: hidden;
+ transition: all 0.3s ease;
+}
+
+.card-glass-glow {
+ position: absolute;
+ top: -50px;
+ right: -50px;
+ width: 150px;
+ height: 150px;
+ border-radius: 50%;
+ filter: blur(40px);
+ pointer-events: none;
+ opacity: 0.15;
+ transition: opacity 0.3s;
+}
+
+.glow-blue .card-glass-glow { background: var(--primary-500); }
+.glow-teal .card-glass-glow { background: var(--teal-500); }
+.glow-yellow .card-glass-glow { background: var(--warning); }
+
+.metric-card:hover {
+ background: rgba(255, 255, 255, 0.05);
+ border-color: rgba(255, 255, 255, 0.12);
+ transform: translateY(-2px);
+}
+
+.metric-card:hover .card-glass-glow {
+ opacity: 0.3;
+}
+
+.metric-title {
+ font-size: 11px;
+ font-weight: 700;
+ color: var(--gray-500);
+ letter-spacing: 0.08em;
+}
+
+.metric-value {
+ font-size: 36px;
+ font-weight: 800;
+ color: #fff;
+ margin: 12px 0 6px 0;
+ letter-spacing: -0.03em;
+}
+
+.metric-subtext {
+ font-size: 12px;
+ color: var(--gray-400);
+}
+
+/* ── Details Layout ── */
+.details-layout {
+ display: grid;
+ grid-template-columns: 1fr 1.5fr;
+ gap: 24px;
+}
+
+.detail-panel {
+ background: rgba(10, 22, 40, 0.4);
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ border-radius: var(--radius-lg);
+ padding: 24px;
+}
+
+.panel-title {
+ font-size: 16px;
+ font-weight: 700;
+ color: #fff;
+ margin-bottom: 20px;
+}
+
+/* Integration Pingers */
+.pinger-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.pinger-card {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ padding: 14px 18px;
+ border-radius: var(--radius-md);
+}
+
+.pinger-meta {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.pinger-name {
+ font-size: 13.5px;
+ font-weight: 600;
+ color: #fff;
+}
+
+.pinger-value {
+ font-size: 12px;
+ color: var(--teal-400);
+ font-weight: 600;
+}
+
+/* Ledger & Reconciliation */
+.ledger-reconciliation {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.reconciliation-summary {
+ display: flex;
+ gap: 16px;
+}
+
+.ledger-kpi {
+ flex: 1;
+ padding: 16px;
+ border-radius: var(--radius-md);
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.bg-teal {
+ background: rgba(20, 184, 166, 0.1);
+ border: 1px solid rgba(20, 184, 166, 0.2);
+}
+
+.bg-blue {
+ background: rgba(59, 130, 246, 0.1);
+ border: 1px solid rgba(59, 130, 246, 0.2);
+}
+
+.kpi-num {
+ font-size: 22px;
+ font-weight: 800;
+ color: #fff;
+}
+
+.kpi-label {
+ font-size: 11px;
+ color: var(--gray-400);
+ font-weight: 500;
+}
+
+/* Alerts List & Approvals */
+.alerts-list {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.alert-card-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 14px 16px;
+ border-radius: var(--radius-md);
+ border-left: 4px solid transparent;
+ background: rgba(255, 255, 255, 0.02);
+}
+
+.alert-critical {
+ border-left-color: var(--danger);
+ background: rgba(239, 68, 68, 0.05);
+}
+
+.alert-warning {
+ border-left-color: var(--warning);
+ background: rgba(245, 158, 11, 0.05);
+}
+
+.alert-info {
+ border-left-color: var(--primary-500);
+ background: rgba(59, 130, 246, 0.05);
+}
+
+.alert-body {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.alert-message {
+ font-size: 13.5px;
+ font-weight: 500;
+ color: var(--gray-200);
+}
+
+.alert-time {
+ font-size: 11px;
+ color: var(--gray-500);
+}
+
+.resolve-btn {
+ background: rgba(255, 255, 255, 0.06);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: var(--gray-300);
+ padding: 6px 12px;
+ border-radius: var(--radius-sm);
+ font-size: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.resolve-btn:hover:not(:disabled) {
+ background: var(--success);
+ color: #fff;
+ border-color: var(--success);
+}
+
+.resolve-btn:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
+.empty-alerts {
+ color: var(--gray-500);
+ text-align: center;
+ padding: 40px 0;
+ font-size: 14px;
+}
+
+/* Table */
+.telemetry-table {
+ width: 100%;
+ border-collapse: collapse;
+ text-align: left;
+}
+
+.telemetry-table th {
+ padding: 12px 16px;
+ font-size: 11px;
+ font-weight: 700;
+ color: var(--gray-500);
+ text-transform: uppercase;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.telemetry-table td {
+ padding: 16px;
+ font-size: 13.5px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.03);
+}
+
+.hr-badge {
+ padding: 4px 10px;
+ border-radius: var(--radius-full);
+ font-size: 12px;
+ font-weight: 700;
+ display: inline-block;
+}
+
+.hr-normal {
+ background: rgba(34, 197, 94, 0.12);
+ color: var(--success);
+}
+
+.hr-high {
+ background: rgba(239, 68, 68, 0.12);
+ color: var(--danger);
+ animation: pulse-hr 1.5s infinite;
+}
+
+@keyframes pulse-hr {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.7; }
+}
+
+.status-indicator-tag {
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ padding: 4px 10px;
+ border-radius: var(--radius-full);
+ font-size: 11px;
+ color: var(--gray-400);
+}
+
+/* ── Admin Provisioning & Roster Cards ── */
+.caregivers-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 24px;
+}
+
+.cg-card {
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ border-radius: var(--radius-lg);
+ padding: 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ transition: all 0.25s ease;
+}
+
+.cg-card:hover {
+ background: rgba(255, 255, 255, 0.04);
+ border-color: rgba(59, 130, 246, 0.2);
+ transform: translateY(-2px);
+}
+
+.cg-header {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.cg-avatar {
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ background: rgba(59, 130, 246, 0.15);
+ color: var(--primary-300);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+ font-size: 14px;
+}
+
+.cg-name {
+ font-size: 15px;
+ font-weight: 700;
+ color: #fff;
+}
+
+.cg-role {
+ font-size: 12.5px;
+ color: var(--gray-400);
+ word-break: break-all;
+}
+
+.cg-body {
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+ padding: 12px 0;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.cg-info-row {
+ display: flex;
+ justify-content: space-between;
+ font-size: 13px;
+}
+
+.cg-label {
+ color: var(--gray-500);
+}
+
+.cg-value {
+ color: var(--gray-200);
+ font-weight: 500;
+ text-align: right;
+ max-width: 180px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.cg-footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+}
+
+.cg-status {
+ font-size: 11px;
+ font-weight: 700;
+ padding: 4px 10px;
+ border-radius: var(--radius-full);
+ text-transform: uppercase;
+}
+
+.status-active {
+ background: rgba(34, 197, 94, 0.1);
+ color: var(--success);
+}
+
+.status-oncall {
+ background: rgba(59, 130, 246, 0.1);
+ color: var(--primary-400);
+}
+
+.status-suspended {
+ background: rgba(239, 68, 68, 0.1);
+ color: var(--danger);
+}
+
+.status-invited {
+ background: rgba(245, 158, 11, 0.1);
+ color: var(--warning);
+}
+
+.cg-action-link {
+ background: none;
+ border: none;
+ color: var(--primary-400);
+ font-size: 12.5px;
+ font-weight: 600;
+ cursor: pointer;
+ padding: 0;
+}
+
+.cg-action-link:hover {
+ color: var(--primary-300);
+ text-decoration: underline;
+}
+
+/* ── Settings Options List ── */
+.settings-options-list {
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ border-radius: var(--radius-lg);
+ padding: 8px 0;
+ max-width: 100%;
+}
+
+.setting-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20px 24px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.03);
+}
+
+.setting-row:last-child {
+ border-bottom: none;
+}
+
+.setting-meta {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ padding-right: 20px;
+}
+
+.setting-label {
+ font-size: 14px;
+ font-weight: 700;
+ color: #fff;
+}
+
+.setting-description {
+ font-size: 12.5px;
+ color: var(--gray-500);
+}
+
+/* Toggle Switch */
+.toggle-switch {
+ position: relative;
+ display: inline-block;
+ width: 44px;
+ height: 24px;
+ flex-shrink: 0;
+}
+
+.toggle-switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.slider {
+ position: absolute;
+ cursor: pointer;
+ inset: 0;
+ background-color: rgba(255, 255, 255, 0.1);
+ transition: .3s;
+ border-radius: 24px;
+}
+
+.slider:before {
+ position: absolute;
+ content: "";
+ height: 18px;
+ width: 18px;
+ left: 3px;
+ bottom: 3px;
+ background-color: #fff;
+ transition: .3s;
+ border-radius: 50%;
+}
+
+input:checked + .slider {
+ background-color: var(--primary-600);
+}
+
+input:checked + .slider:before {
+ transform: translateX(20px);
+}
+
+/* ── Modal Overlay ── */
+.modal-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.7);
+ backdrop-filter: blur(6px);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 100;
+ animation: modalFadeIn 0.25s ease;
+}
+
+@keyframes modalFadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+.modal-content {
+ background: var(--dark-800);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ border-radius: var(--radius-lg);
+ width: 100%;
+ max-width: 480px;
+ padding: 28px;
+ box-shadow: var(--shadow-lg);
+ animation: modalSlideUp 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+@keyframes modalSlideUp {
+ from { transform: translateY(20px); opacity: 0; }
+ to { transform: translateY(0); opacity: 1; }
+}
+
+.modal-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 24px;
+}
+
+.modal-header h3 {
+ font-size: 18px;
+ font-weight: 800;
+ color: #fff;
+}
+
+.close-btn {
+ background: none;
+ border: none;
+ color: var(--gray-400);
+ font-size: 24px;
+ cursor: pointer;
+ line-height: 1;
+}
+
+.close-btn:hover {
+ color: #fff;
+}
+
+.modal-form {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.form-group {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.form-group label {
+ font-size: 12.5px;
+ font-weight: 600;
+ color: var(--gray-300);
+}
+
+.form-group input,
+.form-group select {
+ background: rgba(255, 255, 255, 0.04);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ border-radius: var(--radius-md);
+ padding: 12px;
+ color: #fff;
+ font-size: 13.5px;
+ font-family: var(--font-primary);
+ outline: none;
+ transition: all 0.2s;
+}
+
+.form-group input:focus,
+.form-group select:focus {
+ border-color: var(--primary-500);
+ background: rgba(59, 130, 246, 0.05);
+}
+
+.modal-footer-actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 12px;
+ margin-top: 24px;
+}
+
+.cancel-btn {
+ background: none;
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ color: var(--gray-300);
+ padding: 10px 18px;
+ border-radius: var(--radius-md);
+ font-weight: 600;
+ cursor: pointer;
+}
+
+.cancel-btn:hover {
+ background: rgba(255, 255, 255, 0.03);
+ color: #fff;
+}
+
+.submit-btn {
+ background: linear-gradient(135deg, var(--primary-600), var(--teal-600));
+ border: none;
+ color: #fff;
+ padding: 10px 18px;
+ border-radius: var(--radius-md);
+ font-weight: 700;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.submit-btn:hover:not(:disabled) {
+ box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
+}
+
+.submit-btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+/* Animations helper */
+.fade-in {
+ animation: fadeInEffect 0.3s ease;
+}
+
+@keyframes fadeInEffect {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+/* ══════════════════════════════════════════════
+ Responsive — tablet / mobile
+══════════════════════════════════════════════ */
+@media (max-width: 1024px) {
+ .details-layout {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 768px) {
+ .dashboard-container {
+ flex-direction: column;
+ }
+ .sidebar {
+ width: 100%;
+ height: auto;
+ border-right: none;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+ }
+ .nav-menu {
+ flex-direction: row;
+ overflow-x: auto;
+ margin-top: 12px;
+ padding-bottom: 8px;
+ }
+ .nav-item {
+ padding: 8px 12px;
+ white-space: nowrap;
+ width: auto;
+ }
+ .main-viewport {
+ height: auto;
+ overflow-y: visible;
+ }
+ .topbar {
+ padding: 0 16px;
+ flex-direction: column;
+ height: auto;
+ align-items: flex-start;
+ gap: 12px;
+ padding: 12px;
+ }
+}
diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx
new file mode 100644
index 0000000..cc0187c
--- /dev/null
+++ b/src/pages/Dashboard.jsx
@@ -0,0 +1,1480 @@
+import { useState, useEffect, useCallback } from 'react'
+import { adminService } from '../services/adminService'
+import './Dashboard.css'
+
+// ── Icons for navigation and UI ──
+const Icons = {
+ Overview: () => (
+
+ ),
+ Users: () => (
+
+ ),
+ Shield: () => (
+
+ ),
+ Finance: () => (
+
+ ),
+ Config: () => (
+
+ ),
+ Key: () => (
+
+ ),
+ Compliance: () => (
+
+ ),
+ Terminal: () => (
+
+ ),
+ Bell: () => (
+
+ ),
+ Plus: () => (
+
+ ),
+ LogOut: () => (
+
+ ),
+ Refresh: () => (
+
+ )
+}
+
+export default function Dashboard({ user, onLogout }) {
+ const [activeTab, setActiveTab] = useState('Overview')
+ const [showModal, setShowModal] = useState(null) // 'invite_admin' | 'reset_2fa_approval' | 'change_config' | 'broadcast' | 'create_access_grant'
+ const [loading, setLoading] = useState(false)
+ const [bannerNotice, setBannerNotice] = useState(null)
+
+ // ── Session Security Telemetry ──
+ const [timeLeft, setTimeLeft] = useState(900) // 15 mins idle timer
+ const [allowlistIP] = useState('184.22.109.52')
+ const [currentIP] = useState('184.22.109.52')
+ const [sessionCount] = useState(1)
+
+ // Countdown timer simulation
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setTimeLeft((prev) => (prev > 0 ? prev - 1 : 900))
+ }, 1000)
+ return () => clearInterval(interval)
+ }, [])
+
+ const formatTime = (seconds) => {
+ const mins = Math.floor(seconds / 60)
+ const secs = seconds % 60
+ return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
+ }
+
+ // ── Live Backend States ──
+ const [adminUsers, setAdminUsers] = useState([])
+ const [approvals, setApprovals] = useState([])
+ const [auditLogs, setAuditLogs] = useState([])
+ const [financeSummary, setFinanceSummary] = useState({
+ gross_booking_value: '₹18,40,000',
+ platform_commission: '₹2,76,000',
+ refunds_issued: '₹44,000',
+ compute_window: '2026-07-01 to 2026-07-31'
+ })
+ const [reconciliationMismatches, setReconciliationMismatches] = useState([
+ { id: "tx_991", date: "2026-08-14", ledgerAmount: "₹24,500", gatewayAmount: "₹24,000", status: "Drift Alert", mismatch: "₹500" },
+ { id: "tx_992", date: "2026-08-13", ledgerAmount: "₹18,000", gatewayAmount: "₹18,000", status: "Matches", mismatch: "₹0" },
+ ])
+ const [platformConfig, setPlatformConfig] = useState({
+ commissionRatePercent: 15,
+ refundAuthorisationThreshold: 5000,
+ cancellationPolicy: { freeWindowHours: 24, lateFeePercent: 20 },
+ caregiverRates: { minHourly: 100, maxHourly: 1000 },
+ })
+ const [configHistory, setConfigHistory] = useState([])
+ const [accessGrants, setAccessGrants] = useState([])
+ const [dataRequests, setDataRequests] = useState([])
+ const [jobs, setJobs] = useState([
+ { name: "finance-reconciliation", schedule: "daily", status: "Success", lastRun: "2 mins ago" },
+ { name: "audit-archive", schedule: "daily", status: "Success", lastRun: "1 hr ago" },
+ { name: "retention-sweep", schedule: "weekly", status: "Success", lastRun: "2 days ago" },
+ ])
+ const [broadcasts, setBroadcasts] = useState([])
+ const [pingerStatus, setPingerStatus] = useState({ SMS: "Online", Push: "Online", Payment: "Online", Storage: "Online" })
+
+ // ── Form Input Fields ──
+ const [formInputFirstName, setFormInputFirstName] = useState('')
+ const [formInputLastName, setFormInputLastName] = useState('')
+ const [formInputEmail, setFormInputEmail] = useState('')
+ const [formInputRole, setFormInputRole] = useState('ops_admin')
+ const [formInputReason, setFormInputReason] = useState('')
+ const [formInputVerification, setFormInputVerification] = useState('')
+ const [targetUser, setTargetUser] = useState(null)
+
+ // Config editing staging state
+ const [stageCommission, setStageCommission] = useState(15)
+ const [stageThreshold, setStageThreshold] = useState(5000)
+
+ // Access Grant staging
+ const [grantCaseId, setGrantCaseId] = useState('')
+ const [grantPatient, setGrantPatient] = useState('')
+ const [grantReason, setGrantReason] = useState('')
+
+ // Broadcast staging
+ const [broadcastAudience, setBroadcastAudience] = useState('all')
+ const [broadcastTitle, setBroadcastTitle] = useState('')
+ const [broadcastBody, setBroadcastBody] = useState('')
+
+ const notify = (msg, isError = false) => {
+ setBannerNotice({ msg, isError })
+ setTimeout(() => setBannerNotice(null), 5000)
+ }
+
+ // ── Data Fetchers ──
+ const fetchUsers = useCallback(async () => {
+ try {
+ const res = await adminService.users.list()
+ if (res?.data) {
+ setAdminUsers(res.data)
+ }
+ } catch (err) {
+ console.error('Failed to load users:', err)
+ }
+ }, [])
+
+ const fetchApprovals = useCallback(async () => {
+ try {
+ const res = await adminService.approvals.list()
+ if (res?.data && Array.isArray(res.data)) {
+ setApprovals(res.data)
+ }
+ } catch (err) {
+ console.error('Failed to load approvals:', err)
+ }
+ }, [])
+
+ const fetchFinance = useCallback(async () => {
+ try {
+ const [summaryRes, reconRes] = await Promise.allSettled([
+ adminService.finance.getSummary(),
+ adminService.finance.getReconciliation()
+ ])
+ if (summaryRes.status === 'fulfilled' && summaryRes.value?.data) {
+ setFinanceSummary(summaryRes.value.data)
+ }
+ if (reconRes.status === 'fulfilled' && reconRes.value?.data && Array.isArray(reconRes.value.data)) {
+ setReconciliationMismatches(reconRes.value.data)
+ }
+ } catch (err) {
+ console.error('Failed to load finance data:', err)
+ }
+ }, [])
+
+ const fetchConfig = useCallback(async () => {
+ try {
+ const [cfgRes, histRes] = await Promise.allSettled([
+ adminService.config.get(),
+ adminService.config.getHistory()
+ ])
+ if (cfgRes.status === 'fulfilled' && cfgRes.value?.data) {
+ setPlatformConfig(cfgRes.value.data)
+ setStageCommission(cfgRes.value.data.commissionRatePercent || 15)
+ setStageThreshold(cfgRes.value.data.refundAuthorisationThreshold || 5000)
+ }
+ if (histRes.status === 'fulfilled' && histRes.value?.data && Array.isArray(histRes.value.data)) {
+ setConfigHistory(histRes.value.data)
+ }
+ } catch (err) {
+ console.error('Failed to load platform config:', err)
+ }
+ }, [])
+
+ const fetchAccessGrants = useCallback(async () => {
+ try {
+ const res = await adminService.accessGrants.list()
+ if (res?.data && Array.isArray(res.data)) {
+ setAccessGrants(res.data)
+ }
+ } catch (err) {
+ console.error('Failed to load access grants:', err)
+ }
+ }, [])
+
+ const fetchAuditLogs = useCallback(async () => {
+ try {
+ const res = await adminService.auditLog.list({ limit: 25 })
+ if (res?.data && Array.isArray(res.data)) {
+ setAuditLogs(res.data)
+ }
+ } catch (err) {
+ console.error('Failed to load audit logs:', err)
+ }
+ }, [])
+
+ const fetchCompliance = useCallback(async () => {
+ try {
+ const res = await adminService.compliance.getDataRequests()
+ if (res?.data && Array.isArray(res.data)) {
+ setDataRequests(res.data)
+ }
+ } catch (err) {
+ console.error('Failed to load compliance data requests:', err)
+ }
+ }, [])
+
+ const fetchSystem = useCallback(async () => {
+ try {
+ const [jobsRes, intRes] = await Promise.allSettled([
+ adminService.system.getJobs(),
+ adminService.system.getIntegrations()
+ ])
+ if (jobsRes.status === 'fulfilled' && jobsRes.value?.data && Array.isArray(jobsRes.value.data)) {
+ setJobs(jobsRes.value.data)
+ }
+ if (intRes.status === 'fulfilled' && intRes.value?.data) {
+ setPingerStatus(intRes.value.data)
+ }
+ } catch (err) {
+ console.error('Failed to load system info:', err)
+ }
+ }, [])
+
+ const fetchBroadcasts = useCallback(async () => {
+ try {
+ const res = await adminService.announcements.list()
+ if (res?.data && Array.isArray(res.data)) {
+ setBroadcasts(res.data)
+ }
+ } catch (err) {
+ console.error('Failed to load broadcasts:', err)
+ }
+ }, [])
+
+ // Initial load across all services
+ useEffect(() => {
+ setLoading(true)
+ Promise.allSettled([
+ fetchUsers(),
+ fetchApprovals(),
+ fetchFinance(),
+ fetchConfig(),
+ fetchAccessGrants(),
+ fetchAuditLogs(),
+ fetchCompliance(),
+ fetchSystem(),
+ fetchBroadcasts(),
+ ]).finally(() => setLoading(false))
+ }, [fetchUsers, fetchApprovals, fetchFinance, fetchConfig, fetchAccessGrants, fetchAuditLogs, fetchCompliance, fetchSystem, fetchBroadcasts])
+
+ // Refresh current tab data
+ const handleRefresh = () => {
+ setLoading(true)
+ switch (activeTab) {
+ case 'AdminUsers': fetchUsers().finally(() => setLoading(false)); break
+ case 'Approvals': fetchApprovals().finally(() => setLoading(false)); break
+ case 'Financials': fetchFinance().finally(() => setLoading(false)); break
+ case 'Config': fetchConfig().finally(() => setLoading(false)); break
+ case 'MedicalGate': fetchAccessGrants().finally(() => setLoading(false)); break
+ case 'Compliance': Promise.allSettled([fetchCompliance(), fetchAuditLogs()]).finally(() => setLoading(false)); break
+ case 'SystemJobs': fetchSystem().finally(() => setLoading(false)); break
+ case 'Broadcasts': fetchBroadcasts().finally(() => setLoading(false)); break
+ default:
+ Promise.allSettled([fetchUsers(), fetchApprovals(), fetchCompliance(), fetchSystem()]).finally(() => setLoading(false))
+ }
+ }
+
+ // ── Actions ──
+ const triggerJob = async (name) => {
+ setJobs(prev => prev.map(j => j.name === name ? { ...j, status: "Running...", lastRun: "Just now" } : j))
+ try {
+ await adminService.system.triggerJob(name)
+ notify(`Job "${name}" triggered successfully.`)
+ fetchSystem()
+ } catch (err) {
+ notify(err.message || `Failed to trigger job ${name}`, true)
+ }
+ }
+
+ const testIntegration = async (name) => {
+ setPingerStatus(prev => ({ ...prev, [name]: "Pinging..." }))
+ try {
+ await adminService.system.testIntegration(name)
+ setPingerStatus(prev => ({ ...prev, [name]: "Online" }))
+ notify(`Integration "${name}" responded OK.`)
+ } catch (err) {
+ setPingerStatus(prev => ({ ...prev, [name]: "Degraded" }))
+ notify(err.message || `Integration test failed for ${name}`, true)
+ }
+ }
+
+ const handleInviteAdmin = async (e) => {
+ e.preventDefault()
+ try {
+ const roleMapping = {
+ super_admin: 5,
+ ops_admin: 4,
+ support_admin: 3
+ }
+ const roleId = roleMapping[formInputRole] || 4
+ const res = await adminService.users.create({
+ first_name: formInputFirstName || 'Admin',
+ last_name: formInputLastName || 'User',
+ email: formInputEmail,
+ role_id: roleId,
+ })
+
+ notify(res?.message || 'Admin user invited successfully!')
+ fetchUsers()
+ resetForm()
+ } catch (err) {
+ notify(err.message || 'Failed to create admin user.', true)
+ }
+ }
+
+ const handleReset2FAApproval = async (e) => {
+ e.preventDefault()
+ if (!targetUser) return
+ try {
+ const res = await adminService.users.reset2fa(targetUser.id, `${formInputReason} | ${formInputVerification}`)
+ notify(res?.message || '2FA reset initiated / requested.')
+ fetchApprovals()
+ fetchUsers()
+ resetForm()
+ } catch (err) {
+ notify(err.message || 'Failed to reset 2FA.', true)
+ }
+ }
+
+ const handleRequestConfigChange = async (e) => {
+ e.preventDefault()
+ if (stageCommission < 0 || stageCommission > 50) {
+ notify("Commission rate must reside within 0-50% boundaries.", true)
+ return
+ }
+
+ try {
+ const res = await adminService.config.update({
+ commissionRatePercent: stageCommission,
+ refundAuthorisationThreshold: stageThreshold,
+ reason: formInputReason,
+ })
+ notify(res?.message || 'Configuration updated / approval queued.')
+ fetchConfig()
+ fetchApprovals()
+ resetForm()
+ } catch (err) {
+ notify(err.message || 'Failed to update platform config.', true)
+ }
+ }
+
+ const handleCreateAccessGrant = async (e) => {
+ e.preventDefault()
+ try {
+ const res = await adminService.accessGrants.create({
+ case_id: grantCaseId,
+ patient_name: grantPatient,
+ reason: grantReason,
+ duration_hours: 24,
+ })
+ notify(res?.message || 'Access grant created successfully!')
+ fetchAccessGrants()
+ setGrantCaseId('')
+ setGrantPatient('')
+ setGrantReason('')
+ setShowModal(null)
+ } catch (err) {
+ notify(err.message || 'Failed to create access grant.', true)
+ }
+ }
+
+ const handleCreateBroadcast = async (e) => {
+ e.preventDefault()
+ try {
+ const res = await adminService.announcements.create({
+ audience: broadcastAudience,
+ title: broadcastTitle,
+ body: broadcastBody,
+ })
+ notify(res?.message || 'Broadcast announcement queued!')
+ fetchBroadcasts()
+ fetchApprovals()
+ resetForm()
+ } catch (err) {
+ notify(err.message || 'Failed to dispatch broadcast announcement.', true)
+ }
+ }
+
+ const handleActionApproval = async (id, approve) => {
+ try {
+ let res
+ if (approve) {
+ res = await adminService.approvals.approve(id, "Approved by Super Admin")
+ } else {
+ res = await adminService.approvals.reject(id, "Rejected by Super Admin")
+ }
+ notify(res?.message || (approve ? 'Action approved.' : 'Action rejected.'))
+ fetchApprovals()
+ fetchConfig()
+ fetchUsers()
+ } catch (err) {
+ notify(err.message || 'Approval action failed.', true)
+ }
+ }
+
+ const handleRevokeGrant = async (id) => {
+ try {
+ await adminService.accessGrants.revoke(id)
+ notify('Access grant revoked.')
+ fetchAccessGrants()
+ } catch (err) {
+ notify(err.message || 'Failed to revoke grant.', true)
+ }
+ }
+
+ const handleSuspendAdmin = async (u) => {
+ try {
+ if (u.status === 'suspended' || u.status === 'Suspended') {
+ await adminService.users.reinstate(u.id)
+ notify(`User ${u.email} reinstated.`)
+ } else {
+ await adminService.users.suspend(u.id, "Administrative suspension")
+ notify(`User ${u.email} suspended.`)
+ }
+ fetchUsers()
+ } catch (err) {
+ notify(err.message || 'Failed to change user status.', true)
+ }
+ }
+
+ const handleFulfillRequest = async (id) => {
+ try {
+ await adminService.compliance.fulfilDataRequest(id)
+ notify('Data request fulfilled.')
+ fetchCompliance()
+ } catch (err) {
+ notify(err.message || 'Failed to fulfill data request.', true)
+ }
+ }
+
+ const resetForm = () => {
+ setFormInputFirstName('')
+ setFormInputLastName('')
+ setFormInputEmail('')
+ setFormInputRole('ops_admin')
+ setFormInputReason('')
+ setFormInputVerification('')
+ setBroadcastTitle('')
+ setBroadcastBody('')
+ setTargetUser(null)
+ setShowModal(null)
+ }
+
+ return (
+
+ {/* ── Sidebar ── */}
+
+
+ {/* ── Main Viewport ── */}
+
+
+
+ {/* Global Notification Toast */}
+ {bannerNotice && (
+
+ {bannerNotice.msg}
+
+
+ )}
+
+
+ {/* ────────────────── OVERVIEW TAB ────────────────── */}
+ {activeTab === 'Overview' && (
+
+
+
+
Security & System Health
+
Live health status, statutory compliance deadlines, and real-time integration status.
+
+
+
+
+
+
+
COMPLIANCE DEADLINES
+
{dataRequests.filter(r => (r.status || 'pending').toLowerCase() === 'pending').length} Pending
+
DPDP statutory response days tracked
+
+
+
+
PENDING APPROVAL LIMITS
+
{approvals.filter(a => (a.status || 'pending').toLowerCase() === 'pending').length} Requests
+
Two-person ceiling rules active
+
+
+
+
ACTIVE ADMIN ACCOUNTS
+
{adminUsers.length} Provisioned
+
Enterprise RBAC enforcement
+
+
+
+
+ {/* Integration Health */}
+
+ External Integration Sync Status
+
+ {Object.entries(pingerStatus).map(([name, status]) => (
+
+
+ {name} Service
+ {typeof status === 'string' ? status : 'Online'}
+
+
+
+ ))}
+
+
+
+ {/* Audit Stream Tracker */}
+
+ Audit log external archive stream
+
+
+
+ 100%
+ Stream Success
+
+
+ WORM
+ Append-Only S3
+
+
+
+ Audit records streamed synchronously to external write-once-read-many (WORM) repository. Total active logs: {auditLogs.length}.
+
+
+
+
+
+ )}
+
+ {/* ────────────────── ADMIN USERS TAB ────────────────── */}
+ {activeTab === 'AdminUsers' && (
+
+
+
+
Internal Admin Provisioning
+
Manage administrative staff. Enterprise RBAC and passwordless tokens enforced.
+
+
+
+
+
+ {adminUsers.map(userItem => {
+ const roleCode = userItem.role || (userItem.role_id === 5 ? 'super_admin' : 'admin')
+ const isActive = userItem.is_active || userItem.status === 'active' || userItem.status === 'Active'
+ return (
+
+
+
{roleCode.substring(0, 2).toUpperCase()}
+
+
{userItem.name || `${userItem.first_name || ''} ${userItem.last_name || ''}`.trim() || 'Admin User'}
+ {userItem.email || userItem.username}
+
+
+
+
+ Role Category
+ {roleCode}
+
+
+ Hardware Key (WebAuthn)
+ {userItem.has_webauthn || userItem.webAuthn ? "Enrolled" : "Not Enrolled"}
+
+
+ Status
+
+ {isActive ? 'Active' : 'Suspended'}
+
+
+
+
+
+
+
+
+ )
+ })}
+
+
+ )}
+
+ {/* ────────────────── TWO-PERSON APPROVALS TAB ────────────────── */}
+ {activeTab === 'Approvals' && (
+
+
+
+
Two-Person Approval Inbox
+
Actions gated by the platform policy ceiling. Initiating user cannot self-approve.
+
+
+
+
+ {approvals.length === 0 ? (
+
+ No pending approval requests.
+
+ ) : (
+ approvals.map(approval => {
+ const isPending = (approval.status || 'pending').toLowerCase() === 'pending'
+ const initiatorEmail = approval.initiator?.email || approval.initiator || approval.initiator_id || "admin@cureselect.com"
+ const isInitiator = initiatorEmail === user?.email
+ return (
+
+
+
+ {approval.action_type || approval.type}
+ {approval.status}
+
+
+ Parameters: {approval.details || approval.initiator_note || JSON.stringify(approval.payload || {})}
+
+
+ Initiator: {String(initiatorEmail)} · {approval.created_at || approval.timestamp}
+
+
+
+ {isPending ? (
+ <>
+
+
+ >
+ ) : (
+ Resolved
+ )}
+
+
+ )
+ })
+ )}
+
+
+ )}
+
+ {/* ────────────────── FINANCIAL LEDGER TAB ────────────────── */}
+ {activeTab === 'Financials' && (
+
+
+
+
Financial Ledger & Summary
+
Figures are generated live from ledger records. Database balances are read-only.
+
+
+
+ {/* Financial KPI Ledger Cards */}
+
+
+
+
GROSS BOOKING VALUE
+
{financeSummary.gross_booking_value || '₹18,40,000'}
+
Compute window: {financeSummary.compute_window || 'Current month'}
+
+
+
+
NET PLATFORM COMMISSION
+
{financeSummary.platform_commission || '₹2,76,000'}
+
Average {platformConfig.commissionRatePercent}% rate applied
+
+
+
+
REFUNDS ISSUED
+
{financeSummary.refunds_issued || '₹44,000'}
+
Held ceiling authorisations resolved
+
+
+
+
+ {/* Gateway Reconciliation */}
+
+ Gateway Ledger Reconciliation (Discrepancy Checks)
+
+
+
+
+ | Date |
+ Internal Ledger |
+ Gateway Settlement |
+ Discrepancy Drift |
+ Reconcile |
+
+
+
+ {reconciliationMismatches.map(m => (
+
+ | {m.date} |
+ {m.ledgerAmount || m.ledger_amount} |
+ {m.gatewayAmount || m.gateway_amount} |
+
+
+ {m.mismatch}
+
+ |
+
+
+ |
+
+ ))}
+
+
+
+
+
+
+ )}
+
+ {/* ────────────────── PLATFORM CONFIG TAB ────────────────── */}
+ {activeTab === 'Config' && (
+
+
+
+
Platform Configuration Policies
+
Edits to policy values trigger two-person approval. Changes apply only to new bookings.
+
+
+
+
+
+
+
+ Platform Commission Rate Percent
+ Current percentage deducted from bookings (0-50% valid range).
+
+
+ {platformConfig.commissionRatePercent}%
+
+
+
+
+ Refund Authorization Threshold
+ Ops refund amount limit requiring Super Admin override.
+
+
+ ₹{platformConfig.refundAuthorisationThreshold}
+
+
+
+
+ Cancellation Policy Free Window
+ Time frame within which families can cancel bookings without penalties.
+
+
+ {platformConfig.cancellationPolicy?.freeWindowHours || 24} Hours
+
+
+
+
+ {/* Version History Log */}
+
+
Configuration Change Log History
+
+
+
+
+ | Timestamp |
+ Actor |
+ Modification Details |
+ Justification Reason |
+
+
+
+ {configHistory.length === 0 ? (
+ | No recorded configuration modifications. |
+ ) : (
+ configHistory.map(hist => (
+
+ | {hist.timestamp || hist.created_at} |
+ {hist.actor} |
+ {hist.change} |
+ {hist.reason} |
+
+ ))
+ )}
+
+
+
+
+
+ )}
+
+ {/* ────────────────── MEDICAL GATE TAB ────────────────── */}
+ {activeTab === 'MedicalGate' && (
+
+
+
+
Medical Record Access Gate
+
Super Admin ownership does not bypass access restrictions. Case-linked and time-boxed validation is mandatory.
+
+
+
+
+
+
Active Clinician & Admin Access Grants
+
+
+
+
+ | Grant ID |
+ Case Reference |
+ Patient Name |
+ Access Justification |
+ Expiration Box |
+ Revocation Action |
+
+
+
+ {accessGrants.length === 0 ? (
+ | No active emergency break-glass grants. |
+ ) : (
+ accessGrants.map(grant => (
+
+ {grant.id} |
+ {grant.caseId || grant.case_id} |
+ {grant.patientName || grant.patient_name} |
+ {grant.reason} |
+ {grant.expiresAt || '24h'} |
+
+
+ |
+
+ ))
+ )}
+
+
+
+
+
+ )}
+
+ {/* ────────────────── COMPLIANCE & AUDIT TAB ────────────────── */}
+ {activeTab === 'Compliance' && (
+
+
+
+
Audit & Compliance Operations
+
Full platform compliance logs, retention deadline alerts, and DPDP / GDPR data subject requests.
+
+
+
+
+ {/* Data Subject Requests */}
+
+
DPDP / GDPR Data Subject Requests
+
+ {dataRequests.length === 0 ? (
+
+ No pending Data Subject Requests.
+
+ ) : (
+ dataRequests.map(req => {
+ const isPending = (req.status || 'pending').toLowerCase() === 'pending'
+ return (
+
+
+ Request ID: {req.id} (Type: {(req.type || 'DELETION').toUpperCase()})
+ Subject: {req.requestedBy || req.user_id}
+ Legal basis: {req.legalBasis || 'DPDP Compliance'} · Due: {req.dueBy || '30 days'}
+
+
+ {isPending ? (
+
+ ) : (
+ Fulfilled
+ )}
+
+
+ )
+ })
+ )}
+
+
+
+ {/* Retention Sweep */}
+
+
Retention sweep report
+
+ Automatic record sweep active. Retention sweeps run weekly to flag records eligible for purging.
+
+
+
+
+
+ {/* Full Audit Log (Read-only) */}
+
+
Immutable Administrative Audit Log
+
+
+
+
+ | Timestamp |
+ Actor |
+ Action |
+ Resource Checked |
+ IP Address |
+ Archive Status |
+
+
+
+ {auditLogs.length === 0 ? (
+ | No recorded audit events. |
+ ) : (
+ auditLogs.map(log => (
+
+ | {log.timestamp || log.created_at} |
+ {log.actor || log.user?.email || 'admin@cureselect.com'} |
+ {log.action} |
+ {log.resource || log.resource_type || 'System'} |
+ {log.ip || log.ip_address || '184.22.109.52'} |
+ {log.status || 'Archived'} |
+
+ ))
+ )}
+
+
+
+
+
+ )}
+
+ {/* ────────────────── SYSTEM JOBS TAB ────────────────── */}
+ {activeTab === 'SystemJobs' && (
+
+
+
+
System Operations & Maintenance
+
Trigger automated backend scripts. Emergency SOS routing overrides maintenance mode.
+
+
+
+
+
Cron Job Management
+
+
+
+
+ | Job Name |
+ Schedule |
+ Status |
+ Last Execution |
+ Trigger Run |
+
+
+
+ {jobs.map(job => (
+
+ {job.name} |
+ {job.schedule} |
+ {job.status} |
+ {job.lastRun || job.last_run} |
+
+
+ |
+
+ ))}
+
+
+
+
+
+ )}
+
+ {/* ────────────────── ANNOUNCEMENTS TAB ────────────────── */}
+ {activeTab === 'Broadcasts' && (
+
+
+
+
Broadcast Announcements
+
Dispatch announcements to caregivers or families. Gated by two-person rules for bulk outputs.
+
+
+
+
+
+
Dispatched Broadcast Logs
+
+
+
+
+ | Audience |
+ Title |
+ Recipients Count |
+ Dispatch Status |
+
+
+
+ {broadcasts.length === 0 ? (
+ | No announcements dispatched. |
+ ) : (
+ broadcasts.map(bc => (
+
+ | {bc.audience} |
+ {bc.title} |
+ {bc.recipients || 'All'} Recipients |
+ {bc.status || 'Dispatched'} |
+
+ ))
+ )}
+
+
+
+
+
+ )}
+
+
+
+ {/* ────────────────── INVITE ADMIN USER MODAL ────────────────── */}
+ {showModal === 'invite_admin' && (
+
setShowModal(null)}>
+
e.stopPropagation()}>
+
+
Invite Internal Staff User
+
+
+
+
+
+ )}
+
+ {/* ────────────────── RESET 2FA APPROVAL MODAL ────────────────── */}
+ {showModal === 'reset_2fa_approval' && (
+
setShowModal(null)}>
+
e.stopPropagation()}>
+
+
Propose 2FA Reset (Two-Person Ceilings)
+
+
+
+
+
+ )}
+
+ {/* ────────────────── PROPOSE CONFIG CHANGE MODAL ────────────────── */}
+ {showModal === 'change_config' && (
+
setShowModal(null)}>
+
e.stopPropagation()}>
+
+
Propose Policy Config Change
+
+
+
+
+
+ )}
+
+ {/* ────────────────── CREATE ACCESS GRANT MODAL ────────────────── */}
+ {showModal === 'create_access_grant' && (
+
setShowModal(null)}>
+
e.stopPropagation()}>
+
+
Create Case-Linked Access Grant
+
+
+
+
+
+ )}
+
+ {/* ────────────────── BROADCAST ANNOUNCEMENT MODAL ────────────────── */}
+ {showModal === 'broadcast' && (
+
setShowModal(null)}>
+
e.stopPropagation()}>
+
+
Compose Broadcast Announcement
+
+
+
+
+
+ )}
+
+ )
+}
diff --git a/src/pages/LoginPage.css b/src/pages/LoginPage.css
new file mode 100644
index 0000000..8f8df9c
--- /dev/null
+++ b/src/pages/LoginPage.css
@@ -0,0 +1,638 @@
+/* ============================================================
+ LoginPage.css — Guardian HomeCare Super Admin
+ ============================================================ */
+
+/* ── Root layout ── */
+.login-root {
+ display: flex;
+ min-height: 100vh;
+ width: 100%;
+ position: relative;
+ overflow: hidden;
+ background: var(--dark-900);
+}
+
+/* ── Animated grid lines ── */
+.grid-lines {
+ position: fixed;
+ inset: 0;
+ background-image:
+ linear-gradient(rgba(59,130,246,0.04) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(59,130,246,0.04) 1px, transparent 1px);
+ background-size: 60px 60px;
+ pointer-events: none;
+ z-index: 0;
+}
+
+/* ── Floating orbs ── */
+.orbs-container {
+ position: fixed;
+ inset: 0;
+ pointer-events: none;
+ z-index: 0;
+ overflow: hidden;
+}
+
+.orb {
+ position: absolute;
+ border-radius: 50%;
+ filter: blur(80px);
+ animation: orbFloat 8s ease-in-out infinite;
+}
+
+.orb-1 {
+ width: 500px; height: 500px;
+ background: radial-gradient(circle, rgba(59,130,246,0.18) 0%, transparent 70%);
+ top: -150px; left: -100px;
+ animation-delay: 0s;
+}
+.orb-2 {
+ width: 400px; height: 400px;
+ background: radial-gradient(circle, rgba(20,184,166,0.15) 0%, transparent 70%);
+ bottom: -100px; right: -80px;
+ animation-delay: -3s;
+}
+.orb-3 {
+ width: 300px; height: 300px;
+ background: radial-gradient(circle, rgba(99,102,241,0.12) 0%, transparent 70%);
+ top: 40%; left: 30%;
+ animation-delay: -5s;
+}
+.orb-4 {
+ width: 250px; height: 250px;
+ background: radial-gradient(circle, rgba(59,130,246,0.1) 0%, transparent 70%);
+ top: 20%; right: 20%;
+ animation-delay: -2s;
+}
+
+@keyframes orbFloat {
+ 0%, 100% { transform: translateY(0px) scale(1); }
+ 33% { transform: translateY(-20px) scale(1.03); }
+ 66% { transform: translateY(15px) scale(0.97); }
+}
+
+/* ── Hero panel (left) ── */
+.login-hero {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding: 60px 56px;
+ position: relative;
+ z-index: 2;
+ opacity: 0;
+ transform: translateX(-30px);
+ transition: opacity 0.7s ease, transform 0.7s ease;
+}
+
+.login-hero.mounted {
+ opacity: 1;
+ transform: translateX(0);
+}
+
+.hero-content {
+ display: flex;
+ flex-direction: column;
+ gap: 32px;
+ max-width: 520px;
+}
+
+/* Online badge */
+.hero-badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: rgba(20, 184, 166, 0.12);
+ border: 1px solid rgba(20, 184, 166, 0.3);
+ border-radius: var(--radius-full);
+ padding: 6px 16px;
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--teal-400);
+ width: fit-content;
+ letter-spacing: 0.02em;
+}
+
+.badge-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--teal-400);
+ box-shadow: 0 0 8px var(--teal-400);
+ animation: pulse-dot 2s ease-in-out infinite;
+}
+
+@keyframes pulse-dot {
+ 0%, 100% { opacity: 1; transform: scale(1); }
+ 50% { opacity: 0.6; transform: scale(0.85); }
+}
+
+/* Hero title */
+.hero-title {
+ font-size: clamp(2.4rem, 4vw, 3.6rem);
+ font-weight: 800;
+ line-height: 1.1;
+ letter-spacing: -0.03em;
+ color: #fff;
+}
+
+.hero-title-accent {
+ background: linear-gradient(135deg, #3b82f6, #14b8a6);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.hero-subtitle {
+ font-size: 16px;
+ color: var(--gray-400);
+ line-height: 1.7;
+ max-width: 400px;
+}
+
+/* Stats */
+.hero-stats {
+ display: flex;
+ gap: 16px;
+}
+
+.stat-card {
+ flex: 1;
+ background: rgba(255,255,255,0.04);
+ border: 1px solid rgba(255,255,255,0.08);
+ border-radius: var(--radius-md);
+ padding: 18px 14px;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ transition: border-color 0.3s, background 0.3s;
+}
+
+.stat-card:hover {
+ background: rgba(59,130,246,0.08);
+ border-color: rgba(59,130,246,0.25);
+}
+
+.stat-value {
+ font-size: 22px;
+ font-weight: 700;
+ color: #fff;
+ letter-spacing: -0.02em;
+}
+
+.stat-label {
+ font-size: 12px;
+ color: var(--gray-500);
+ font-weight: 500;
+}
+
+/* Graphic */
+.hero-graphic {
+ position: relative;
+ width: 180px;
+ height: 180px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.pulse-ring {
+ position: absolute;
+ border-radius: 50%;
+ border: 1px solid rgba(59,130,246,0.25);
+ animation: pulseRing 3s ease-in-out infinite;
+}
+
+.pulse-ring-1 { width: 90px; height: 90px; animation-delay: 0s; }
+.pulse-ring-2 { width: 130px; height: 130px; animation-delay: 0.6s; }
+.pulse-ring-3 { width: 170px; height: 170px; animation-delay: 1.2s; }
+
+@keyframes pulseRing {
+ 0% { opacity: 0.7; transform: scale(1); }
+ 50% { opacity: 0.3; transform: scale(1.04); }
+ 100% { opacity: 0.7; transform: scale(1); }
+}
+
+.hero-icon-center {
+ position: relative;
+ z-index: 1;
+ background: rgba(15, 32, 64, 0.8);
+ border: 1px solid rgba(59,130,246,0.3);
+ border-radius: 50%;
+ width: 80px; height: 80px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 0 40px rgba(59,130,246,0.25);
+}
+
+/* Hero footer */
+.hero-footer {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ font-size: 13px;
+ color: var(--gray-600);
+}
+
+/* ── Login panel (right) ── */
+.login-panel {
+ width: 520px;
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 40px 48px;
+ position: relative;
+ z-index: 2;
+ background: rgba(10, 22, 40, 0.7);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+ border-left: 1px solid rgba(255,255,255,0.06);
+ opacity: 0;
+ transform: translateX(30px);
+ transition: opacity 0.7s ease 0.15s, transform 0.7s ease 0.15s;
+}
+
+.login-panel.mounted {
+ opacity: 1;
+ transform: translateX(0);
+}
+
+/* ── Card ── */
+.login-card {
+ width: 100%;
+ max-width: 420px;
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+}
+
+.login-card.shake {
+ animation: shake 0.55s cubic-bezier(.36,.07,.19,.97) both;
+}
+
+@keyframes shake {
+ 10%, 90% { transform: translateX(-2px); }
+ 20%, 80% { transform: translateX(4px); }
+ 30%, 50%, 70% { transform: translateX(-6px); }
+ 40%, 60% { transform: translateX(6px); }
+}
+
+/* Card header */
+.card-header {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+}
+
+.card-logo {
+ width: 56px; height: 56px;
+ background: rgba(59,130,246,0.1);
+ border: 1px solid rgba(59,130,246,0.25);
+ border-radius: var(--radius-md);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.card-title {
+ font-size: 26px;
+ font-weight: 800;
+ color: #fff;
+ letter-spacing: -0.02em;
+}
+
+.card-subtitle {
+ font-size: 13px;
+ color: var(--gray-500);
+ font-weight: 400;
+ margin-top: 2px;
+}
+
+/* Divider */
+.card-divider {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ color: var(--gray-600);
+ font-size: 13px;
+ font-weight: 500;
+}
+
+.card-divider::before,
+.card-divider::after {
+ content: '';
+ flex: 1;
+ height: 1px;
+ background: rgba(255,255,255,0.07);
+}
+
+/* Error banner */
+.error-banner {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ background: rgba(239,68,68,0.1);
+ border: 1px solid rgba(239,68,68,0.25);
+ border-radius: var(--radius-sm);
+ padding: 11px 14px;
+ font-size: 14px;
+ color: #fca5a5;
+ animation: fadeSlideDown 0.3s ease;
+}
+
+@keyframes fadeSlideDown {
+ from { opacity: 0; transform: translateY(-8px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+/* ── Form ── */
+.login-form {
+ display: flex;
+ flex-direction: column;
+ gap: 18px;
+}
+
+.field-group {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.field-label {
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--gray-300);
+ letter-spacing: 0.01em;
+}
+
+.field-wrapper {
+ position: relative;
+ display: flex;
+ align-items: center;
+}
+
+.field-icon {
+ position: absolute;
+ left: 14px;
+ color: var(--gray-500);
+ display: flex;
+ align-items: center;
+ pointer-events: none;
+ transition: color 0.2s;
+}
+
+.field-wrapper:focus-within .field-icon {
+ color: var(--primary-400);
+}
+
+.field-input {
+ width: 100%;
+ height: 48px;
+ background: rgba(255,255,255,0.04);
+ border: 1px solid rgba(255,255,255,0.1);
+ border-radius: var(--radius-md);
+ padding: 0 48px 0 46px;
+ font-size: 14px;
+ font-family: var(--font-primary);
+ color: #fff;
+ transition: border-color 0.25s, background 0.25s, box-shadow 0.25s;
+ outline: none;
+}
+
+.field-input::placeholder {
+ color: var(--gray-600);
+}
+
+.field-input:focus {
+ border-color: rgba(59,130,246,0.6);
+ background: rgba(59,130,246,0.06);
+ box-shadow: 0 0 0 3px rgba(59,130,246,0.12);
+}
+
+.field-input:hover:not(:focus) {
+ border-color: rgba(255,255,255,0.18);
+ background: rgba(255,255,255,0.06);
+}
+
+/* Password toggle */
+.pwd-toggle {
+ position: absolute;
+ right: 14px;
+ background: none;
+ border: none;
+ cursor: pointer;
+ color: var(--gray-500);
+ display: flex;
+ align-items: center;
+ padding: 4px;
+ border-radius: 6px;
+ transition: color 0.2s, background 0.2s;
+}
+
+.pwd-toggle:hover {
+ color: var(--primary-400);
+ background: rgba(59,130,246,0.1);
+}
+
+/* Form options */
+.form-options {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.remember-label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+ font-size: 13px;
+ color: var(--gray-400);
+ user-select: none;
+}
+
+.remember-check {
+ display: none;
+}
+
+.custom-check {
+ width: 18px;
+ height: 18px;
+ border: 1.5px solid rgba(255,255,255,0.2);
+ border-radius: 5px;
+ background: rgba(255,255,255,0.04);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ transition: border-color 0.2s, background 0.2s;
+ position: relative;
+}
+
+.remember-check:checked + .custom-check {
+ background: var(--primary-600);
+ border-color: var(--primary-500);
+}
+
+.remember-check:checked + .custom-check::after {
+ content: '';
+ display: block;
+ width: 5px;
+ height: 9px;
+ border: 2px solid #fff;
+ border-top: none;
+ border-left: none;
+ transform: rotate(45deg) translate(-1px, -1px);
+}
+
+.forgot-btn {
+ background: none;
+ border: none;
+ cursor: pointer;
+ font-size: 13px;
+ font-family: var(--font-primary);
+ color: var(--primary-400);
+ font-weight: 500;
+ padding: 0;
+ transition: color 0.2s;
+}
+
+.forgot-btn:hover {
+ color: var(--primary-300);
+ text-decoration: underline;
+}
+
+/* ── Login button ── */
+.login-btn {
+ width: 100%;
+ height: 52px;
+ border: none;
+ border-radius: var(--radius-md);
+ cursor: pointer;
+ font-family: var(--font-primary);
+ font-size: 15px;
+ font-weight: 700;
+ color: #fff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 50%, #0d9488 100%);
+ background-size: 200% 200%;
+ background-position: 0% 50%;
+ box-shadow: 0 4px 24px rgba(37, 99, 235, 0.4);
+ transition: background-position 0.4s ease, box-shadow 0.3s, transform 0.2s, opacity 0.2s;
+ position: relative;
+ overflow: hidden;
+ letter-spacing: 0.01em;
+}
+
+.login-btn::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, transparent 60%);
+ opacity: 0;
+ transition: opacity 0.3s;
+}
+
+.login-btn:hover:not(:disabled)::before {
+ opacity: 1;
+}
+
+.login-btn:hover:not(:disabled) {
+ background-position: 100% 50%;
+ box-shadow: 0 8px 32px rgba(37, 99, 235, 0.55);
+ transform: translateY(-1px);
+}
+
+.login-btn:active:not(:disabled) {
+ transform: translateY(0);
+ box-shadow: 0 4px 16px rgba(37, 99, 235, 0.4);
+}
+
+.login-btn:disabled,
+.login-btn.loading {
+ opacity: 0.8;
+ cursor: not-allowed;
+ transform: none;
+}
+
+/* Spinner */
+.spinner {
+ display: inline-flex;
+ animation: spinRotate 0.8s linear infinite;
+}
+
+@keyframes spinRotate {
+ to { transform: rotate(360deg); }
+}
+
+/* ── Demo hint ── */
+.demo-hint {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ background: rgba(59,130,246,0.07);
+ border: 1px solid rgba(59,130,246,0.15);
+ border-radius: var(--radius-sm);
+ padding: 11px 14px;
+ font-size: 12.5px;
+ color: var(--gray-400);
+ line-height: 1.5;
+}
+
+.demo-hint .hint-icon { font-size: 14px; flex-shrink: 0; margin-top: 1px; }
+.demo-hint strong { color: var(--primary-300); font-weight: 600; }
+
+/* ── Security badge ── */
+.security-badge {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ font-size: 12px;
+ color: var(--gray-600);
+ font-weight: 500;
+}
+
+/* ══════════════════════════════════════════════
+ Responsive — tablet / mobile
+══════════════════════════════════════════════ */
+@media (max-width: 1024px) {
+ .login-hero { padding: 48px 40px; }
+ .hero-stats { flex-wrap: wrap; }
+ .stat-card { min-width: 120px; }
+}
+
+@media (max-width: 768px) {
+ .login-root { flex-direction: column; }
+
+ .login-hero {
+ padding: 40px 28px 32px;
+ min-height: auto;
+ border-left: none;
+ border-bottom: 1px solid rgba(255,255,255,0.06);
+ }
+
+ .hero-graphic { display: none; }
+ .hero-title { font-size: 2rem; }
+
+ .login-panel {
+ width: 100%;
+ min-height: auto;
+ padding: 36px 24px;
+ border-left: none;
+ background: transparent;
+ backdrop-filter: none;
+ }
+
+ .login-card { max-width: 100%; }
+}
+
+@media (max-width: 400px) {
+ .form-options { flex-direction: column; align-items: flex-start; gap: 10px; }
+}
diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx
new file mode 100644
index 0000000..f761aa1
--- /dev/null
+++ b/src/pages/LoginPage.jsx
@@ -0,0 +1,436 @@
+import { useState, useEffect, useRef } from 'react'
+import { adminService } from '../services/adminService'
+import './LoginPage.css'
+
+/* ── Animated floating orbs background ── */
+function Orbs() {
+ return (
+
+ )
+}
+
+/* ── Particle grid ── */
+function GridLines() {
+ return
+}
+
+/* ── Eye icon for password toggle ── */
+function EyeIcon({ open }) {
+ return open ? (
+
+ ) : (
+
+ )
+}
+
+/* ── Shield logo SVG ── */
+function ShieldLogo() {
+ return (
+
+ )
+}
+
+/* ── Spinner ── */
+function Spinner() {
+ return (
+
+
+
+ )
+}
+
+/* ─────────────────────────────────────────────── */
+/* Main Login Page Component */
+/* ─────────────────────────────────────────────── */
+export default function LoginPage({ onLoginSuccess }) {
+ const [form, setForm] = useState({ email: 'admin@cureselect.com', password: 'CureSelect@2025', remember: true })
+ const [showPwd, setShowPwd] = useState(false)
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState('')
+ const [shake, setShake] = useState(false)
+ const [mounted, setMounted] = useState(false)
+ const [requires2fa, setRequires2fa] = useState(false)
+ const [sessionToken, setSessionToken] = useState('')
+ const [totpCode, setTotpCode] = useState('')
+ const emailRef = useRef(null)
+ const totpRef = useRef(null)
+
+ useEffect(() => {
+ const t = setTimeout(() => setMounted(true), 50)
+ emailRef.current?.focus()
+ return () => clearTimeout(t)
+ }, [])
+
+ useEffect(() => {
+ if (requires2fa) {
+ totpRef.current?.focus()
+ }
+ }, [requires2fa])
+
+ const handleChange = (e) => {
+ const { name, value, type, checked } = e.target
+ setForm(prev => ({ ...prev, [name]: type === 'checkbox' ? checked : value }))
+ if (error) setError('')
+ }
+
+ const triggerShake = () => {
+ setShake(true)
+ setTimeout(() => setShake(false), 600)
+ }
+
+ const handleSubmit = async (e) => {
+ e.preventDefault()
+ if (!form.email || !form.password) {
+ setError('Please fill in all fields.')
+ triggerShake()
+ return
+ }
+
+ setLoading(true)
+ setError('')
+
+ try {
+ const res = await adminService.auth.login(form.email, form.password)
+ if (res?.data?.requires2fa) {
+ setRequires2fa(true)
+ setSessionToken(res.data.sessionToken)
+ setLoading(false)
+ } else if (res?.data?.user) {
+ onLoginSuccess(res.data.user)
+ } else {
+ throw new Error('Invalid response structure received from server.')
+ }
+ } catch (err) {
+ setLoading(false)
+ setError(err.message || 'Login failed. Please check credentials.')
+ triggerShake()
+ }
+ }
+
+ const handle2faSubmit = async (e) => {
+ e.preventDefault()
+ if (!totpCode || totpCode.length < 6) {
+ setError('Please enter a valid 6-digit authentication code.')
+ triggerShake()
+ return
+ }
+
+ setLoading(true)
+ setError('')
+
+ try {
+ const res = await adminService.auth.verifyTotp(form.email, totpCode, sessionToken)
+ if (res?.data?.user) {
+ onLoginSuccess(res.data.user)
+ } else {
+ throw new Error('2FA verification failed.')
+ }
+ } catch (err) {
+ setLoading(false)
+ setError(err.message || 'Invalid 2FA code. Please try again.')
+ triggerShake()
+ }
+ }
+
+ return (
+
+
+
+
+ {/* ── Left panel — brand hero ── */}
+
+
+ {/* ── Right panel — login form ── */}
+
+
+
+ {/* Card header */}
+
+
+
+
+
+
Super Admin
+
Secure administrative access
+
+
+
+ {/* Divider */}
+
+ {requires2fa ? 'Two-Factor Authentication' : 'Sign in to your account'}
+
+
+ {/* Error banner */}
+ {error && (
+
+
+ {error}
+
+ )}
+
+ {requires2fa ? (
+ /* ── 2FA TOTP Form ── */
+
+ ) : (
+ /* ── Standard Login Form ── */
+
+ )}
+
+ {/* Demo hint */}
+
+ 💡
+ Default: admin@cureselect.com / CureSelect@2025
+
+
+ {/* Security badge */}
+
+
+ 256-bit SSL encrypted · Direct API Integration
+
+
+
+
+ )
+}
diff --git a/src/services/adminService.js b/src/services/adminService.js
new file mode 100644
index 0000000..3a6e12f
--- /dev/null
+++ b/src/services/adminService.js
@@ -0,0 +1,143 @@
+import api, { setToken, setStoredUser, removeToken, removeStoredUser } from './api'
+
+export const adminService = {
+ // ── Authentication ──
+ auth: {
+ login: async (email, password) => {
+ const res = await api.post('/api/admin/auth/login', { email, password })
+ if (res?.data?.token) {
+ setToken(res.data.token)
+ if (res.data.user) {
+ setStoredUser(res.data.user)
+ }
+ }
+ return res
+ },
+
+ verifyTotp: async (email, totp, sessionToken) => {
+ const res = await api.post('/api/admin/auth/verify-totp', { email, totp, sessionToken })
+ if (res?.data?.token) {
+ setToken(res.data.token)
+ if (res.data.user) {
+ setStoredUser(res.data.user)
+ }
+ }
+ return res
+ },
+
+ verify2fa: async (email, code) => {
+ const res = await api.post('/api/admin/auth/verify-2fa', { email, code })
+ if (res?.data?.token) {
+ setToken(res.data.token)
+ if (res.data.user) {
+ setStoredUser(res.data.user)
+ }
+ }
+ return res
+ },
+
+ setup2fa: () => api.post('/api/admin/auth/setup-2fa'),
+ confirm2fa: (totp) => api.post('/api/admin/auth/confirm-2fa', { totp }),
+ changePassword: (old_password, new_password) =>
+ api.post('/api/admin/auth/change-password', { old_password, new_password }),
+
+ logout: () => {
+ removeToken()
+ removeStoredUser()
+ },
+ },
+
+ // ── User Management (RBAC) ──
+ users: {
+ list: (params = {}) => api.get('/api/admin/users', params),
+ all: () => api.get('/api/admin/users/all'),
+ get: (id) => api.get(`/api/admin/users/${id}`),
+ create: (data) => api.post('/api/admin/users', data),
+ update: (id, data) => api.patch(`/api/admin/users/${id}`, data),
+ suspend: (id, reason) => api.post(`/api/admin/users/${id}/suspend`, { reason }),
+ reinstate: (id) => api.post(`/api/admin/users/${id}/reinstate`),
+ reset2fa: (id, verification_evidence) =>
+ api.post(`/api/admin/users/${id}/reset-2fa`, { verification_evidence }),
+ forceLogout: (id) => api.post(`/api/admin/users/${id}/force-logout`),
+ delete: (id) => api.delete(`/api/admin/users/${id}`),
+ getActivity: (id) => api.get(`/api/admin/users/${id}/activity`),
+ },
+
+ // ── Approvals (Two-person rule) ──
+ approvals: {
+ list: (params = {}) => api.get('/api/admin/approvals', params),
+ create: (data) => api.post('/api/admin/approvals', data),
+ approve: (id, note) => api.post(`/api/admin/approvals/${id}/approve`, { note }),
+ reject: (id, reason) => api.post(`/api/admin/approvals/${id}/reject`, { reason }),
+ },
+
+ // ── Financial Engine ──
+ finance: {
+ getSummary: () => api.get('/api/admin/finance/summary'),
+ getRevenue: (params = {}) => api.get('/api/admin/finance/revenue', params),
+ getPayouts: (params = {}) => api.get('/api/admin/finance/payouts', params),
+ getRefunds: (params = {}) => api.get('/api/admin/finance/refunds', params),
+ getReconciliation: () => api.get('/api/admin/finance/reconciliation'),
+ getCaregiverEarnings: (params = {}) => api.get('/api/admin/finance/caregiver-earnings', params),
+ getRefundAuthorisations: () => api.get('/api/admin/finance/refund-authorisations'),
+ approveRefund: (id) => api.post(`/api/admin/finance/refund-authorisations/${id}/approve`),
+ rejectRefund: (id, reason) => api.post(`/api/admin/finance/refund-authorisations/${id}/reject`, { reason }),
+ },
+
+ // ── Dynamic Configuration ──
+ config: {
+ get: () => api.get('/api/admin/config'),
+ update: (data) => api.patch('/api/admin/config', data),
+ getHistory: () => api.get('/api/admin/config/history'),
+ },
+
+ // ── Metrics & Growth ──
+ metrics: {
+ growth: () => api.get('/api/admin/metrics/growth'),
+ engagement: () => api.get('/api/admin/metrics/engagement'),
+ quality: () => api.get('/api/admin/metrics/quality'),
+ },
+
+ // ── Break-Glass Access Grants ──
+ accessGrants: {
+ list: () => api.get('/api/admin/access-grants'),
+ create: (data) => api.post('/api/admin/access-grants', data),
+ revoke: (id) => api.post(`/api/admin/access-grants/${id}/revoke`),
+ },
+
+ // ── Audit Logs ──
+ auditLog: {
+ list: (params = {}) => api.get('/api/admin/audit-log', params),
+ get: (id) => api.get(`/api/admin/audit-log/${id}`),
+ export: (format = 'json') => api.get('/api/admin/audit-log/export', { format }),
+ },
+
+ // ── Compliance & Data Governance ──
+ compliance: {
+ getDataRequests: () => api.get('/api/admin/compliance/data-requests'),
+ fulfilDataRequest: (id) => api.post(`/api/admin/compliance/data-requests/${id}/fulfil`),
+ rejectDataRequest: (id, reason) => api.post(`/api/admin/compliance/data-requests/${id}/reject`, { reason }),
+ getRetentionReport: () => api.get('/api/admin/compliance/retention-report'),
+ },
+
+ // ── System Operations ──
+ system: {
+ getHealth: () => api.get('/api/admin/system/health'),
+ getJobs: () => api.get('/api/admin/system/jobs'),
+ triggerJob: (name) => api.post(`/api/admin/system/jobs/${encodeURIComponent(name)}/trigger`),
+ setMaintenanceMode: (enabled, reason) =>
+ api.post('/api/admin/system/maintenance-mode', { enabled, reason }),
+ getIntegrations: () => api.get('/api/admin/system/integrations'),
+ testIntegration: (name) =>
+ api.post(`/api/admin/system/integrations/${encodeURIComponent(name)}/test`),
+ },
+
+ // ── Announcements & Broadcasts ──
+ announcements: {
+ list: () => api.get('/api/admin/announcements'),
+ create: (data) => api.post('/api/admin/announcements', data),
+ cancel: (id) => api.post(`/api/admin/announcements/${id}/cancel`),
+ },
+}
+
+export default adminService
diff --git a/src/services/api.js b/src/services/api.js
new file mode 100644
index 0000000..742f3ac
--- /dev/null
+++ b/src/services/api.js
@@ -0,0 +1,96 @@
+// Base API client configured for CureSelect Super Admin Backend
+const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000'
+const API_KEY = import.meta.env.VITE_API_KEY || 'DUMUqJrNeyuaPDSZSsMf91qjgwc5K3N7'
+
+export const getToken = () => localStorage.getItem('admin_token')
+export const setToken = (token) => localStorage.setItem('admin_token', token)
+export const removeToken = () => localStorage.removeItem('admin_token')
+
+export const getStoredUser = () => {
+ try {
+ const raw = localStorage.getItem('admin_user')
+ return raw ? JSON.parse(raw) : null
+ } catch {
+ return null
+ }
+}
+export const setStoredUser = (user) => localStorage.setItem('admin_user', JSON.stringify(user))
+export const removeStoredUser = () => localStorage.removeItem('admin_user')
+
+async function request(endpoint, options = {}) {
+ const url = endpoint.startsWith('http') ? endpoint : `${API_BASE_URL}${endpoint.startsWith('/') ? '' : '/'}${endpoint}`
+
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ 'x-api-key': API_KEY,
+ ...options.headers,
+ }
+
+ const token = getToken()
+ if (token && !headers['Authorization']) {
+ headers['Authorization'] = `Bearer ${token}`
+ }
+
+ const config = {
+ ...options,
+ headers,
+ }
+
+ if (config.body && typeof config.body === 'object' && !(config.body instanceof FormData)) {
+ config.body = JSON.stringify(config.body)
+ }
+
+ try {
+ const response = await fetch(url, config)
+ const data = await response.json().catch(() => null)
+
+ if (!response.ok) {
+ if (response.status === 401 && !endpoint.includes('/auth/login')) {
+ // Token expired or unauthorized
+ removeToken()
+ removeStoredUser()
+ window.dispatchEvent(new Event('auth:unauthorized'))
+ }
+
+ const errorMessage = data?.message || data?.errors?.description || `HTTP error ${response.status}: ${response.statusText}`
+ const error = new Error(errorMessage)
+ error.status = response.status
+ error.data = data
+ throw error
+ }
+
+ return data
+ } catch (err) {
+ if (!err.status && err.message === 'Failed to fetch') {
+ const connErr = new Error(`Cannot connect to backend server at ${API_BASE_URL}. Ensure PHP server is running.`)
+ connErr.status = 0
+ throw connErr
+ }
+ throw err
+ }
+}
+
+export const api = {
+ get: (endpoint, params = {}, headers = {}) => {
+ const query = new URLSearchParams(
+ Object.entries(params).filter(([_, v]) => v !== undefined && v !== null && v !== '')
+ ).toString()
+ const url = query ? `${endpoint}?${query}` : endpoint
+ return request(url, { method: 'GET', headers })
+ },
+ post: (endpoint, body = {}, headers = {}) => {
+ return request(endpoint, { method: 'POST', body, headers })
+ },
+ patch: (endpoint, body = {}, headers = {}) => {
+ return request(endpoint, { method: 'PATCH', body, headers })
+ },
+ put: (endpoint, body = {}, headers = {}) => {
+ return request(endpoint, { method: 'PUT', body, headers })
+ },
+ delete: (endpoint, body = {}, headers = {}) => {
+ return request(endpoint, { method: 'DELETE', body, headers })
+ },
+}
+
+export default api
diff --git a/vite.config.js b/vite.config.js
new file mode 100644
index 0000000..06e5510
--- /dev/null
+++ b/vite.config.js
@@ -0,0 +1,22 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ port: 5173,
+ proxy: {
+ '/api': {
+ target: 'http://localhost:8000',
+ changeOrigin: true,
+ secure: false,
+ },
+ '/admin': {
+ target: 'http://localhost:8000',
+ changeOrigin: true,
+ secure: false,
+ },
+ },
+ },
+})