diff --git a/package-lock.json b/package-lock.json index 6df2d0a..e3e85a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,15 +8,12 @@ "name": "doormile-console-admin", "version": "1.0.0", "dependencies": { - "@emotion/react": "^11.11.4", - "@emotion/styled": "^11.11.5", - "@mui/icons-material": "^5.15.20", - "@mui/lab": "^5.0.0-alpha.170", - "@mui/material": "^5.15.20", - "@mui/x-date-pickers": "^6.20.2", + "@astryxdesign/cli": "^0.1.2", + "@astryxdesign/core": "^0.1.2", + "@astryxdesign/theme-neutral": "^0.1.2", "dayjs": "^1.11.11", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.2.7", + "react-dom": "^19.2.7", "react-router-dom": "^6.23.1" }, "devDependencies": { @@ -24,6 +21,66 @@ "vite": "^5.3.1" } }, + "node_modules/@astryxdesign/cli": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@astryxdesign/cli/-/cli-0.1.2.tgz", + "integrity": "sha512-I4VlPGQRI7PTMcjBRX1hj/wB/KCHU8wd8NbqF1p/McAPgj0A5k72lyJFzH+IAA+ciaef5NMdakvovzbfJGPcSw==", + "license": "MIT", + "dependencies": { + "@clack/prompts": "^1.5.1", + "commander": "^12.1.0", + "jiti": "^2.7.0", + "jscodeshift": "^17.3.0", + "zod": "^4.4.3" + }, + "bin": { + "astryx": "bin/astryx.mjs", + "cli": "bin/astryx.mjs" + }, + "peerDependencies": { + "@astryxdesign/core": "*", + "@astryxdesign/lab": "*", + "@astryxdesign/theme-neutral": "*", + "gpt-tokenizer": "^2.0.0" + }, + "peerDependenciesMeta": { + "@astryxdesign/core": { + "optional": true + }, + "@astryxdesign/lab": { + "optional": true + }, + "@astryxdesign/theme-neutral": { + "optional": true + } + } + }, + "node_modules/@astryxdesign/core": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@astryxdesign/core/-/core-0.1.2.tgz", + "integrity": "sha512-SJOEXUaRvcVgGbdPwV5UL5aYMbwIl09H2yQb3hPh4NNGWqmLTHE05IFTNRi37Mk9ywoo01+UM2ZrewmJe39f0A==", + "license": "MIT", + "dependencies": { + "@stylexjs/stylex": "^0.18.3" + }, + "peerDependencies": { + "react": ">=19.0.0", + "react-dom": ">=19.0.0" + } + }, + "node_modules/@astryxdesign/theme-neutral": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@astryxdesign/theme-neutral/-/theme-neutral-0.1.2.tgz", + "integrity": "sha512-kEBBJ7O5H+27IACsGEqZSEVFy2r4VlBu4Vzfm/LOJ/Ez6C/RBTCXq9Iu8CoZS4Qt/WkYUvqLPDiJlgXuALy9uQ==", + "license": "MIT", + "dependencies": { + "lucide-react": "^1.18.0" + }, + "peerDependencies": { + "@astryxdesign/core": "0.1.2", + "react": ">=19" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -42,7 +99,6 @@ "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" @@ -52,7 +108,6 @@ "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", @@ -83,7 +138,6 @@ "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/@babel/generator": { @@ -102,11 +156,22 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "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", @@ -119,6 +184,27 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/helper-globals": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", @@ -128,6 +214,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "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", @@ -145,7 +244,6 @@ "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", @@ -159,16 +257,57 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.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-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "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", @@ -191,7 +330,6 @@ "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" @@ -201,7 +339,6 @@ "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", @@ -226,6 +363,146 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.29.7.tgz", + "integrity": "sha512-ajMX6QPcyomotqwpzhkYGxcK2i/us0rs1Qo9QvUpa+Fca0FTmqrzKrctoIYLMxcOhGZldGT/BAVkRGTWBiR8gQ==", + "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-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "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-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "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-class-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.29.7.tgz", + "integrity": "sha512-wRHeUjUjCZnMHmiO5bRgjFLcoEh7JyTdByOW11ahhwNa4V0bmeGEaIvt51yq0zQp2yWIpqfxXXPyUP6GFJZHOQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-flow": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", + "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-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@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-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", @@ -258,13 +535,78 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/runtime": { + "node_modules/@babel/plugin-transform-typescript": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", - "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-flow": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.29.7.tgz", + "integrity": "sha512-KYIRV0BuaN68CDdsqFkAD7MU7yipUqQNuNElwATdxaIdpTjhvtY82QvkBJs7zV3Evxj2jFAAZ1iO8nyy0nhjqA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-transform-flow-strip-types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz", + "integrity": "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/register": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.29.7.tgz", + "integrity": "sha512-AMGJoWuES861riy6pcB0fphE1YXybtQnBYQMuIyPv6mKLiosfa79BKTnAOyx215c/3RJPJpdQwoHZ3earVH7AA==", + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.6", + "source-map-support": "^0.5.16" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/template": { @@ -312,151 +654,33 @@ "node": ">=6.9.0" } }, - "node_modules/@emotion/babel-plugin": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", - "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/serialize": "^1.3.3", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", - "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.9.0", - "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", - "license": "MIT" - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", - "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.9.0" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", - "license": "MIT" - }, - "node_modules/@emotion/react": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", - "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.13.5", - "@emotion/cache": "^11.14.0", - "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", - "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", - "license": "MIT", - "dependencies": { - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.2", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", - "license": "MIT" - }, - "node_modules/@emotion/styled": { - "version": "11.14.1", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", - "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.13.5", - "@emotion/is-prop-valid": "^1.3.0", - "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", - "@emotion/utils": "^1.4.2" - }, - "peerDependencies": { - "@emotion/react": "^11.0.0-rc.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/unitless": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", - "license": "MIT" - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", - "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { + "node_modules/@clack/core": { "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.2.tgz", + "integrity": "sha512-0Ty/1Gfm+Kb07sXcuESjyKfwEhSy4Ns1AgeEisHb/bDY5fWme0tTeTkU14T1Gmcs17YIjB/teiDe4uaCghbYqQ==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } }, - "node_modules/@emotion/weak-memoize": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", - "license": "MIT" + "node_modules/@clack/prompts": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.6.0.tgz", + "integrity": "sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.4.2", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", @@ -849,44 +1073,6 @@ "node": ">=12" } }, - "node_modules/@floating-ui/core": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", - "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.11" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", - "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.5", - "@floating-ui/utils": "^0.2.11" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", - "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.7.6" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", - "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", - "license": "MIT" - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -901,7 +1087,6 @@ "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", @@ -933,381 +1118,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.40-1", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40-1.tgz", - "integrity": "sha512-agKXuNNy0bHUmeU7pNmoZwNFr7Hiyhojkb9+2PVyDG5+6RafYuyMgbrav8CndsB7KUc/U51JAw9vKNDLYBzaUA==", - "deprecated": "This package has been replaced by @base-ui/react", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@floating-ui/react-dom": "^2.0.8", - "@mui/types": "~7.2.15", - "@mui/utils": "^5.17.1", - "@popperjs/core": "^2.11.8", - "clsx": "^2.1.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/core-downloads-tracker": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.18.0.tgz", - "integrity": "sha512-jbhwoQ1AY200PSSOrNXmrFCaSDSJWP7qk6urkTmIirvRXDROkqe+QwcLlUiw/PrREwsIF/vm3/dAXvjlMHF0RA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - } - }, - "node_modules/@mui/icons-material": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.18.0.tgz", - "integrity": "sha512-1s0vEZj5XFXDMmz3Arl/R7IncFqJ+WQ95LDp1roHWGDE2oCO3IS4/hmiOv1/8SD9r6B7tv9GLiqVZYHo+6PkTg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@mui/material": "^5.0.0", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/lab": { - "version": "5.0.0-alpha.177", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.177.tgz", - "integrity": "sha512-bdCxxtNjlWAgN9rtrwlmFydJ1qxA3IIbb6OlomGFsIXw0zGoHomLyjvh72q/R3yUAC0kvSef18cHY1UalLylyQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/base": "5.0.0-beta.40-1", - "@mui/system": "^5.18.0", - "@mui/types": "~7.2.15", - "@mui/utils": "^5.17.1", - "clsx": "^2.1.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@mui/material": ">=5.15.0", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/material": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.18.0.tgz", - "integrity": "sha512-bbH/HaJZpFtXGvWg3TsBWG4eyt3gah3E7nCNU8GLyRjVoWcA91Vm/T+sjHfUcwgJSw9iLtucfHBoq+qW/T30aA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/core-downloads-tracker": "^5.18.0", - "@mui/system": "^5.18.0", - "@mui/types": "~7.2.15", - "@mui/utils": "^5.17.1", - "@popperjs/core": "^2.11.8", - "@types/react-transition-group": "^4.4.10", - "clsx": "^2.1.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1", - "react-is": "^19.0.0", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/private-theming": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.17.1.tgz", - "integrity": "sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.17.1", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/styled-engine": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.18.0.tgz", - "integrity": "sha512-BN/vKV/O6uaQh2z5rXV+MBlVrEkwoS/TK75rFQ2mjxA7+NBo8qtTAOA4UaM0XeJfn7kh2wZ+xQw2HAx0u+TiBg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@emotion/cache": "^11.13.5", - "@emotion/serialize": "^1.3.3", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, - "node_modules/@mui/system": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.18.0.tgz", - "integrity": "sha512-ojZGVcRWqWhu557cdO3pWHloIGJdzVtxs3rk0F9L+x55LsUjcMUVkEhiF7E4TMxZoF9MmIHGGs0ZX3FDLAf0Xw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.17.1", - "@mui/styled-engine": "^5.18.0", - "@mui/types": "~7.2.15", - "@mui/utils": "^5.17.1", - "clsx": "^2.1.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/types": { - "version": "7.2.24", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", - "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/utils": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", - "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/types": "~7.2.15", - "@types/prop-types": "^15.7.12", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^19.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/x-date-pickers": { - "version": "6.20.2", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.20.2.tgz", - "integrity": "sha512-x1jLg8R+WhvkmUETRfX2wC+xJreMii78EXKLl6r3G+ggcAZlPyt0myID1Amf6hvJb9CtR7CgUo8BwR+1Vx9Ggw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.2", - "@mui/base": "^5.0.0-beta.22", - "@mui/utils": "^5.14.16", - "@types/react-transition-group": "^4.4.8", - "clsx": "^2.0.0", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, - "peerDependencies": { - "@emotion/react": "^11.9.0", - "@emotion/styled": "^11.8.1", - "@mui/material": "^5.8.6", - "@mui/system": "^5.8.0", - "date-fns": "^2.25.0 || ^3.2.0", - "date-fns-jalali": "^2.13.0-0", - "dayjs": "^1.10.7", - "luxon": "^3.0.2", - "moment": "^2.29.4", - "moment-hijri": "^2.1.2", - "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "date-fns": { - "optional": true - }, - "date-fns-jalali": { - "optional": true - }, - "dayjs": { - "optional": true - }, - "luxon": { - "optional": true - }, - "moment": { - "optional": true - }, - "moment-hijri": { - "optional": true - }, - "moment-jalaali": { - "optional": true - } - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, "node_modules/@remix-run/router": { "version": "1.23.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.3.tgz", @@ -1674,6 +1484,17 @@ "win32" ] }, + "node_modules/@stylexjs/stylex": { + "version": "0.18.3", + "resolved": "https://registry.npmjs.org/@stylexjs/stylex/-/stylex-0.18.3.tgz", + "integrity": "sha512-15gDzAJAorOE0yzxaWLNxldW2aqdmCiLG5QcPD8nmVCVqvrWp0Asgv45zk7LtN86WJb/4Ym9eQ6qT5MJW59tWQ==", + "license": "MIT", + "dependencies": { + "css-mediaquery": "^0.1.2", + "invariant": "^2.2.4", + "styleq": "0.2.1" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1726,37 +1547,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "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==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.2.16", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.16.tgz", - "integrity": "sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==", - "license": "MIT", - "peer": true, - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-transition-group": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", - "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*" - } - }, "node_modules/@vitejs/plugin-react": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", @@ -1778,26 +1568,22 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" + "tslib": "^2.0.1" }, "engines": { - "node": ">=10", - "npm": ">=6" + "node": ">=4" } }, "node_modules/baseline-browser-mapping": { "version": "2.10.33", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz", "integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==", - "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -1806,11 +1592,22 @@ "node": ">=6.0.0" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.28.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -1840,20 +1637,16 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" }, "node_modules/caniuse-lite": { "version": "1.0.30001793", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -1870,43 +1663,41 @@ ], "license": "CC-BY-4.0" }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, "engines": { "node": ">=6" } }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "license": "MIT" - }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "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==", + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "license": "MIT" }, + "node_modules/css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==", + "license": "BSD" + }, "node_modules/dayjs": { "version": "1.11.21", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", @@ -1930,41 +1721,12 @@ } } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.368", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.368.tgz", "integrity": "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==", - "dev": true, "license": "ISC" }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -2008,30 +1770,107 @@ "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/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4" } }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", "license": "MIT" }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flow-estree": { + "version": "0.321.0", + "resolved": "https://registry.npmjs.org/flow-estree/-/flow-estree-0.321.0.tgz", + "integrity": "sha512-rQY7YKoo+PKpAHQjEP2cxyIefk04OCEKUlbtV4y6LAUG3Ly7guQX9YH8th6drSmYcNkMgpqWMaHRhhYaAF69CA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/flow-parser": { + "version": "0.321.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.321.0.tgz", + "integrity": "sha512-9LNK2rp/NWWbwdEK6mxC54XfQRyD2lVV0iPVBYf+5o5vvqJl7ietkR1hX3/dZ39j0GAbL4PIFoeekDViOW0x/Q==", + "license": "MIT", + "dependencies": { + "flow-estree": "0.321.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2047,87 +1886,83 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "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/hasown": { + "node_modules/gpt-tokenizer": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/gpt-tokenizer/-/gpt-tokenizer-2.9.0.tgz", + "integrity": "sha512-YSpexBL/k4bfliAzMrRqn3M6+it02LutVyhVpDeMKrC/O9+pCe/5s8U2hYKa2vFLD5/vHhsKc8sOn/qGqII8Kg==", + "license": "MIT", + "peer": true + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "isobject": "^3.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "jiti": "lib/jiti-cli.mjs" } }, "node_modules/js-tokens": { @@ -2136,6 +1971,46 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, + "node_modules/jscodeshift": { + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-17.3.0.tgz", + "integrity": "sha512-LjFrGOIORqXBU+jwfC9nbkjmQfFldtMIoS6d9z2LG/lkmyNXsJAySPT+2SWXJEoE68/bCWcxKpXH37npftgmow==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/preset-flow": "^7.24.7", + "@babel/preset-typescript": "^7.24.7", + "@babel/register": "^7.24.6", + "flow-parser": "0.*", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.7", + "neo-async": "^2.5.0", + "picocolors": "^1.0.1", + "recast": "^0.23.11", + "tmp": "^0.2.3", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "jscodeshift": "bin/jscodeshift.js" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@babel/preset-env": "^7.1.6" + }, + "peerDependenciesMeta": { + "@babel/preset-env": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -2148,17 +2023,10 @@ "node": ">=6" } }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT" - }, "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" @@ -2167,11 +2035,27 @@ "node": ">=6" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } }, "node_modules/loose-envify": { "version": "1.4.0", @@ -2189,12 +2073,55 @@ "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/lucide-react": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.22.0.tgz", + "integrity": "sha512-c9o3l0PiNcgOQDW4F31BEYHudE7kgxVt3o30qMl36ZPwTxXlGB4QnLilhERvVM4uh/pl5MDyY1/gzZSYcHDtBg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2220,68 +2147,64 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.47", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "license": "MIT", "dependencies": { - "callsites": "^3.0.0" + "p-try": "^2.0.0" }, "engines": { "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" } }, "node_modules/picocolors": { @@ -2290,6 +2213,48 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss": { "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", @@ -2319,54 +2284,27 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "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==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "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==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.2.7" } }, - "node_modules/react-is": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", - "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==", - "license": "MIT" - }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -2409,50 +2347,29 @@ "react-dom": ">=16.8" } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "node_modules/recast": { + "version": "0.23.12", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.12.tgz", + "integrity": "sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/recast/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "license": "MIT", - "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, "node_modules/rollup": { @@ -2501,33 +2418,50 @@ } }, "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" - } + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" }, "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": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2538,29 +2472,68 @@ "node": ">=0.10.0" } }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styleq": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/styleq/-/styleq-0.2.1.tgz", + "integrity": "sha512-L0TR0NQb+X4/ktDEKmjWyp27gla+LUYi/by5k5SjKXf6/pvZP7wbwEB5J+tqxdFVPgzbsuz+d4RTScO/QZquBw==", "license": "MIT" }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14.14" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2647,20 +2620,32 @@ } } }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "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" }, - "node_modules/yaml": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", - "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", - "license": "ISC", - "engines": { - "node": ">= 6" + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" } } } diff --git a/package.json b/package.json index c94e1ab..ea70a55 100644 --- a/package.json +++ b/package.json @@ -10,15 +10,12 @@ "preview": "vite preview" }, "dependencies": { - "@emotion/react": "^11.11.4", - "@emotion/styled": "^11.11.5", - "@mui/icons-material": "^5.15.20", - "@mui/lab": "^5.0.0-alpha.170", - "@mui/material": "^5.15.20", - "@mui/x-date-pickers": "^6.20.2", + "@astryxdesign/cli": "^0.1.2", + "@astryxdesign/core": "^0.1.2", + "@astryxdesign/theme-neutral": "^0.1.2", "dayjs": "^1.11.11", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.2.7", + "react-dom": "^19.2.7", "react-router-dom": "^6.23.1" }, "devDependencies": { diff --git a/public/navbarLogo.png b/public/navbarLogo.png new file mode 100644 index 0000000..f643518 Binary files /dev/null and b/public/navbarLogo.png differ diff --git a/src/App.jsx b/src/App.jsx index ad72721..74cc6bc 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,5 @@ import { Suspense, lazy } from 'react'; import { Routes, Route, Navigate } from 'react-router-dom'; -import { Box, CircularProgress } from '@mui/material'; import MainLayout from '@/layout/MainLayout'; import MinimalLayout from '@/layout/MinimalLayout'; @@ -12,9 +11,10 @@ const load = (factory) => { return ( - - +
+
+ +
} > @@ -29,7 +29,7 @@ export default function App() { }> import('@/pages/Dashboard'))} /> - import('@/pages/tenants/Tenants'))} /> + import('@/pages/tenants/Tenants'))} /> import('@/pages/survey/Survey.jsx'))} /> diff --git a/src/app/dashboard-ref/page.tsx b/src/app/dashboard-ref/page.tsx new file mode 100644 index 0000000..194aa74 --- /dev/null +++ b/src/app/dashboard-ref/page.tsx @@ -0,0 +1,808 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +'use client'; + +import { + VStack, + HStack, + Layout, + LayoutContent, +} from '@astryxdesign/core/Layout'; +import {Text, Heading} from '@astryxdesign/core/Text'; +import {Card} from '@astryxdesign/core/Card'; +import {Button} from '@astryxdesign/core/Button'; +import {ProgressBar} from '@astryxdesign/core/ProgressBar'; +import { + BarChart, + Bar, + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + ResponsiveContainer, +} from 'recharts'; +import {Grid} from '@astryxdesign/core/Grid'; +import {Table, proportional, pixel} from '@astryxdesign/core/Table'; +import type {TableColumn} from '@astryxdesign/core/Table'; +import {Divider} from '@astryxdesign/core/Divider'; +import {Link} from '@astryxdesign/core/Link'; +import {Icon} from '@astryxdesign/core/Icon'; + +// ============= ICONS ============= + +import { + ArrowPathIcon, + ArrowUpIcon, + ArrowDownIcon, +} from '@heroicons/react/24/outline'; +import {StopIcon} from '@heroicons/react/24/solid'; + +// ============= DATA ============= + +// Active users chart data (96 points over 24h at 15-min intervals: Apr 1 14:00 → Apr 2 14:00) +// Each point has an index (0–95) for even spacing, plus a label for display +const activeUsersData = [ + // Apr 1 14:00 — mid-afternoon, strong work hours + {hour: 0, label: 'Apr 1 14:00', allUsers: 116, desktop: 79, mobile: 37}, + {hour: 1, label: 'Apr 1 14:15', allUsers: 118, desktop: 80, mobile: 38}, + {hour: 2, label: 'Apr 1 14:30', allUsers: 117, desktop: 79, mobile: 38}, + {hour: 3, label: 'Apr 1 14:45', allUsers: 119, desktop: 81, mobile: 38}, + // Apr 1 15:00 — afternoon lull + {hour: 4, label: 'Apr 1 15:00', allUsers: 116, desktop: 79, mobile: 37}, + {hour: 5, label: 'Apr 1 15:15', allUsers: 113, desktop: 76, mobile: 37}, + {hour: 6, label: 'Apr 1 15:30', allUsers: 109, desktop: 72, mobile: 37}, + {hour: 7, label: 'Apr 1 15:45', allUsers: 111, desktop: 74, mobile: 37}, + // Apr 1 16:00 — late afternoon, some leaving early + {hour: 8, label: 'Apr 1 16:00', allUsers: 110, desktop: 72, mobile: 38}, + {hour: 9, label: 'Apr 1 16:15', allUsers: 107, desktop: 69, mobile: 38}, + {hour: 10, label: 'Apr 1 16:30', allUsers: 104, desktop: 66, mobile: 38}, + {hour: 11, label: 'Apr 1 16:45', allUsers: 101, desktop: 62, mobile: 39}, + // Apr 1 17:00 — 5PM exodus, desktop drops fast, mobile bumps + {hour: 12, label: 'Apr 1 17:00', allUsers: 100, desktop: 57, mobile: 43}, + {hour: 13, label: 'Apr 1 17:15', allUsers: 99, desktop: 54, mobile: 45}, + {hour: 14, label: 'Apr 1 17:30', allUsers: 97, desktop: 50, mobile: 47}, + {hour: 15, label: 'Apr 1 17:45', allUsers: 95, desktop: 46, mobile: 49}, + // Apr 1 18:00 — commute, mobile overtakes desktop + {hour: 16, label: 'Apr 1 18:00', allUsers: 93, desktop: 41, mobile: 52}, + {hour: 17, label: 'Apr 1 18:15', allUsers: 91, desktop: 39, mobile: 52}, + {hour: 18, label: 'Apr 1 18:30', allUsers: 89, desktop: 37, mobile: 52}, + {hour: 19, label: 'Apr 1 18:45', allUsers: 87, desktop: 36, mobile: 51}, + // Apr 1 19:00 — dinner, decline slowing + {hour: 20, label: 'Apr 1 19:00', allUsers: 85, desktop: 34, mobile: 51}, + {hour: 21, label: 'Apr 1 19:15', allUsers: 83, desktop: 32, mobile: 51}, + {hour: 22, label: 'Apr 1 19:30', allUsers: 80, desktop: 30, mobile: 50}, + {hour: 23, label: 'Apr 1 19:45', allUsers: 77, desktop: 28, mobile: 49}, + // Apr 1 20:00 — couch browsing, mobile plateau + {hour: 24, label: 'Apr 1 20:00', allUsers: 75, desktop: 26, mobile: 49}, + {hour: 25, label: 'Apr 1 20:15', allUsers: 76, desktop: 26, mobile: 50}, + {hour: 26, label: 'Apr 1 20:30', allUsers: 76, desktop: 27, mobile: 49}, + {hour: 27, label: 'Apr 1 20:45', allUsers: 75, desktop: 27, mobile: 48}, + // Apr 1 21:00 — winding down, mobile dropping off + {hour: 28, label: 'Apr 1 21:00', allUsers: 73, desktop: 27, mobile: 46}, + {hour: 29, label: 'Apr 1 21:15', allUsers: 71, desktop: 27, mobile: 44}, + {hour: 30, label: 'Apr 1 21:30', allUsers: 69, desktop: 27, mobile: 42}, + {hour: 31, label: 'Apr 1 21:45', allUsers: 67, desktop: 28, mobile: 39}, + // Apr 1 22:00 — bedtime wave, mobile drops, desktop holds + {hour: 32, label: 'Apr 1 22:00', allUsers: 65, desktop: 29, mobile: 36}, + {hour: 33, label: 'Apr 1 22:15', allUsers: 63, desktop: 29, mobile: 34}, + {hour: 34, label: 'Apr 1 22:30', allUsers: 61, desktop: 30, mobile: 31}, + {hour: 35, label: 'Apr 1 22:45', allUsers: 60, desktop: 31, mobile: 29}, + // Apr 1 23:00 — desktop overtakes as local users sleep, other TZs active + {hour: 36, label: 'Apr 1 23:00', allUsers: 59, desktop: 33, mobile: 26}, + {hour: 37, label: 'Apr 1 23:15', allUsers: 58, desktop: 34, mobile: 24}, + {hour: 38, label: 'Apr 1 23:30', allUsers: 57, desktop: 35, mobile: 22}, + {hour: 39, label: 'Apr 1 23:45', allUsers: 56, desktop: 36, mobile: 20}, + // Apr 2 00:00 — EMEA morning starts, desktop dominant + {hour: 40, label: 'Apr 2 00:00', allUsers: 56, desktop: 38, mobile: 18}, + {hour: 41, label: 'Apr 2 00:15', allUsers: 56, desktop: 39, mobile: 17}, + {hour: 42, label: 'Apr 2 00:30', allUsers: 57, desktop: 40, mobile: 17}, + {hour: 43, label: 'Apr 2 00:45', allUsers: 56, desktop: 40, mobile: 16}, + // Apr 2 01:00 — EMEA working, plateau + {hour: 44, label: 'Apr 2 01:00', allUsers: 56, desktop: 41, mobile: 15}, + {hour: 45, label: 'Apr 2 01:15', allUsers: 55, desktop: 41, mobile: 14}, + {hour: 46, label: 'Apr 2 01:30', allUsers: 55, desktop: 41, mobile: 14}, + {hour: 47, label: 'Apr 2 01:45', allUsers: 54, desktop: 40, mobile: 14}, + // Apr 2 02:00 — EMEA mid-morning, holding steady + {hour: 48, label: 'Apr 2 02:00', allUsers: 54, desktop: 40, mobile: 14}, + {hour: 49, label: 'Apr 2 02:15', allUsers: 53, desktop: 39, mobile: 14}, + {hour: 50, label: 'Apr 2 02:30', allUsers: 53, desktop: 39, mobile: 14}, + {hour: 51, label: 'Apr 2 02:45', allUsers: 52, desktop: 38, mobile: 14}, + // Apr 2 03:00 — EMEA lunch gap, slight dip + {hour: 52, label: 'Apr 2 03:00', allUsers: 51, desktop: 37, mobile: 14}, + {hour: 53, label: 'Apr 2 03:15', allUsers: 50, desktop: 36, mobile: 14}, + {hour: 54, label: 'Apr 2 03:30', allUsers: 49, desktop: 35, mobile: 14}, + {hour: 55, label: 'Apr 2 03:45', allUsers: 49, desktop: 35, mobile: 14}, + // Apr 2 04:00 — EMEA afternoon, floor + {hour: 56, label: 'Apr 2 04:00', allUsers: 48, desktop: 34, mobile: 14}, + {hour: 57, label: 'Apr 2 04:15', allUsers: 48, desktop: 33, mobile: 15}, + {hour: 58, label: 'Apr 2 04:30', allUsers: 49, desktop: 33, mobile: 16}, + {hour: 59, label: 'Apr 2 04:45', allUsers: 49, desktop: 32, mobile: 17}, + // Apr 2 05:00 — early risers checking phones, mobile climbing + {hour: 60, label: 'Apr 2 05:00', allUsers: 50, desktop: 31, mobile: 19}, + {hour: 61, label: 'Apr 2 05:15', allUsers: 51, desktop: 30, mobile: 21}, + {hour: 62, label: 'Apr 2 05:30', allUsers: 52, desktop: 29, mobile: 23}, + {hour: 63, label: 'Apr 2 05:45', allUsers: 54, desktop: 28, mobile: 26}, + // Apr 2 06:00 — alarms going off, mobile surging + {hour: 64, label: 'Apr 2 06:00', allUsers: 56, desktop: 27, mobile: 29}, + {hour: 65, label: 'Apr 2 06:15', allUsers: 58, desktop: 26, mobile: 32}, + {hour: 66, label: 'Apr 2 06:30', allUsers: 61, desktop: 26, mobile: 35}, + {hour: 67, label: 'Apr 2 06:45', allUsers: 64, desktop: 27, mobile: 37}, + // Apr 2 07:00 — commute, mobile peaks, desktop starting + {hour: 68, label: 'Apr 2 07:00', allUsers: 67, desktop: 28, mobile: 39}, + {hour: 69, label: 'Apr 2 07:15', allUsers: 70, desktop: 30, mobile: 40}, + {hour: 70, label: 'Apr 2 07:30', allUsers: 73, desktop: 33, mobile: 40}, + {hour: 71, label: 'Apr 2 07:45', allUsers: 76, desktop: 37, mobile: 39}, + // Apr 2 08:00 — arriving at desks, desktop ramping + {hour: 72, label: 'Apr 2 08:00', allUsers: 80, desktop: 43, mobile: 37}, + {hour: 73, label: 'Apr 2 08:15', allUsers: 85, desktop: 49, mobile: 36}, + {hour: 74, label: 'Apr 2 08:30', allUsers: 90, desktop: 55, mobile: 35}, + {hour: 75, label: 'Apr 2 08:45', allUsers: 95, desktop: 61, mobile: 34}, + // Apr 2 09:00 — work day, desktop dominant + {hour: 76, label: 'Apr 2 09:00', allUsers: 99, desktop: 66, mobile: 33}, + {hour: 77, label: 'Apr 2 09:15', allUsers: 102, desktop: 69, mobile: 33}, + {hour: 78, label: 'Apr 2 09:30', allUsers: 104, desktop: 72, mobile: 32}, + {hour: 79, label: 'Apr 2 09:45', allUsers: 104, desktop: 72, mobile: 32}, + // Apr 2 10:00 — coffee break stall, then climbing + {hour: 80, label: 'Apr 2 10:00', allUsers: 106, desktop: 74, mobile: 32}, + {hour: 81, label: 'Apr 2 10:15', allUsers: 109, desktop: 76, mobile: 33}, + {hour: 82, label: 'Apr 2 10:30', allUsers: 112, desktop: 78, mobile: 34}, + {hour: 83, label: 'Apr 2 10:45', allUsers: 114, desktop: 80, mobile: 34}, + // Apr 2 11:00 — approaching peak + {hour: 84, label: 'Apr 2 11:00', allUsers: 116, desktop: 81, mobile: 35}, + {hour: 85, label: 'Apr 2 11:15', allUsers: 117, desktop: 81, mobile: 36}, + {hour: 86, label: 'Apr 2 11:30', allUsers: 119, desktop: 83, mobile: 36}, + {hour: 87, label: 'Apr 2 11:45', allUsers: 119, desktop: 82, mobile: 37}, + // Apr 2 12:00 — lunch dip, mobile ticks up + {hour: 88, label: 'Apr 2 12:00', allUsers: 120, desktop: 83, mobile: 37}, + {hour: 89, label: 'Apr 2 12:15', allUsers: 115, desktop: 78, mobile: 37}, + {hour: 90, label: 'Apr 2 12:30', allUsers: 111, desktop: 74, mobile: 37}, + {hour: 91, label: 'Apr 2 12:45', allUsers: 110, desktop: 73, mobile: 37}, + // Apr 2 13:00 — returning from lunch + {hour: 92, label: 'Apr 2 13:00', allUsers: 113, desktop: 76, mobile: 37}, + {hour: 93, label: 'Apr 2 13:15', allUsers: 116, desktop: 79, mobile: 37}, + {hour: 94, label: 'Apr 2 13:30', allUsers: 118, desktop: 81, mobile: 37}, + {hour: 95, label: 'Apr 2 14:00', allUsers: 120, desktop: 83, mobile: 37}, +]; + +// X-axis tick indices and their display labels +const xAxisTicks = [0, 32, 64, 95]; +const xAxisLabels: Record = { + 0: 'Apr 1 14:00', + 32: 'Apr 1 22:00', + 64: 'Apr 2 06:00', + 95: 'Apr 2 14:00', +}; + +// Metric cards +const metrics = [ + { + label: 'Monthly Visitors', + value: '27.3 k', + change: '+18.2%', + positive: true, + }, + { + label: 'Monthly Page Views', + value: '48.2 k', + change: '+12.5%', + positive: true, + }, + { + label: 'Avg. Session', + value: '4.5 min', + change: '-14.3%', + positive: false, + }, + { + label: 'Bounce Rate', + value: '42.3%', + change: '-8.7%', + positive: false, + }, +]; + +// Sparkline data for each metric card (30 days, weekends at indices 5-6, 12-13, 19-20, 26-27) +const sparklines = [ + // Monthly Visitors: +18.2% — declining first 2 weeks, hits bottom around day 14, then sharp recovery + // prettier-ignore + [48, 46, 44, 42, 40, 18, 16, 38, 36, 34, 32, 30, 12, 10, 28, 26, 28, 32, 36, 14, 12, 40, 44, 48, 52, 56, 28, 24, 58, 62], + // Monthly Page Views: +12.5% — flat/choppy first 3 weeks, then kicks up sharply in final week + // prettier-ignore + [36, 38, 35, 37, 36, 14, 12, 38, 36, 34, 37, 35, 12, 10, 36, 34, 36, 35, 38, 14, 12, 40, 44, 50, 54, 56, 26, 22, 58, 60], + // Avg. Session: -14.3% — strong start, holds through week 2, then clear drop-off week 3-4 + // prettier-ignore + [58, 56, 60, 58, 62, 30, 26, 60, 58, 62, 60, 58, 28, 24, 56, 54, 50, 46, 42, 18, 14, 38, 36, 34, 32, 30, 10, 8, 28, 26], + // Bounce Rate: -8.7% — high and volatile first half, starts dropping around day 16, steady decline + // prettier-ignore + [52, 56, 50, 54, 58, 62, 60, 54, 52, 56, 50, 54, 60, 58, 50, 48, 46, 44, 40, 46, 44, 38, 36, 34, 36, 32, 38, 36, 30, 28], +]; + +// Demographics +const regionData = [ + { + label: 'NORAM', + value: 38, + color: 'var(--color-data-categorical-blue, #0171E3)', + }, + { + label: 'EMEA', + value: 28, + color: 'var(--color-data-categorical-orange, #EB6E00)', + }, + { + label: 'APAC', + value: 22, + color: 'var(--color-data-categorical-green, #0B991F)', + }, + { + label: 'LATAM', + value: 8, + color: 'var(--color-data-categorical-purple, #6B1EFD)', + }, + {label: 'Other', value: 4, color: 'var(--color-data-neutral, #8494A3)'}, +]; + +const roleData = [ + { + label: 'Engineer', + value: 45, + color: 'var(--color-data-categorical-blue, #0171E3)', + }, + { + label: 'Manager', + value: 20, + color: 'var(--color-data-categorical-orange, #EB6E00)', + }, + { + label: 'Designer', + value: 15, + color: 'var(--color-data-categorical-green, #0B991F)', + }, + { + label: 'Data Scientist', + value: 12, + color: 'var(--color-data-categorical-purple, #6B1EFD)', + }, + {label: 'Other', value: 8, color: 'var(--color-data-neutral, #8494A3)'}, +]; + +// Engagement — Top pages +interface PageRow extends Record { + id: string; + page: string; + views: number; + newUsers: string; + avgTime: string; + exits: string; +} + +const topPagesData: PageRow[] = [ + { + id: '1', + page: '/home', + views: 8420, + newUsers: '62.3%', + avgTime: '3:42', + exits: '18.5%', + }, + { + id: '2', + page: '/products', + views: 6150, + newUsers: '45.1%', + avgTime: '4:15', + exits: '22.8%', + }, + { + id: '3', + page: '/pricing', + views: 4830, + newUsers: '38.7%', + avgTime: '2:58', + exits: '35.2%', + }, + { + id: '4', + page: '/blog', + views: 3920, + newUsers: '71.4%', + avgTime: '5:30', + exits: '12.1%', + }, + { + id: '5', + page: '/docs', + views: 3410, + newUsers: '29.8%', + avgTime: '6:12', + exits: '8.4%', + }, + { + id: '6', + page: '/about', + views: 2980, + newUsers: '55.6%', + avgTime: '2:15', + exits: '28.3%', + }, + { + id: '7', + page: '/contact', + views: 2540, + newUsers: '48.2%', + avgTime: '1:48', + exits: '41.7%', + }, + { + id: '8', + page: '/changelog', + views: 2210, + newUsers: '22.1%', + avgTime: '4:55', + exits: '15.6%', + }, + { + id: '9', + page: '/support', + views: 1870, + newUsers: '59.3%', + avgTime: '3:22', + exits: '30.9%', + }, + { + id: '10', + page: '/careers', + views: 1520, + newUsers: '83.1%', + avgTime: '2:34', + exits: '45.2%', + }, +]; + +const topPagesMaxViews = Math.max(...topPagesData.map(d => d.views)); + +const topPagesColumns: TableColumn[] = [ + {key: 'page', header: 'Page', width: pixel(160)}, + { + key: 'views', + header: 'Views', + width: proportional(1), + renderCell: (item: PageRow) => ( + + + {item.views.toLocaleString()} views + + ), + }, + {key: 'newUsers', header: 'New Users', width: pixel(120)}, + {key: 'avgTime', header: 'Avg. Time', width: pixel(120)}, +]; + +// Engagement — Top events +interface EventRow extends Record { + id: string; + event: string; + count: number; + users: number; + newUsers: number; +} + +const topEventsData: EventRow[] = [ + {id: '1', event: 'page_view', count: 18420, users: 12300, newUsers: 4920}, + {id: '2', event: 'session_start', count: 14850, users: 9870, newUsers: 3950}, + {id: '3', event: 'first_visit', count: 8230, users: 8230, newUsers: 8230}, + {id: '4', event: 'user_engagement', count: 6120, users: 4510, newUsers: 1580}, + {id: '5', event: 'click', count: 3540, users: 2680, newUsers: 940}, + {id: '6', event: 'scroll', count: 2910, users: 2140, newUsers: 750}, + {id: '7', event: 'form_submit', count: 1870, users: 1350, newUsers: 540}, + {id: '8', event: 'video_play', count: 1240, users: 980, newUsers: 390}, + {id: '9', event: 'search', count: 960, users: 720, newUsers: 290}, + {id: '10', event: 'share', count: 580, users: 410, newUsers: 160}, +]; + +const topEventsMaxCount = Math.max(...topEventsData.map(d => d.count)); + +const topEventsColumns: TableColumn[] = [ + {key: 'event', header: 'Event', width: pixel(160)}, + { + key: 'count', + header: 'Count', + width: proportional(1), + renderCell: (item: EventRow) => ( + + + {item.count.toLocaleString()} + + ), + }, + { + key: 'users', + header: 'Users', + width: pixel(120), + renderCell: (item: EventRow) => item.users.toLocaleString(), + }, + { + key: 'newUsers', + header: 'New Users', + width: pixel(120), + renderCell: (item: EventRow) => item.newUsers.toLocaleString(), + }, +]; + +// ============= CHART COMPONENTS ============= + +// Chart line colors via Astryx design tokens (CSS custom properties) +const chartColors = { + allUsers: 'var(--color-data-categorical-blue, #0171E3)', + desktop: 'var(--color-data-categorical-orange, #EB6E00)', + mobile: 'var(--color-data-categorical-purple, #6B1EFD)', +}; + +function ChartLegendItem({color, label}: {color: string; label: string}) { + return ( + + + + {label} + + + ); +} + +function ChartTooltip({ + active, + payload, + label, +}: { + active?: boolean; + payload?: Array<{name: string; value: number; color: string}>; + label?: number; +}) { + if (!active || !payload?.length) { + return null; + } + const point = activeUsersData.find(d => d.hour === label); + return ( + + + + {point?.label ?? ''} + + {payload.map(entry => ( + + + + {entry.name}: {entry.value} + + + ))} + + + ); +} + +function ActiveUsersChart() { + return ( + + + + + xAxisLabels[v] ?? ''} + tick={{ + fontSize: 'var(--font-size-sm, 12px)', + fill: 'var(--color-text-secondary, #4E606F)', + }} + axisLine={false} + tickLine={false} + /> + + } + cursor={{stroke: 'var(--color-border, rgba(5, 54, 89, 0.1))'}} + /> + + + + + + + + + + + + ); +} + +function Sparkline({data}: {data: number[]}) { + const chartData = data.map((v, i) => ({i, v})); + return ( + + + + + + ); +} + +// ============= CARD COMPONENTS ============= + +function MetricCard({ + label, + value, + change, + positive, + sparkline, +}: { + label: string; + value: string; + change: string; + positive: boolean; + sparkline: number[]; +}) { + return ( + + + {label} + + {value} + + {positive ? ( + + ) : ( + + )} + + {change} + + + + + Last 30 days vs. Previous + + + + + ); +} + +function StackedBarCard({ + title, + data, +}: { + title: string; + data: Array<{label: string; value: number; color: string}>; +}) { + const total = data.reduce((sum, d) => sum + d.value, 0); + // Recharts needs a single data row with each segment as a separate key + const chartData = [Object.fromEntries(data.map(d => [d.label, d.value]))]; + + return ( + + + {title} + + + + + {data.map((d, i) => ( + + ))} + + + {/* Legend */} + + {data.map(d => ( + + + + {d.label} + + + {d.value} - {((d.value / total) * 100).toFixed(2)}% + + + ))} + + + + ); +} + +// ============= TABLE COMPONENTS ============= + +function TableCard({ + title, + linkLabel, + linkHref, + data, + columns, +}: { + title: string; + linkLabel: string; + linkHref: string; + data: T[]; + columns: TableColumn[]; +}) { + return ( + + + + {title} + {linkLabel} + + + data={data} + columns={columns} + idKey="id" + density="compact" + dividers="rows" + hasHover + /> + + + ); +} + +// ============= SIDENAV ============= + +// ============= MAIN COMPONENT ============= + +export default function DashboardTemplate() { + return ( + + + {/* Active Users Chart */} + + + Active users +
); } return ( - - + Doormile - + ); } diff --git a/src/components/PageHeader.jsx b/src/components/PageHeader.jsx index 0a616e9..356c8a4 100644 --- a/src/components/PageHeader.jsx +++ b/src/components/PageHeader.jsx @@ -1,42 +1,24 @@ -import { Box, Typography, Breadcrumbs, Link, Stack } from '@mui/material'; -import NavigateNextIcon from '@mui/icons-material/NavigateNext'; -import { Link as RouterLink } from 'react-router-dom'; +// ==============================|| PAGE HEADER (title + actions) ||============================== // -// ==============================|| PAGE HEADER (title + breadcrumb + actions) ||============================== // - -export default function PageHeader({ title, breadcrumbs = [], action }) { +export default function PageHeader({ title, action }) { return ( - - - +
+

{title} - - {breadcrumbs.length > 0 && ( - } sx={{ mt: 0.5 }}> - - Home - - {breadcrumbs.map((b, i) => - b.to && i < breadcrumbs.length - 1 ? ( - - {b.label} - - ) : ( - - {b.label} - - ) - )} - - )} - - {action && {action}} - +

+
+ {action &&
{action}
} + ); } diff --git a/src/components/StatCard.jsx b/src/components/StatCard.jsx index 483aca0..b750bb2 100644 --- a/src/components/StatCard.jsx +++ b/src/components/StatCard.jsx @@ -1,83 +1,99 @@ -import { Card, CardContent, Box, Typography, Avatar, Stack } from '@mui/material'; -import ArrowUpwardRoundedIcon from '@mui/icons-material/ArrowUpwardRounded'; -import ArrowDownwardRoundedIcon from '@mui/icons-material/ArrowDownwardRounded'; +import { Card } from '@astryxdesign/core/Card'; // ==============================|| STAT / KPI CARD ||============================== // -export default function StatCard({ title, value, icon: Icon, color = 'primary', trend, caption, accent = false }) { +export default function StatCard({ title, value, icon: Icon, color = 'primary', trend, caption, size = 'md' }) { const trendUp = typeof trend === 'number' ? trend >= 0 : null; + const isCompact = size === 'sm'; + + // Map theme colors to CSS values + const colorMap = { + primary: { main: '#C01227', light: 'rgba(192, 18, 39, 0.04)', border: 'rgba(192, 18, 39, 0.1)' }, + secondary: { main: '#475569', light: 'rgba(71, 85, 105, 0.04)', border: 'rgba(71, 85, 105, 0.1)' }, + success: { main: '#10b981', light: 'rgba(16, 185, 129, 0.04)', border: 'rgba(16, 185, 129, 0.1)' }, + warning: { main: '#f59e0b', light: 'rgba(245, 158, 11, 0.04)', border: 'rgba(245, 158, 11, 0.1)' }, + error: { main: '#ef4444', light: 'rgba(239, 68, 68, 0.04)', border: 'rgba(239, 68, 68, 0.1)' }, + info: { main: '#3b82f6', light: 'rgba(59, 130, 246, 0.04)', border: 'rgba(59, 130, 246, 0.1)' } + }; + + const themeColor = colorMap[color] || colorMap.primary; return ( `linear-gradient(145deg, ${theme.palette.background.paper} 0%, ${theme.palette[color].lighter}15 100%)` + cursor: 'pointer', + boxSizing: 'border-box' }} + className="stat-card" > {Icon && ( - - - +
+ +
)} - - - - {title} - - {Icon && ( - + + {title} + + {Icon && ( +
+ +
+ )} + + +
+ + {value} + +
+ + {(trendUp !== null || caption) && ( +
+ {trendUp !== null && ( +
- - + + {trendUp ? '↑' : '↓'} {Math.abs(trend)}% + +
)} - - - - - {value} - - - - {(trendUp !== null || caption) && ( - - {trendUp !== null && ( - - {trendUp ? ( - - ) : ( - - )} - - {Math.abs(trend)}% - - - )} - {caption && ( - - {caption} - - )} - - )} - + {caption && ( + + {caption} + + )} +
+ )}
); } diff --git a/src/components/StatusChip.jsx b/src/components/StatusChip.jsx index 318571d..a466a49 100644 --- a/src/components/StatusChip.jsx +++ b/src/components/StatusChip.jsx @@ -1,65 +1,56 @@ -import { Chip } from '@mui/material'; +import { Badge } from '@astryxdesign/core/Badge'; // ==============================|| STATUS CHIP ||============================== // // Soft-filled status chips used across orders, deliveries, riders, invoices. const MAP = { // orders / deliveries - pending: { color: 'warning', label: 'Pending' }, - created: { color: 'info', label: 'Created' }, - assigned: { color: 'info', label: 'Assigned' }, - accepted: { color: 'info', label: 'Accepted' }, - arrived: { color: 'info', label: 'Arrived' }, - picked: { color: 'primary', label: 'Picked' }, - 'in-transit': { color: 'info', label: 'In Transit' }, - active: { color: 'primary', label: 'Active' }, - delivered: { color: 'success', label: 'Delivered' }, - completed: { color: 'success', label: 'Completed' }, - skipped: { color: 'warning', label: 'Skipped' }, - failed: { color: 'error', label: 'Failed' }, - cancelled: { color: 'error', label: 'Cancelled' }, + pending: { variant: 'warning', label: 'Pending' }, + created: { variant: 'info', label: 'Created' }, + assigned: { variant: 'info', label: 'Assigned' }, + accepted: { variant: 'info', label: 'Accepted' }, + arrived: { variant: 'info', label: 'Arrived' }, + picked: { variant: 'red', label: 'Picked' }, + 'in-transit': { variant: 'info', label: 'In Transit' }, + active: { variant: 'red', label: 'Active' }, + delivered: { variant: 'success', label: 'Delivered' }, + completed: { variant: 'success', label: 'Completed' }, + skipped: { variant: 'warning', label: 'Skipped' }, + failed: { variant: 'error', label: 'Failed' }, + cancelled: { variant: 'error', label: 'Cancelled' }, + 'miler-assigned': { variant: 'info', label: 'Miler Assigned' }, + 'pending-pickup': { variant: 'warning', label: 'Pending Pickup' }, // clients (doormile_clients) - newclient: { color: 'info', label: 'New Client' }, - contacted: { color: 'warning', label: 'Contacted' }, - onboarded: { color: 'success', label: 'Onboarded' }, - lost: { color: 'error', label: 'Lost' }, + newclient: { variant: 'info', label: 'New Client' }, + contacted: { variant: 'warning', label: 'Contacted' }, + onboarded: { variant: 'success', label: 'Onboarded' }, + lost: { variant: 'error', label: 'Lost' }, // team users (doormile_auth) - admin: { color: 'primary', label: 'Admin' }, - rep: { color: 'info', label: 'Rep' }, - manager: { color: 'success', label: 'Manager' }, - support: { color: 'warning', label: 'Support' }, + admin: { variant: 'red', label: 'Admin' }, + rep: { variant: 'info', label: 'Rep' }, + manager: { variant: 'success', label: 'Manager' }, + support: { variant: 'warning', label: 'Support' }, // riders / tenants - online: { color: 'success', label: 'Online' }, - offline: { color: 'default', label: 'Offline' }, - 'on-delivery': { color: 'info', label: 'On Delivery' }, - inactive: { color: 'default', label: 'Inactive' }, + online: { variant: 'success', label: 'Online' }, + offline: { variant: 'neutral', label: 'Offline' }, + 'on-delivery': { variant: 'info', label: 'On Delivery' }, + inactive: { variant: 'neutral', label: 'Inactive' }, // invoices - paid: { color: 'success', label: 'Paid' }, - open: { color: 'info', label: 'Open' }, - overdue: { color: 'error', label: 'Overdue' }, - prepaid: { color: 'success', label: 'Prepaid' }, - cod: { color: 'warning', label: 'COD' } + paid: { variant: 'success', label: 'Paid' }, + open: { variant: 'info', label: 'Open' }, + overdue: { variant: 'error', label: 'Overdue' }, + prepaid: { variant: 'success', label: 'Prepaid' }, + cod: { variant: 'warning', label: 'COD' } }; -const TONE = { - success: { bg: '#E3F6EC', fg: '#00773B' }, - warning: { bg: '#FFF7E0', fg: '#8A6500' }, - info: { bg: '#E0F7F8', fg: '#00727B' }, - error: { bg: '#FEEAE9', fg: '#A82216' }, - primary: { bg: '#F8E0E3', fg: '#9E0E20' }, - default: { bg: '#F0F0F0', fg: '#595959' } -}; - -export default function StatusChip({ status, size = 'small', label, sx }) { +export default function StatusChip({ status, label }) { const key = String(status || '').toLowerCase().replace(/\s+/g, '-'); const humanized = String(status || '').replace(/[_-]+/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); - const cfg = MAP[key] || { color: 'default', label: humanized || status }; - const tone = TONE[cfg.color] || TONE.default; + const cfg = MAP[key] || { variant: 'neutral', label: humanized || status }; return ( - ); } diff --git a/src/components/TabLabelCount.jsx b/src/components/TabLabelCount.jsx index 8fe1b99..0dab8a0 100644 --- a/src/components/TabLabelCount.jsx +++ b/src/components/TabLabelCount.jsx @@ -1,4 +1,4 @@ -import { Stack, Box } from '@mui/material'; +import { Badge } from '@astryxdesign/core/Badge'; // ==============================|| TAB LABEL WITH INLINE COUNT PILL ||============================== // // Renders a tab label with the count laid out inline (not an overlapping Badge), @@ -6,25 +6,9 @@ import { Stack, Box } from '@mui/material'; export default function TabLabelCount({ label, count, active = false }) { return ( - +
{label} - - {count} - - + +
); } diff --git a/src/components/UserAvatar.jsx b/src/components/UserAvatar.jsx index 1e150f6..4a35d02 100644 --- a/src/components/UserAvatar.jsx +++ b/src/components/UserAvatar.jsx @@ -1,12 +1,9 @@ -import { Avatar } from '@mui/material'; -import { stringToColor, initials } from '@/utils/format'; +import { Avatar } from '@astryxdesign/core/Avatar'; // ==============================|| INITIALS AVATAR ||============================== // -export default function UserAvatar({ name = '', size = 32, sx }) { +export default function UserAvatar({ name = '', size = 32 }) { return ( - - {initials(name)} - + ); } diff --git a/src/components/charts/DonutChart.jsx b/src/components/charts/DonutChart.jsx index b85c52a..f726c0d 100644 --- a/src/components/charts/DonutChart.jsx +++ b/src/components/charts/DonutChart.jsx @@ -1,5 +1,3 @@ -import { Box, Stack, Typography } from '@mui/material'; - // Dependency-free donut chart. data: [{ label, value, color }] export default function DonutChart({ data = [], size = 180, thickness = 26, centerLabel, centerValue }) { const total = data.reduce((s, d) => s + d.value, 0) || 1; @@ -8,14 +6,18 @@ export default function DonutChart({ data = [], size = 180, thickness = 26, cent let offset = 0; return ( - - +
@@ -40,20 +42,20 @@ export default function DonutChart({ data = [], size = 180, thickness = 26, cent })} - - {centerValue ?? total} - {centerLabel ?? 'Total'} - - - +
+
{centerValue ?? total}
+
{centerLabel ?? 'Total'}
+
+
+
{data.map((d) => ( - - - {d.label} - {d.value.toLocaleString('en-IN')} - +
+
+
{d.label}
+
{d.value.toLocaleString('en-IN')}
+
))} - - +
+
); } diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..64282c5 --- /dev/null +++ b/src/index.css @@ -0,0 +1,177 @@ +/* ─── Force light mode + brand color: override every Astryx light-dark() token ─── */ +/* Astryx's renders a wrapper div carrying [data-astryx-theme] in addition to + syncing the attribute onto ; theme.css sets tokens directly on that wrapper via + @scope, so a direct-on-element declaration beats one merely inherited from :root + regardless of layers. Targeting [data-astryx-theme] here (not just :root) ensures our + overrides win on the actual element the tokens are read from. */ +:root, +[data-astryx-theme] { + color-scheme: light; + + /* Surface / background tokens */ + --color-background-surface: #ffffff; + --color-background-body: #f1f4f7; + --color-background-card: #ffffff; + --color-background-popover: #ffffff; + --color-background-muted: rgba(5, 54, 89, 0.047); + --color-background-inverted: #0A1317; + --color-background-error-inverted: #AA071E; + + /* Text tokens */ + --color-text-primary: #0A1317; + --color-text-secondary: #4E606F; + --color-text-disabled: #A4B0BC; + --color-text-accent: #C01227; + + /* Icon tokens */ + --color-icon-primary: #0A1317; + --color-icon-secondary: #4E606F; + --color-icon-disabled: #A4B0BC; + --color-icon-accent: #C01227; + + /* Border tokens */ + --color-border: rgba(5, 54, 89, 0.1); + --color-border-emphasized: #CCD3DB; + + /* Interactive overlay tokens */ + --color-neutral: rgba(5, 54, 89, 0.1); + --color-overlay: rgba(1, 18, 40, 0.4); + --color-overlay-hover: rgba(5, 54, 89, 0.047); + --color-overlay-pressed: rgba(5, 54, 89, 0.098); + + /* Misc */ + --color-skeleton: #CCD3DB; + --color-track: #CCD3DB; + --color-shadow: rgba(5, 54, 89, 0.1); + + /* Brand accent — Doormile red, replaces the theme-neutral default */ + --color-accent: #C01227; + --color-accent-muted: rgba(192, 18, 39, 0.12); + --color-on-accent: #ffffff; + + /* App-level overrides */ + background-color: #f8fafc; + color: #0f172a; +} + +/* Apply professional font styling globally */ +*, *::before, *::after { + font-family: 'Public Sans', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + background-color: #f8fafc; + color: #334155; /* slate-700 for highly readable, professional body text */ +} + +/* Header typography styling for a crisp, modern look */ +h1, h2, h3, h4, h5, h6 { + font-weight: 700; + color: #0f172a; /* slate-900 for bold headers */ + letter-spacing: -0.02em; +} + +/* All native inputs and textareas — catches every Astryx TextInput */ +input, textarea { + font-family: inherit !important; + background-color: #ffffff !important; + color: #0f172a !important; +} + +input::placeholder, textarea::placeholder { + color: #94a3b8 !important; + opacity: 1 !important; +} + +/* Base input overrides for custom plain inputs (excluding Astryx styled components) */ +input:not(.search-bar-input):not([class]), select:not([class]), textarea:not([class]) { + background-color: #ffffff !important; + color: #0f172a !important; + border: 1px solid rgba(5, 54, 89, 0.12) !important; + border-radius: 8px !important; + padding: 10px 14px !important; + font-size: 0.875rem !important; + outline: none !important; + transition: border-color 0.2s, box-shadow 0.2s !important; + box-sizing: border-box !important; + width: 100% !important; +} + +input:not(.search-bar-input):not([class]), select:not([class]) { + height: 38px !important; +} + +input:not(.search-bar-input):not([class]):focus, select:not([class]):focus, textarea:not([class]):focus { + border-color: #C01227 !important; + box-shadow: 0 0 0 3px rgba(192, 18, 39, 0.12) !important; +} + +/* Premium Select Dropdown Styling */ +select:not([class]) { + appearance: none !important; + background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%2364748b'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpolyline%20points='6%209%2012%2015%2018%209'%3E%3C/polyline%3E%3C/svg%3E") !important; + background-repeat: no-repeat !important; + background-position: right 12px center !important; + background-size: 16px !important; + padding-right: 40px !important; + cursor: pointer !important; +} + +/* Premium Search Bar Custom Styling */ +.search-bar-form { + display: flex; + align-items: center; + background-color: #ffffff !important; + border: 1px solid #cbd5e1; + border-radius: 9999px; + padding: 8px 16px; + width: 100%; + max-width: 320px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-sizing: border-box; +} + +.search-bar-form:focus-within { + border-color: #C01227; + box-shadow: 0 0 0 4px rgba(192, 18, 39, 0.12), 0 10px 15px -3px rgba(0, 0, 0, 0.05); + max-width: 380px; /* Expand slightly on focus for premium desktop feel */ +} + +.search-bar-input { + border: none !important; + background: transparent !important; + outline: none !important; + font-size: 0.875rem !important; + color: #0f172a !important; + width: 100% !important; + box-shadow: none !important; + padding: 0 !important; +} + +/* Table and interactive enhancements */ +.table-row-hover:hover { + background-color: #f8fafc !important; +} + +/* Pagination rows per page dropdown */ +.rpp-select { + appearance: none !important; + -webkit-appearance: none !important; + width: 75px !important; + height: 32px !important; + padding: 4px 24px 4px 8px !important; + border-radius: 6px !important; + border: 1px solid rgba(5, 54, 89, 0.12) !important; + background-color: #ffffff !important; + color: #0f172a !important; + font-size: 0.875rem !important; + outline: none !important; + cursor: pointer !important; + background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%2364748b'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpolyline%20points='6%209%2012%2015%2018%209'%3E%3C/polyline%3E%3C/svg%3E") !important; + background-repeat: no-repeat !important; + background-position: right 8px center !important; + background-size: 12px !important; +} diff --git a/src/layout/MainLayout/Header.jsx b/src/layout/MainLayout/Header.jsx index f25b3d2..98057d8 100644 --- a/src/layout/MainLayout/Header.jsx +++ b/src/layout/MainLayout/Header.jsx @@ -1,242 +1,79 @@ -import { useState, useRef, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { - AppBar, - Toolbar, - IconButton, - Box, - InputBase, - Avatar, - Typography, - Stack, - Menu, - MenuItem, - Divider, - ListItemIcon, - Popper, - Paper, - ClickAwayListener, - CircularProgress, - alpha -} from '@mui/material'; -import MenuIcon from '@mui/icons-material/Menu'; -import SearchIcon from '@mui/icons-material/Search'; -import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined'; -import LogoutIcon from '@mui/icons-material/Logout'; +import { Settings, LogOut } from 'lucide-react'; +import { TopNav, TopNavHeading } from '@astryxdesign/core/TopNav'; +import { DropdownMenu } from '@astryxdesign/core/DropdownMenu'; import Logo from '@/components/Logo'; import UserAvatar from '@/components/UserAvatar'; -import { fetchClients, fetchUsers } from '@/utils/apiClient'; -import { toClient } from '@/utils/mappers'; +import { fetchUsers } from '@/utils/apiClient'; -const RED = '#C01227'; - -export default function Header({ onToggle }) { +export default function Header() { const navigate = useNavigate(); - const [account, setAccount] = useState(null); - const [search, setSearch] = useState(''); - - // Read user from localStorage + let storedUserObj = { name: 'Admin', role: 'Operations Admin', id: 0 }; try { const storedUser = localStorage.getItem('user'); - if (storedUser) { - storedUserObj = JSON.parse(storedUser); - } - } catch(e) {} - + if (storedUser) storedUserObj = JSON.parse(storedUser); + } catch (e) {} + const [activeUserName, setActiveUserName] = useState(storedUserObj.name || 'Admin'); - - const displayRole = storedUserObj.role === 'admin' ? 'Administrator' : + const displayRole = storedUserObj.role === 'admin' ? 'Administrator' : storedUserObj.role === 'manager' ? 'Manager' : storedUserObj.role === 'executive' ? 'Executive' : 'Operations Admin'; - const displayInitial = activeUserName.charAt(0).toUpperCase(); - const searchRef = useRef(null); - const [clients, setClients] = useState([]); - const [loadedClients, setLoadedClients] = useState(false); - const [loadingClients, setLoadingClients] = useState(false); - const [openResults, setOpenResults] = useState(false); - - // Fetch users to get the real name if the login payload didn't have it useEffect(() => { fetchUsers().then((users) => { if (storedUserObj.email) { - // Strictly use email to match, as stored ID might belong to the doormile_auth table instead of appusers table const matchingUser = users.find(u => u.email === storedUserObj.email); if (matchingUser && matchingUser.first_name) { setActiveUserName(matchingUser.first_name); - // Also update localStorage so it's fresh - const updatedUser = { ...storedUserObj, name: matchingUser.first_name }; - localStorage.setItem('user', JSON.stringify(updatedUser)); + localStorage.setItem('user', JSON.stringify({ ...storedUserObj, name: matchingUser.first_name })); } } }).catch(console.error); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const ensureClients = () => { - if (loadedClients || loadingClients) return; - setLoadingClients(true); - fetchClients() - .then((points) => setClients(points.map(toClient))) - .catch(() => {}) - .finally(() => { setLoadedClients(true); setLoadingClients(false); }); - }; - - const q = search.trim().toLowerCase(); - const results = q - ? clients.filter((c) => [c.name, c.city, c.businessType, c.phone].join(' ').toLowerCase().includes(q)).slice(0, 6) - : []; - - const onSearchChange = (e) => { - setSearch(e.target.value); - ensureClients(); - setOpenResults(true); - }; - - const goToClients = (term) => { - navigate(`/tenants?q=${encodeURIComponent(term)}`); - setSearch(''); - setOpenResults(false); - }; - - const submitSearch = (e) => { - e.preventDefault(); - const term = search.trim(); - if (term) goToClients(term); - }; - return ( - t.zIndex.drawer + 1, borderBottom: 1, borderColor: 'divider' }} - > - - - - - - navigate('/dashboard')} - sx={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }} - > - - - - - - setOpenResults(false)}> - - - - { ensureClients(); if (search.trim()) setOpenResults(true); }} - placeholder="Search clients…" - sx={{ color: 'grey.800', fontSize: '0.875rem', flex: 1, '&::placeholder': { color: 'grey.500' } }} - inputProps={{ 'aria-label': 'search' }} - /> - - - - - {loadingClients && results.length === 0 ? ( - - ) : results.length === 0 ? ( - - No clients match “{search.trim()}”. - - ) : ( - <> - {results.map((c) => ( - goToClients(c.name)} sx={{ py: 1, gap: 1.25 }}> - - - {c.name} - - {[c.businessType, c.city].filter(Boolean).join(' · ') || c.phone} - - - - ))} - - goToClients(search.trim())} sx={{ py: 1.25, color: 'primary.main', fontWeight: 600 }}> - - See all results for “{search.trim()}” - - - )} - - - - - - setAccount(e.currentTarget)} - sx={{ display: 'flex', alignItems: 'center', gap: 1, ml: 0.5, cursor: 'pointer', py: 0.5, px: 0.5, borderRadius: 2, '&:hover': { bgcolor: 'grey.100' } }} - > - {displayInitial} - - - {activeUserName} - - - {displayRole} - - - - - setAccount(null)} - transformOrigin={{ horizontal: 'right', vertical: 'top' }} - anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} - PaperProps={{ sx: { mt: 1, minWidth: 220 } }} - > - - - {displayInitial} - - {activeUserName} - {displayRole} - - - - - { setAccount(null); navigate('/settings'); }}> - - Settings - - - { setAccount(null); localStorage.removeItem('auth_token'); navigate('/login'); }} sx={{ color: 'error.main' }}> - - Logout - - - - + <> + } + heading="Doormile CRM" + subheading={displayRole} + headingHref="/dashboard" + /> + } + endContent={ +
+ , + label: activeUserName + }} + items={[ + { label: 'Settings', icon: Settings, onClick: () => navigate('/settings') }, + { type: 'divider' }, + { + label: 'Logout', + icon: LogOut, + onClick: () => { localStorage.removeItem('auth_token'); navigate('/login'); } + } + ]} + /> +
+ } + /> + ); } diff --git a/src/layout/MainLayout/Sidebar.jsx b/src/layout/MainLayout/Sidebar.jsx index 102d543..a33bc85 100644 --- a/src/layout/MainLayout/Sidebar.jsx +++ b/src/layout/MainLayout/Sidebar.jsx @@ -1,204 +1,158 @@ -import { useState } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; -import { - Drawer, - Box, - List, - ListItemButton, - ListItemIcon, - ListItemText, - Typography, - Collapse, - Tooltip, - Toolbar, - alpha -} from '@mui/material'; -import ExpandLess from '@mui/icons-material/ExpandLess'; -import ExpandMore from '@mui/icons-material/ExpandMore'; -import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'; +import { useLocation } from 'react-router-dom'; +import { SideNav, SideNavSection, SideNavItem } from '@astryxdesign/core/SideNav'; +import { Text } from '@astryxdesign/core/Text'; import navItems from '@/menu/navItems'; -import Logo from '@/components/Logo'; -export const DRAWER_WIDTH = 264; -export const MINI_WIDTH = 78; +// ==============================|| DOORMILE - SIDE NAV ||============================== // +// Thin wrapper around Astryx's SideNav template: sections + items driven by navItems. +// Branding lives in the TopNav heading, so this stays icon+label only (Astryx guidance: +// avoid a SideNavHeading when TopNav already carries app identity). -const RED = '#C01227'; - -function NavLeaf({ item, open, active, depth = 0, onClick }) { - const Icon = item.icon; - const button = ( - - - {depth > 0 && !Icon ? : Icon ? : null} - - {open && ( - - )} - - ); - - return open ? button : {button}; -} - -export default function Sidebar({ open, mobileOpen, onMobileClose, isMobile }) { +export default function Sidebar() { const location = useLocation(); - const navigate = useNavigate(); - const isActive = (url) => url && location.pathname.startsWith(url); - const expanded = open || isMobile; - - const initialOpen = navItems - .flatMap((g) => g.items) - .filter((i) => i.children && i.children.some((c) => isActive(c.url))) - .map((i) => i.id); - const [collapse, setCollapse] = useState(initialOpen); - - const go = (url) => { - navigate(url); - if (isMobile) onMobileClose(); - }; - - const content = ( - - - - - - {navItems.map((grp) => ( - - {expanded && ( - - {grp.group} - - )} - - {grp.items.map((item) => { - if (item.children) { - const opened = collapse.includes(item.id); - const childActive = item.children.some((c) => isActive(c.url)); - const Icon = item.icon; - const head = ( - - expanded - ? setCollapse((p) => (p.includes(item.id) ? p.filter((x) => x !== item.id) : [...p, item.id])) - : go(item.children[0].url) - } - sx={{ - minHeight: 44, - my: 0.25, - mx: expanded ? 1 : 0.75, - px: expanded ? 1.5 : 0, - justifyContent: expanded ? 'flex-start' : 'center', - borderRadius: 2, - color: childActive ? RED : 'grey.700', - bgcolor: childActive && !opened ? alpha(RED, 0.08) : 'transparent', - '& .MuiListItemIcon-root': { color: childActive ? RED : 'grey.500' }, - '&:hover': { bgcolor: alpha(RED, 0.04), color: RED, '& .MuiListItemIcon-root': { color: RED } } - }} - > - - - - {expanded && ( - <> - - {opened ? : } - - )} - - ); - return ( - - {expanded ? head : {head}} - {expanded && ( - - - {item.children.map((c) => ( - go(c.url)} /> - ))} - - - )} - - ); - } - return ( - go(item.url)} /> - ); - })} - - - ))} - - {expanded && ( - - - Doormile CRM v1.0 - - - )} - - ); - - if (isMobile) { - return ( - - {content} - - ); - } + const isActive = (url) => !!url && location.pathname.startsWith(url); return ( - t.transitions.create('width', { duration: t.transitions.duration.standard }) + <> + + {navItems.map((grp) => ( + + {grp.items.map((item) => ( + + ))} + + ))} + + + ); } diff --git a/src/layout/MainLayout/index.jsx b/src/layout/MainLayout/index.jsx index 5caab1b..5504d9f 100644 --- a/src/layout/MainLayout/index.jsx +++ b/src/layout/MainLayout/index.jsx @@ -1,45 +1,36 @@ -import { useState } from 'react'; import { Outlet } from 'react-router-dom'; -import { Box, Toolbar, useMediaQuery } from '@mui/material'; -import { useTheme } from '@mui/material/styles'; +import { AppShell } from '@astryxdesign/core/AppShell'; import Header from './Header'; -import Sidebar, { DRAWER_WIDTH, MINI_WIDTH } from './Sidebar'; +import Sidebar from './Sidebar'; + +// ==============================|| DOORMILE - APP SHELL ||============================== // +// Astryx AppShell owns the responsive chrome: sticky top nav, collapsible side nav, +// and the auto-generated mobile drawer. Pages keep their own internal padding. export default function MainLayout() { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('lg')); - const [open, setOpen] = useState(true); - const [mobileOpen, setMobileOpen] = useState(false); - - const toggle = () => { - if (isMobile) setMobileOpen((p) => !p); - else setOpen((p) => !p); - }; - return ( - -
- setMobileOpen(false)} - /> - - - - - - - + } + sideNav={} + mobileNav={{ breakpoint: 'lg' }} + > +
+ +
+ +
); } diff --git a/src/layout/MinimalLayout.jsx b/src/layout/MinimalLayout.jsx index d1357cf..3d359b2 100644 --- a/src/layout/MinimalLayout.jsx +++ b/src/layout/MinimalLayout.jsx @@ -1,11 +1,10 @@ import { Outlet } from 'react-router-dom'; -import { Box } from '@mui/material'; // Used by auth + maintenance pages — full-bleed, no shell. export default function MinimalLayout() { return ( - +
- +
); } diff --git a/src/main.jsx b/src/main.jsx index 3997aa9..24018a1 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,22 +1,28 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import { BrowserRouter } from 'react-router-dom'; -import { ThemeProvider, CssBaseline } from '@mui/material'; -import { LocalizationProvider } from '@mui/x-date-pickers'; -import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { BrowserRouter, Link } from 'react-router-dom'; -import theme from '@/theme'; +// Astryx UI CSS +import '@astryxdesign/core/reset.css'; +import '@astryxdesign/core/astryx.css'; +import '@astryxdesign/theme-neutral/theme.css'; +import './index.css'; + +import { Theme } from '@astryxdesign/core'; +import { LinkProvider } from '@astryxdesign/core/Link'; +import { neutralTheme } from '@astryxdesign/theme-neutral/built'; import App from '@/App'; ReactDOM.createRoot(document.getElementById('root')).render( - - - - + + + {/* Makes every Astryx nav/link component (SideNavItem, TopNavHeading, DropdownMenu, ...) + route through React Router's Link instead of a hard reload. */} + - - - + + + ); diff --git a/src/menu/navItems.jsx b/src/menu/navItems.jsx index 6a4f4b8..181ad1a 100644 --- a/src/menu/navItems.jsx +++ b/src/menu/navItems.jsx @@ -1,8 +1,4 @@ -import DashboardOutlinedIcon from '@mui/icons-material/DashboardOutlined'; -import ApartmentOutlinedIcon from '@mui/icons-material/ApartmentOutlined'; -import GroupsOutlinedIcon from '@mui/icons-material/GroupsOutlined'; -import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined'; -import AttachMoneyOutlinedIcon from '@mui/icons-material/AttachMoneyOutlined'; +import { LayoutDashboard, Building2, Users, Settings, DollarSign, Calendar } from 'lucide-react'; // ==============================|| DOORMILE - SIDEBAR NAV CONFIG ||============================== // @@ -10,18 +6,18 @@ const navItems = [ { group: 'CRM', items: [ - { id: 'dashboard', title: 'Dashboard', url: '/dashboard', icon: DashboardOutlinedIcon }, - { id: 'tenants', title: 'Clients', url: '/tenants', icon: ApartmentOutlinedIcon }, - { id: 'survey', title: 'Providers', url: '/survey', icon: DashboardOutlinedIcon }, - { id: 'pricing', title: 'Pricing Matrix', url: '/pricing', icon: AttachMoneyOutlinedIcon }, - { id: 'bookings', title: 'Bookings', url: '/bookings', icon: DashboardOutlinedIcon }, - { id: 'team-users', title: 'App Users', url: '/team-users', icon: GroupsOutlinedIcon } + { id: 'dashboard', title: 'Dashboard', url: '/dashboard', icon: LayoutDashboard }, + { id: 'tenants', title: 'Clients', url: '/clients', icon: Building2 }, + { id: 'survey', title: 'Providers', url: '/survey', icon: Users }, + { id: 'pricing', title: 'Pricing Matrix', url: '/pricing', icon: DollarSign }, + { id: 'bookings', title: 'Bookings', url: '/bookings', icon: Calendar }, + { id: 'team-users', title: 'App Users', url: '/team-users', icon: Users } ] }, { group: 'System', items: [ - { id: 'settings', title: 'Settings', url: '/settings', icon: SettingsOutlinedIcon } + { id: 'settings', title: 'Settings', url: '/settings', icon: Settings } ] } ]; diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx index d6bc483..1160798 100644 --- a/src/pages/Dashboard.jsx +++ b/src/pages/Dashboard.jsx @@ -1,18 +1,24 @@ -import { useState, useEffect, useMemo } from 'react'; +import { useState, useEffect, useMemo, useRef } from 'react'; +import { useNavigate } from 'react-router-dom'; +import dayjs from 'dayjs'; import { - Grid, Card, Box, Stack, Typography, Button, Divider, LinearProgress, CircularProgress, Alert, - Table, TableBody, TableCell, TableHead, TableRow, TableContainer, useMediaQuery -} from '@mui/material'; -import { useTheme } from '@mui/material/styles'; -import RefreshIcon from '@mui/icons-material/Refresh'; -import ApartmentOutlinedIcon from '@mui/icons-material/ApartmentOutlined'; -import FiberNewOutlinedIcon from '@mui/icons-material/FiberNewOutlined'; -import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined'; -import HandshakeOutlinedIcon from '@mui/icons-material/HandshakeOutlined'; -import HistoryOutlinedIcon from '@mui/icons-material/HistoryOutlined'; -import DonutLargeOutlinedIcon from '@mui/icons-material/DonutLargeOutlined'; -import CategoryOutlinedIcon from '@mui/icons-material/CategoryOutlined'; -import GroupsOutlinedIcon from '@mui/icons-material/GroupsOutlined'; + Building2, + UserPlus, + Package, + Handshake, + History, + PieChart, + Layers, + Users, + ArrowRight, + Calendar +} from 'lucide-react'; +import { Button } from '@astryxdesign/core/Button'; +import { Card } from '@astryxdesign/core/Card'; +import { Table, proportional, pixel } from '@astryxdesign/core/Table'; +import { Badge } from '@astryxdesign/core/Badge'; +import { Text, Heading } from '@astryxdesign/core/Text'; +import { Banner } from '@astryxdesign/core/Banner'; import PageHeader from '@/components/PageHeader'; import StatCard from '@/components/StatCard'; @@ -30,50 +36,86 @@ const BAR_COLORS = ['#C01227', '#00A2AE', '#00A854', '#FFBF00', '#9E0E20', '#8C8 function Panel({ icon: Icon, title, action, color = 'primary', noPadding = false, children }) { return ( - - `linear-gradient(90deg, ${theme.palette[color].lighter}66 0%, transparent 100%)` + +
- - - - {title} +
+ +
+
+ {title} +
{action} - - {children} +
+
+ {children} +
); } export default function Dashboard() { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + const navigate = useNavigate(); + const [selectedDate, setSelectedDate] = useState(() => dayjs().format('YYYY-MM-DD')); const [clients, setClients] = useState([]); const [team, setTeam] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + // Guards against out-of-order responses (e.g. React StrictMode's double-invoked + // effect) so a stale request can never clobber a newer one's result. + const loadRequestId = useRef(0); + const load = () => { + const requestId = ++loadRequestId.current; setLoading(true); setError(null); Promise.all([fetchClients(), fetchUsers()]) .then(([cs, us]) => { + if (loadRequestId.current !== requestId) return; setClients((cs || []).map(toClient)); setTeam((us || []).map(toUser)); }) - .catch((e) => setError(e.message || 'Failed to load dashboard data')) - .finally(() => setLoading(false)); + .catch((e) => { + if (loadRequestId.current === requestId) setError(e.message || 'Failed to load dashboard data'); + }) + .finally(() => { + if (loadRequestId.current === requestId) setLoading(false); + }); }; useEffect(() => { load(); }, []); @@ -103,148 +145,273 @@ export default function Dashboard() { [clients] ); - const today = new Date().toLocaleDateString('en-IN', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }); + const recentColumns = useMemo(() => [ + { + key: 'name', + header:
Client
, + width: proportional(2), + renderCell: (c) => ( +
+ + {c.name} +
+ ) + }, + { + key: 'businessType', + header: 'Type', + width: proportional(1), + renderCell: (c) => c.businessType + ? + : + }, + { + key: 'location', + header: 'Location', + width: proportional(1), + renderCell: (c) => ( + {`${c.city || '—'}${c.businessState ? `, ${c.businessState}` : ''}`} + ) + }, + { + key: 'parcelVolume', + header: 'Parcels', + width: pixel(110), + align: 'end', + renderCell: (c) => {c.parcelVolume.toLocaleString('en-IN')} + }, + { + key: 'status', + header: 'Status', + width: pixel(140), + renderCell: (c) => + } + ], []); + + const datePickerAction = ( + setSelectedDate(e.target.value)} + style={{ + padding: '8px 12px', + borderRadius: '10px', + border: '1px solid rgba(5, 54, 89, 0.12)', + fontSize: '0.875rem', + fontWeight: 500, + fontFamily: 'inherit', + color: '#1e293b', + backgroundColor: '#ffffff', + outline: 'none', + cursor: 'pointer', + transition: 'all 0.2s ease', + boxShadow: '0 1px 2px rgba(15, 23, 42, 0.04)', + width: '150px' + }} + onFocus={(e) => { + e.target.style.borderColor = '#C01227'; + e.target.style.boxShadow = '0 0 0 3px rgba(192, 18, 39, 0.12)'; + }} + onBlur={(e) => { + e.target.style.borderColor = 'rgba(5, 54, 89, 0.12)'; + e.target.style.boxShadow = '0 1px 2px rgba(15, 23, 42, 0.04)'; + }} + /> + ); if (loading) { return ( - <> - - - +
+ +
+
+ +
+
); } return ( - <> - } onClick={load}>Refresh} - /> +
+ - {error && Retry}>{error}} + {error && ( +
+ } + /> +
+ )} +
+ + + + +
+ {/* Main dashboard panels */} +
+
+ {/* Recent Clients */} +
+ 0 && ( +
- - - - {statusData.length === 0 - ? - : } - - - + {/* App Users */} +
+ + {team.length === 0 ? ( + + ) : ( +
+ {team.slice(0, 6).map((u) => ( +
+ +
+ {u.name} + {u.email} +
+ +
+ ))} +
+ )} +
+
+
+
- - - {byType.length === 0 ? ( - - ) : ( - - {byType.map(([type, count], i) => ( - - - - - {titleCase(type)} - - - {count} · {Math.round((count / clients.length) * 100)}% - - - - - ))} - - )} - - + +
); } diff --git a/src/pages/Settings.jsx b/src/pages/Settings.jsx index 2c42eef..03939ee 100644 --- a/src/pages/Settings.jsx +++ b/src/pages/Settings.jsx @@ -1,22 +1,27 @@ import { useState } from 'react'; import { - Grid, Card, Box, Stack, TextField, MenuItem, Switch, Button, Typography, Divider, - Snackbar, Alert, Chip, IconButton, InputAdornment, LinearProgress, Avatar -} from '@mui/material'; -import SaveOutlinedIcon from '@mui/icons-material/SaveOutlined'; -import TuneOutlinedIcon from '@mui/icons-material/TuneOutlined'; -import NotificationsNoneIcon from '@mui/icons-material/NotificationsNone'; -import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; -import ShieldOutlinedIcon from '@mui/icons-material/ShieldOutlined'; -import CampaignOutlinedIcon from '@mui/icons-material/CampaignOutlined'; -import BusinessOutlinedIcon from '@mui/icons-material/BusinessOutlined'; -import VerifiedOutlinedIcon from '@mui/icons-material/VerifiedOutlined'; -import WarningAmberRoundedIcon from '@mui/icons-material/WarningAmberRounded'; -import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded'; -import LogoutOutlinedIcon from '@mui/icons-material/LogoutOutlined'; -import ChevronRightIcon from '@mui/icons-material/ChevronRight'; -import Visibility from '@mui/icons-material/Visibility'; -import VisibilityOff from '@mui/icons-material/VisibilityOff'; + Save, + Sliders, + Bell, + Lock, + Shield, + Megaphone, + Building, + CheckCircle2, + AlertTriangle, + HelpCircle, + LogOut, + Eye, + EyeOff +} from 'lucide-react'; +import { Button } from '@astryxdesign/core/Button'; +import { Card } from '@astryxdesign/core/Card'; +import { List, ListItem } from '@astryxdesign/core/List'; +import { Text, Heading } from '@astryxdesign/core/Text'; +import { TextInput } from '@astryxdesign/core/TextInput'; +import { Selector } from '@astryxdesign/core/Selector'; +import { Switch } from '@astryxdesign/core/Switch'; +import { Badge } from '@astryxdesign/core/Badge'; import PageHeader from '@/components/PageHeader'; @@ -36,9 +41,9 @@ const INITIAL_NOTIFY = { const INITIAL_SECURITY = { currentPassword: '', newPassword: '', confirmPassword: '', twoFactor: false }; const NAV = [ - { icon: TuneOutlinedIcon, label: 'General', desc: 'Organisation profile' }, - { icon: NotificationsNoneIcon, label: 'Notifications', desc: 'Alerts & channels' }, - { icon: LockOutlinedIcon, label: 'Security', desc: 'Password & 2FA' } + { icon: Sliders, label: 'General', desc: 'Organisation profile' }, + { icon: Bell, label: 'Notifications', desc: 'Alerts & channels' }, + { icon: Lock, label: 'Security', desc: 'Password & 2FA' } ]; const NOTIFY_ROWS = [ @@ -49,12 +54,13 @@ const NOTIFY_ROWS = [ ]; const STRENGTH = [ - { label: 'Too weak', color: 'error' }, - { label: 'Weak', color: 'error' }, - { label: 'Fair', color: 'warning' }, - { label: 'Good', color: 'info' }, - { label: 'Strong', color: 'success' } + { label: 'Too weak', color: '#ef4444' }, + { label: 'Weak', color: '#ef4444' }, + { label: 'Fair', color: '#f59e0b' }, + { label: 'Good', color: '#3b82f6' }, + { label: 'Strong', color: '#10b981' } ]; + const scorePassword = (pw) => { let s = 0; if (pw.length >= 8) s++; @@ -64,63 +70,89 @@ const scorePassword = (pw) => { return s; }; -// Section surface with a tinted icon header band. -function Section({ icon: Icon, title, subtitle, color = 'primary', danger = false, children }) { +function Section({ icon: Icon, title, subtitle, danger = false, children }) { return ( - - `linear-gradient(90deg, ${theme.palette[color].lighter}66 0%, ${theme.palette.background.paper} 72%)` + +
- - - - - {title} - {subtitle && {subtitle}} - - - {children} +
+ +
+
+ {title} + {subtitle && {subtitle}} +
+
+
{children}
); } -// Two-column row: label + helper on the left, control on the right. -function Row({ label, description, children, align = 'center' }) { +function Row({ label, description, controlAlign = 'stretch', children }) { return ( - - - {label} - {description && {description}} - - {children} - +
+
+ {label} + {description && {description}} +
+
+ {children} +
+
); } -const rightAlign = { display: 'flex', justifyContent: { sm: 'flex-end' } }; - -function PasswordField({ label, value, onChange, autoComplete }) { +function PasswordField({ label, value, onChange }) { const [show, setShow] = useState(false); return ( - - setShow((s) => !s)} edge="end" size="small"> - {show ? : } - - - ) - }} - /> +
+ + +
); } @@ -133,11 +165,11 @@ export default function Settings() { const [notify, setNotify] = useState(INITIAL_NOTIFY); const [security, setSecurity] = useState(INITIAL_SECURITY); - const setG = (k) => (e) => { setGeneral((p) => ({ ...p, [k]: e.target.value })); setDirty(true); }; - const setN = (k) => (e) => { setNotify((p) => ({ ...p, [k]: e.target.checked })); setDirty(true); }; - const setSText = (k) => (e) => { setSecurity((p) => ({ ...p, [k]: e.target.value })); setDirty(true); }; + const setG = (k) => (val) => { setGeneral((p) => ({ ...p, [k]: val })); setDirty(true); }; + const setN = (k) => (val) => { setNotify((p) => ({ ...p, [k]: val })); setDirty(true); }; + const setSText = (k) => (val) => { setSecurity((p) => ({ ...p, [k]: val })); setDirty(true); }; - const save = () => { setToast(true); setDirty(false); }; + const save = () => { setToast(true); setDirty(false); setTimeout(() => setToast(false), 2500); }; const discard = () => { setGeneral(INITIAL_GENERAL); setNotify(INITIAL_NOTIFY); @@ -150,211 +182,211 @@ export default function Settings() { const mismatch = security.confirmPassword && security.newPassword !== security.confirmPassword; return ( - <> +
- {dirty && } - - - +
+ {dirty && } +
} /> - +
{/* Sidebar */} - - - - Preferences - - {NAV.map((item, i) => { - const active = tab === i; - const Icon = item.icon; - return ( - setTab(i)} - sx={{ - px: 1.5, py: 1.25, borderRadius: 2, cursor: 'pointer', position: 'relative', - bgcolor: active ? 'primary.lighter' : 'transparent', - transition: 'background-color .15s', - '&:hover': { bgcolor: active ? 'primary.lighter' : 'grey.50' }, - '&::before': active ? { content: '""', position: 'absolute', left: 0, top: 9, bottom: 9, width: 3, borderRadius: 3, bgcolor: 'primary.main' } : {} - }} - > - - - - - {item.label} - {item.desc} - - - - ); - })} - +
+
+ + + {NAV.map((item, i) => ( + } + isSelected={tab === i} + onClick={() => setTab(i)} + /> + ))} + - - - - - - - Need a hand? - Our team is available 24/7 for operational support. - - - + +
+
+ +
+
+ Need a hand? +
+ Our team is available 24/7 for operational support. +
+
+
- - +
+
{/* Content */} - +
{tab === 0 && ( - - {/* Organisation identity banner */} - - `linear-gradient(90deg, ${theme.palette.primary.lighter}88 0%, ${theme.palette.background.paper} 75%)` }} +
+ {/* Org banner */} + +
- - - - - - {general.orgName} - } label="Verified" sx={{ fontWeight: 700, bgcolor: 'success.lighter', color: 'success.dark', '& .MuiChip-icon': { color: 'inherit' } }} /> - - {general.supportEmail} · {general.contact} - - - +
+ +
+
+
+ {general.orgName} + } label="Verified" /> +
+ {general.supportEmail} · {general.contact} +
+
-
- }> - - - - - - - - - - - - {TIMEZONES.map((t) => {t})} - - - - - {LANGUAGES.map((l) => {l})} - - - +
+ + + + + + + + + + +
+ setG('timezone')(v || '')} /> +
+
+ +
+ setG('language')(v || '')} /> +
+
- +
)} {tab === 1 && ( - -
- }> - {NOTIFY_ROWS.map((row) => ( - - - - ))} - -
-
- }> - - +
+
+ {NOTIFY_ROWS.map((row) => ( + + - - - - + ))}
- +
+ + + + + + +
+
)} {tab === 2 && ( - -
- }> - - - - - - - {security.newPassword && ( - - - {pwMeta.label} - - )} - - - - - - {mismatch && Passwords do not match} - - - +
+
+ + + + +
+ + {security.newPassword && ( +
+
+
+
+ {pwMeta.label} +
+ )} +
+ + +
+ + {mismatch && Passwords do not match} +
+
-
- - - } - label={security.twoFactor ? 'Enabled' : 'Disabled'} - sx={{ fontWeight: 700, bgcolor: security.twoFactor ? 'success.lighter' : 'grey.100', color: security.twoFactor ? 'success.dark' : 'grey.600', '& .MuiChip-icon': { color: 'inherit' } }} +
+ +
+ + { setSecurity((p) => ({ ...p, twoFactor: v })); setDirty(true); }} /> - { setSecurity((p) => ({ ...p, twoFactor: e.target.checked })); setDirty(true); }} /> - +
-
- - - - +
+ +
- +
)} - - +
+
- setToast(false)} - anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} - > - setToast(false)} sx={{ width: '100%' }}> + {toast && ( +
+ Settings saved successfully. - - - +
+ )} + + +
); } diff --git a/src/pages/auth/Login.jsx b/src/pages/auth/Login.jsx index 352ef34..45286c4 100644 --- a/src/pages/auth/Login.jsx +++ b/src/pages/auth/Login.jsx @@ -1,25 +1,18 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { - Box, - Grid, - Card, - Stack, - Typography, - TextField, - InputAdornment, - IconButton, - Button, - Checkbox, - FormControlLabel, - Link -} from '@mui/material'; -import Visibility from '@mui/icons-material/Visibility'; -import VisibilityOff from '@mui/icons-material/VisibilityOff'; +import { Eye, EyeOff, Mail, Lock } from 'lucide-react'; +import { Center } from '@astryxdesign/core/Center'; +import { Card } from '@astryxdesign/core/Card'; +import { VStack, HStack } from '@astryxdesign/core/Layout'; +import { Text } from '@astryxdesign/core/Text'; +import { TextInput } from '@astryxdesign/core/TextInput'; +import { CheckboxInput } from '@astryxdesign/core/CheckboxInput'; +import { Button } from '@astryxdesign/core/Button'; +import { Link } from '@astryxdesign/core/Link'; +import { Banner } from '@astryxdesign/core/Banner'; import Logo from '@/components/Logo'; import bgImage from '../../assets/mid-mile-approach.jpg'; - import { loginAdmin } from '@/utils/apiClient'; export default function Login() { @@ -28,10 +21,14 @@ export default function Login() { const [auth, setAuth] = useState(''); const [pwd, setPwd] = useState(''); const [error, setError] = useState(''); + const [rememberMe, setRememberMe] = useState(true); + const [loading, setLoading] = useState(false); const handleLogin = async () => { + if (!auth || !pwd || loading) return; + setLoading(true); + setError(''); try { - setError(''); const data = await loginAdmin(auth, pwd); localStorage.setItem('logged_in', 'true'); localStorage.setItem('auth_token', data.token); @@ -39,6 +36,8 @@ export default function Login() { navigate('/dashboard'); } catch (err) { setError(err.message || 'Invalid email or password'); + } finally { + setLoading(false); } }; @@ -47,132 +46,128 @@ export default function Login() { }; return ( - - - - Welcome back - - Sign in to your Doormile operations account. - + /> - - - Email - setAuth(e.target.value)} - onKeyDown={handleKeyDown} - sx={{ - '& .MuiOutlinedInput-root': { - bgcolor: 'rgba(255,255,255,0.6)', - borderRadius: 2, - transition: 'all 0.2s ease', - '&:hover': { bgcolor: 'rgba(255,255,255,0.9)' }, - '&.Mui-focused': { bgcolor: '#fff', boxShadow: '0 4px 12px rgba(0,0,0,0.05)' } - } - }} +
+ + + +
+ +
+ Welcome back + + Sign in to your Doormile operations account. + +
+ + {error && } + + + } + hasAutoFocus + /> + +
+ } + /> + +
+
+ + + + Forgot password? + + +
- {error && {error}} - - -
-
- - - +
+ © {new Date().getFullYear()} Doormile Technologies. All rights reserved. - - - + +
+
); } diff --git a/src/pages/bookings/Bookings.jsx b/src/pages/bookings/Bookings.jsx index 5a5dc61..71c74df 100644 --- a/src/pages/bookings/Bookings.jsx +++ b/src/pages/bookings/Bookings.jsx @@ -1,15 +1,23 @@ -import { useState, useEffect } from 'react'; -import { - Card, Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, - Typography, TextField, Button, Dialog, DialogTitle, DialogContent, DialogActions, - Grid, IconButton, CircularProgress, Chip, MenuItem, InputAdornment, alpha, Autocomplete, Checkbox, FormControlLabel, Tabs, Tab, Snackbar, Alert -} from '@mui/material'; -import AddIcon from '@mui/icons-material/Add'; -import SearchIcon from '@mui/icons-material/Search'; -import CloseIcon from '@mui/icons-material/Close'; -import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import { useState, useEffect, useMemo } from 'react'; +import { Plus, Search, Trash2 } from 'lucide-react'; +import { Button } from '@astryxdesign/core/Button'; +import { Card } from '@astryxdesign/core/Card'; +import { Table, proportional, pixel } from '@astryxdesign/core/Table'; +import { Badge } from '@astryxdesign/core/Badge'; +import { Text } from '@astryxdesign/core/Text'; +import { Dialog, DialogHeader } from '@astryxdesign/core/Dialog'; +import { Layout, LayoutContent, LayoutFooter } from '@astryxdesign/core/Layout'; +import { FormLayout } from '@astryxdesign/core/FormLayout'; +import { Field } from '@astryxdesign/core/Field'; +import { TextInput } from '@astryxdesign/core/TextInput'; +import { NumberInput } from '@astryxdesign/core/NumberInput'; +import { Selector } from '@astryxdesign/core/Selector'; +import { CheckboxInput } from '@astryxdesign/core/CheckboxInput'; import PageHeader from '@/components/PageHeader'; +import EmptyState from '@/components/EmptyState'; +import StatusChip from '@/components/StatusChip'; +import TabLabelCount from '@/components/TabLabelCount'; import { fetchClients } from '@/utils/apiClient'; import ClientFormDialog from '../tenants/ClientFormDialog'; @@ -116,11 +124,13 @@ function BookingFormDialog({ open, onClose, onSave, clients, surveys }) { } }, [open]); - const handleChange = (field) => (e) => setFormData(prev => ({ ...prev, [field]: e.target.value })); + const set = (field) => (val) => { + setFormData(prev => ({ ...prev, [field]: val })); + }; - const handleParcelChange = (index, field) => (e) => { + const handleParcelChange = (index, field) => (val) => { const newParcels = [...formData.parcels]; - newParcels[index] = { ...newParcels[index], [field]: e.target.value }; + newParcels[index] = { ...newParcels[index], [field]: val }; setFormData(prev => ({ ...prev, parcels: newParcels })); }; @@ -196,214 +206,219 @@ function BookingFormDialog({ open, onClose, onSave, clients, surveys }) { const uniqueProviders = Array.from(new Set(surveys.map(s => s.company).filter(Boolean))); const providerLocations = surveys.filter(s => s.company === formData.providercompany).map(s => s.area || s.address).filter(Boolean); + const num = (v) => (v === '' || v == null ? undefined : Number(v)); + const setNum = (field) => (v) => set(field)(v == null ? '' : v); return ( <> - setError(null)} - anchorOrigin={{ vertical: 'top', horizontal: 'right' }} - sx={{ zIndex: 9999 }} - > - setError(null)} severity="error" variant="filled" sx={{ width: '100%', boxShadow: 3 }}> - {error} - - - - - Create New Booking - - Customer Details - - - - { - if (typeof option === 'string') return option; - return `${option.first_name || ''} ${option.last_name || ''} - ${option.phone || ''}`; - }} - onChange={(e, newValue) => { - if (newValue && typeof newValue === 'object') { - setFormData(prev => ({ - ...prev, - customer_name: `${newValue.first_name || ''} ${newValue.last_name || ''}`.trim(), - customer_phone: newValue.phone || '' - })); - } else { - setFormData(prev => ({ ...prev, customer_name: '', customer_phone: '' })); - } - }} - renderInput={(params) => ( - + { if (!o) onClose(); }} width={800} maxHeight="90dvh" purpose="form"> + { if (!o) onClose(); }} />} + content={ + + + {error && ( +
+ {error} +
)} - /> - -
-
-
- Assigned Provider (From Survey) - - - setFormData(prev => ({ ...prev, providercompany: val || '', providerlocation: '' }))} - renderInput={(params) => } - /> - - - setFormData(prev => ({ ...prev, providerlocation: val || '' }))} - renderInput={(params) => } - disabled={!formData.providercompany} - /> - - + {/* Customer Details */} +
+
Customer Details
+
+
+ + { + const selected = clients.find(c => `${c.first_name || ''} ${c.last_name || ''} - ${c.phone || ''}` === e.target.value); + if (selected) { + setFormData(prev => ({ + ...prev, + customer_name: `${selected.first_name || ''} ${selected.last_name || ''}`.trim(), + customer_phone: selected.phone || '' + })); + } else { + setFormData(prev => ({ ...prev, customer_name: '', customer_phone: '' })); + } + }} + /> + + {clients.map((c) => ( + + +
+
+
- Pickup Location - - - - - - - - + {/* Assigned Provider */} +
+
Assigned Provider (From Survey)
+ + setFormData(prev => ({ ...prev, providercompany: v ?? '', providerlocation: '' }))} + /> + set('providerlocation')(v ?? '')} + isDisabled={!formData.providercompany} + /> + +
- Delivery Destination - - - - - - setFormData(prev => ({ ...prev, deliverycity: val || '' }))} - onInputChange={(e, val) => setFormData(prev => ({ ...prev, deliverycity: val || '' }))} - renderInput={(params) => } - /> - - - - - + {/* Pickup Location */} +
+
Pickup Location
+ + + + +
- - Parcel Details - - - - {formData.parcels.map((parcel, idx) => ( - - - - - - - - - - - - - - - - - - removeParcel(idx)} disabled={formData.parcels.length === 1}> - - - - - ))} + {/* Delivery Destination */} +
+
Delivery Destination
+ + + + + set('deliverycity')(e.target.value)} /> + + {CITIES.map((c) => + + + + +
- - Price & Insurance - - - {quoteInfo && ( - - Estimated Base Price: ₹{quoteInfo.basequote?.toFixed(2)} (Chargeable Weight: {quoteInfo.chargeableweight} kg) - - )} - - - - - - - { - const checked = e.target.checked; - setFormData(prev => ({ - ...prev, - needsinsurance: checked, - insuranceamount: checked && prev.declaredvalue ? (parseFloat(prev.declaredvalue) * 0.01).toFixed(2) : prev.insuranceamount - })); - }} />} - label="Needs Insurance? (1%)" - sx={{ whiteSpace: 'nowrap' }} - /> - { - const val = e.target.value; - setFormData(prev => ({ - ...prev, - declaredvalue: val, - insuranceamount: prev.needsinsurance && val ? (parseFloat(val) * 0.01).toFixed(2) : prev.insuranceamount - })); - }} - /> - - - - + {/* Parcel Details */} +
+
+
Parcel Details
+
- Additional Information - - - - Normal - Fast Express - - - - - - + {formData.parcels.map((parcel, idx) => ( +
+
+ +
+
+ handleParcelChange(idx, 'weight')(v == null ? '' : v)} /> +
+
+ handleParcelChange(idx, 'length')(v == null ? '' : v)} /> +
+
+ handleParcelChange(idx, 'width')(v == null ? '' : v)} /> +
+
+ handleParcelChange(idx, 'height')(v == null ? '' : v)} /> +
+ +
+ ))} +
+ + {/* Price & Insurance */} +
+
+
Price & Insurance
+
+ {quoteInfo && ( +
+ Estimated Base Price: ₹{quoteInfo.basequote?.toFixed(2)} (Chargeable Weight: {quoteInfo.chargeableweight} kg) +
+ )} + + +
+ { + setFormData(prev => ({ + ...prev, + needsinsurance: checked, + insuranceamount: checked && prev.declaredvalue ? (parseFloat(prev.declaredvalue) * 0.01).toFixed(2) : prev.insuranceamount + })); + }} + /> +
+ { + setFormData(prev => ({ + ...prev, + declaredvalue: v == null ? '' : v, + insuranceamount: prev.needsinsurance && v != null ? (v * 0.01).toFixed(2) : prev.insuranceamount + })); + }} + /> + +
+
+ + {/* Additional info */} +
+
Additional Information
+ + + + +
+ + + } + footer={ + +
+
+
+ } + /> +
- - - - - - - {/* Embedded Client Registration Form */} {clientDialogOpen && ( setClientDialogOpen(false)} onSaved={() => { setClientDialogOpen(false); - onSave(); // Refetch clients so they appear in dropdown + onSave(); }} /> )} @@ -427,6 +442,8 @@ export default function Bookings() { const [search, setSearch] = useState(''); const [sourceFilter, setSourceFilter] = useState('All'); const [formOpen, setFormOpen] = useState(false); + const [page, setPage] = useState(0); + const [rpp, setRpp] = useState(10); const loadData = async () => { setLoading(true); @@ -459,78 +476,177 @@ export default function Bookings() { return matchesSearch && matchesSource; }); + const pagedBookings = displayBookings.slice(page * rpp, page * rpp + rpp); + + const bookingColumns = useMemo(() => [ + { + key: 'bookingno', + header:
Booking No
, + width: proportional(1.5), + renderCell: (b) => ( +
+ {b.bookingno} +
+ ) + }, + { + key: 'deliverycity', + header: 'Delivery City', + width: proportional(1), + renderCell: (b) => { + const city = b.deliverycity || b.deliverypincode; + return city + ? {city} + : N/A; + } + }, + { + key: 'providercompany', + header: 'Provider', + width: proportional(1), + renderCell: (b) => b.providercompany + ? + : N/A + }, + { + key: 'bookingsource', + header: 'Source', + width: pixel(100), + renderCell: (b) => ( + + ) + }, + { + key: 'price', + header: 'Price', + width: pixel(120), + renderCell: (b) => ( + + {b.serviceoptions && b.serviceoptions.length > 0 ? `₹${b.serviceoptions[0].estimatedprice}` : 'N/A'} + + ) + }, + { + key: 'status', + header: 'Status', + width: pixel(150), + renderCell: (b) => + } + ], []); + + const sourceCounts = useMemo(() => { + const counts = { All: bookings.length, Customer_App: 0, CRM_Console: 0 }; + bookings.forEach((b) => { + if (b.bookingsource === 'CRM_Console') counts.CRM_Console += 1; + else counts.Customer_App += 1; + }); + return counts; + }, [bookings]); + return ( - - +
+ +
+ } + hasClear + value={search} + onChange={(v) => { setSearch(v); setPage(0); }} + /> +
+
+ } + /> - - - setSearch(e.target.value)} - size="small" - InputProps={{ - startAdornment: , - sx: { borderRadius: 3, bgcolor: alpha('#e2e8f0', 0.4) } - }} - /> - - - - - setSourceFilter(val)} aria-label="booking source filter tabs"> - - - - - + + {/* Tab filters */} +
+ {[ + { label: 'All Bookings', value: 'All' }, + { label: 'App Bookings', value: 'Customer_App' }, + { label: 'CRM Bookings', value: 'CRM_Console' } + ].map((tab) => ( +
{ setSourceFilter(tab.value); setPage(0); }} + style={{ + padding: '16px 0', + cursor: 'pointer', + borderBottom: sourceFilter === tab.value ? '2px solid #C01227' : '2px solid transparent', + color: sourceFilter === tab.value ? '#C01227' : '#64748b', + fontWeight: sourceFilter === tab.value ? 600 : 500, + fontSize: '0.875rem', + transition: 'all 0.2s' + }} + > + +
+ ))} +
{loading ? ( - +
+
+
+ ) : displayBookings.length === 0 ? ( + ) : ( - - - - - Booking No - Delivery City - Provider - Source - Price - Status - - - - {displayBookings.length === 0 ? ( - No bookings found. - ) : ( - displayBookings.map(b => ( - - {b.bookingno} - {b.deliverycity || b.deliverypincode || 'N/A'} - - {b.providercompany ? ( - - ) : 'N/A'} - - - - - - {b.serviceoptions && b.serviceoptions.length > 0 ? `₹${b.serviceoptions[0].estimatedprice}` : 'N/A'} - - - - - - )) - )} - -
-
+
+ + + )} + + {!loading && displayBookings.length > 0 && ( +
+
+ Rows per page: + +
+ + {displayBookings.length === 0 ? '0-0' : `${page * rpp + 1}-${Math.min((page + 1) * rpp, displayBookings.length)}`} of {displayBookings.length} + +
+
+
)} @@ -541,6 +657,31 @@ export default function Bookings() { clients={clients} surveys={surveys} /> - + + ); } + +const inputStyle = { + width: '100%', + padding: '8px 12px', + borderRadius: '6px', + border: '1px solid #cbd5e1', + fontSize: '0.875rem', + color: '#1e293b', + outline: 'none', + boxSizing: 'border-box', + transition: 'border-color 0.2s', + backgroundColor: '#ffffff' +}; + +const selectStyle = { + ...inputStyle, + height: '38px', + cursor: 'pointer' +}; diff --git a/src/pages/maintenance/ComingSoon.jsx b/src/pages/maintenance/ComingSoon.jsx index de12bf3..d3c70a4 100644 --- a/src/pages/maintenance/ComingSoon.jsx +++ b/src/pages/maintenance/ComingSoon.jsx @@ -1,5 +1,8 @@ import { useState } from 'react'; -import { Box, Stack, Typography, TextField, Button } from '@mui/material'; +import { Button } from '@astryxdesign/core/Button'; +import { TextInput } from '@astryxdesign/core/TextInput'; +import { Text, Heading } from '@astryxdesign/core/Text'; +import { Mail } from 'lucide-react'; import Logo from '@/components/Logo'; @@ -14,67 +17,73 @@ export default function ComingSoon() { const [email, setEmail] = useState(''); return ( - - +
- Coming Soon - Something new is on its way + Coming Soon + Something new is on its way {/* Countdown */} - +
{COUNTDOWN.map((c, i) => ( - - +
- +
{String(c.value).padStart(2, '0')} - - +
+
{c.label} - - +
+
{i < COUNTDOWN.length - 1 && ( - : +
:
)} -
+
))} -
+
{/* Subscribe */} - - - Be the first to be notified when Doormile launches. - - - setEmail(e.target.value)} - /> - - - -
-
+
+
+ + Be the first to be notified when Doormile launches. + +
+
+
+ } + value={email} + onChange={setEmail} + /> +
+
+
+ + ); } diff --git a/src/pages/maintenance/Error404.jsx b/src/pages/maintenance/Error404.jsx index afd0b4b..8ff0025 100644 --- a/src/pages/maintenance/Error404.jsx +++ b/src/pages/maintenance/Error404.jsx @@ -1,34 +1,37 @@ import { useNavigate } from 'react-router-dom'; -import { Box, Stack, Typography, Button } from '@mui/material'; -import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined'; +import { Button } from '@astryxdesign/core/Button'; +import { Text, Heading } from '@astryxdesign/core/Text'; +import { Home } from 'lucide-react'; export default function Error404() { const navigate = useNavigate(); return ( - - - +
+ 404 - - - Page Not Found - - The page you are looking for was moved, removed, renamed, or might never exist! - - - - + +
+ Page Not Found +
+ + The page you are looking for was moved, removed, renamed, or might never exist! + +
+
+
); } diff --git a/src/pages/maintenance/Error500.jsx b/src/pages/maintenance/Error500.jsx index 62998d9..7291077 100644 --- a/src/pages/maintenance/Error500.jsx +++ b/src/pages/maintenance/Error500.jsx @@ -1,34 +1,37 @@ import { useNavigate } from 'react-router-dom'; -import { Box, Stack, Typography, Button } from '@mui/material'; -import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined'; +import { Button } from '@astryxdesign/core/Button'; +import { Text, Heading } from '@astryxdesign/core/Text'; +import { Home } from 'lucide-react'; export default function Error500() { const navigate = useNavigate(); return ( - - - +
+ 500 - - - Internal Server Error - - Server error 500. We are fixing the problem. Please try again at a later stage. - - - - + +
+ Internal Server Error +
+ + Server error 500. We are fixing the problem. Please try again at a later stage. + +
+
+
); } diff --git a/src/pages/maintenance/UnderConstruction.jsx b/src/pages/maintenance/UnderConstruction.jsx index be57252..9cf9e6d 100644 --- a/src/pages/maintenance/UnderConstruction.jsx +++ b/src/pages/maintenance/UnderConstruction.jsx @@ -1,45 +1,47 @@ import { useNavigate } from 'react-router-dom'; -import { Box, Stack, Typography, Button } from '@mui/material'; -import ConstructionOutlinedIcon from '@mui/icons-material/ConstructionOutlined'; -import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined'; +import { Button } from '@astryxdesign/core/Button'; +import { Text, Heading } from '@astryxdesign/core/Text'; +import { Construction, Home } from 'lucide-react'; export default function UnderConstruction() { const navigate = useNavigate(); return ( - - - +
- - - Under Construction - - Hey! Please check out this site later. We are doing some maintenance on it right now. - - - - + +
+ Under Construction +
+ + Hey! Please check out this site later. We are doing some maintenance on it right now. + +
+ - - + const zoneOptions = providerColumns.length > 0 ? providerColumns : ZONES; + + return ( + { if (!o) onClose(); }} width={440} purpose="form"> + { if (!o) onClose(); }} + /> + } + content={ + + + {error && } + + {isCell ? ( + + Updating rate for +
+ {formData.company} +
+
+ {formData.weight_slab} · {formData.zone || formData.service_type} +
+
+ ) : ( + <> + setFormData((prev) => ({ ...prev, company: v }))} + /> + setFormData((prev) => ({ ...prev, weight_slab: v || '' }))} + /> + setFormData((prev) => ({ + ...prev, + zone: providerUsesServiceType ? '' : (v || ''), + service_type: providerUsesServiceType ? (v || '') : '' + }))} + /> + + )} + + setFormData((prev) => ({ ...prev, rate: v }))} + /> +
+
+ } + footer={ + +
+
+
+ } + />
- ); } @@ -223,9 +226,9 @@ export default function Pricing() { const [selectedProvider, setSelectedProvider] = useState(''); const [searchQuery, setSearchQuery] = useState(''); const [locationsOpen, setLocationsOpen] = useState(false); + const [locationSearch, setLocationSearch] = useState(''); const [formOpen, setFormOpen] = useState(false); const [editingRecord, setEditingRecord] = useState(null); - const [isEditMode, setIsEditMode] = useState(false); const [isCellEdit, setIsCellEdit] = useState(false); const [users, setUsers] = useState([]); @@ -253,7 +256,6 @@ export default function Pricing() { return loadData(); }, []); - // Ensure absolutely unique companies and default selection const companies = [...new Set(pricingList.map(p => p.company).filter(Boolean))]; useEffect(() => { if (!selectedProvider && companies.length > 0) { @@ -262,19 +264,23 @@ export default function Pricing() { }, [companies, selectedProvider]); const providerRates = pricingList.filter(p => p.company === selectedProvider); - // Normalize: lowercase, trim edges, collapse internal spaces const normCompany = (s) => (s || '').toLowerCase().trim().replace(/\s+/g, ' '); const normProvider = normCompany(selectedProvider); const providerSurveys = surveysList.filter(p => { const pn = normCompany(p.company); - // Exact after normalising, or one name starts with the other (handles "Pvt Ltd" suffixes) return pn === normProvider || pn.startsWith(normProvider) || normProvider.startsWith(pn); }); - // Sort by ID desc so most recent record wins when duplicates exist + const filteredSurveys = useMemo(() => { + if (!locationSearch.trim()) return providerSurveys; + const q = locationSearch.toLowerCase(); + return providerSurveys.filter((s) => + (s.area || '').toLowerCase().includes(q) || (s.address || '').toLowerCase().includes(q) + ); + }, [providerSurveys, locationSearch]); + const sortedProviderRates = [...providerRates].sort((a, b) => (b.id || 0) - (a.id || 0)); - // Extract numeric weight from a slab string for sorting rows logically const slabSortWeight = (slab) => { const s = (slab || '').toLowerCase().replace(/\s+/g, ''); const nums = s.match(/[\d.]+/g); @@ -286,15 +292,23 @@ export default function Pricing() { return grams; }; - // Use zone if available, fall back to service_type (e.g. DTDC has service_type instead of zone) - const getColKey = (r) => ((r.zone || '').trim() || (r.service_type || '').trim()); + const normalizeZone = (z) => { + if (!z) return ''; + let val = z.trim(); + const lower = val.toLowerCase().replace(/\s+/g, ''); + if (lower === 'tamilnadu') return 'Tamil Nadu'; + if (lower === 'karanataka' || lower === 'karnataka') return 'Karnataka'; + if (lower === 'local' || lower === 'localsame' || lower === 'local/same') return 'Local'; + if (lower === 'central/northindia' || lower === 'central/north') return 'Central/North India'; + return val; + }; + + const getColKey = (r) => normalizeZone((r.zone || '').trim() || (r.service_type || '').trim()); const providerUsesServiceType = sortedProviderRates.length > 0 && sortedProviderRates.every(r => !(r.zone || '').trim()); - // Dynamic columns — whichever field the provider uses for differentiation const providerZones = [...new Set(sortedProviderRates.map(getColKey).filter(Boolean))].sort(); - // Dynamic rows: every unique weight slab, sorted by actual weight const allProviderSlabs = [...new Set(sortedProviderRates.map(r => (r.weight_slab || '').trim()).filter(Boolean))] .sort((a, b) => slabSortWeight(a) - slabSortWeight(b)); @@ -302,321 +316,281 @@ export default function Pricing() { ? allProviderSlabs.filter(s => s.toLowerCase().includes(searchQuery.toLowerCase())) : allProviderSlabs; + const matrixData = useMemo( + () => displaySlabs.map((slab) => ({ + slab, + rates: Object.fromEntries( + providerZones.map((zone) => [ + zone, + sortedProviderRates.find(r => (r.weight_slab || '').trim() === slab && getColKey(r) === zone) || null + ]) + ) + })), + [displaySlabs, providerZones, sortedProviderRates] + ); + + const matrixColumns = useMemo(() => { + const openCellEdit = (slab, zone, match) => { + setEditingRecord({ + id: match ? match.id : undefined, + company: selectedProvider, + weight_slab: slab, + zone: providerUsesServiceType ? '' : zone, + service_type: providerUsesServiceType ? zone : (match?.service_type || ''), + rate: match ? String(match.rate) : '' + }); + setIsCellEdit(true); + setFormOpen(true); + }; + + return [ + { + key: 'slab', + header:
Weight Slab
, + width: pixel(180), + renderCell: (row) => ( +
+ {row.slab} +
+ ) + }, + ...providerZones.map((zone) => ({ + key: zone, + header: zone, + width: proportional(1), + align: 'center', + renderCell: (row) => { + const match = row.rates[zone]; + const hasRate = !!match; + return ( +
+ {hasRate ? ( + + ) : ( + + )} + +
+ ); + } + })) + ]; + }, [providerZones, selectedProvider, providerUsesServiceType]); + return ( - +
+
+ setSelectedProvider(v || '')} + /> +
+
+ } + hasClear + value={searchQuery} + onChange={(v) => setSearchQuery(v)} + /> +
+
+ } /> - - - {/* Modern Premium Control Panel */} - - - - - - - - Active Logistics Provider - - - - - - +
- - setSearchQuery(e.target.value)} - InputProps={{ - startAdornment: , - sx: { - borderRadius: 50, - bgcolor: alpha('#e2e8f0', 0.3), - '& fieldset': { border: 'none' }, - '&:hover': { bgcolor: alpha('#e2e8f0', 0.5) }, - '&.Mui-focused': { bgcolor: '#fff', '& fieldset': { border: '2px solid', borderColor: 'primary.main' } } - } - }} - /> - - - - - - {/* Minimalist Collapsible Provider Profile Widget */} + {/* Collapsible Provider Profile */} {selectedProvider && providerSurveys.length > 0 && ( - - setLocationsOpen(!locationsOpen)} > - - {/* Avatar & Title */} - - +
+
{(selectedProvider || 'A')[0].toUpperCase()} - - - LOGISTICS PARTNER - {selectedProvider} - - - {providerSurveys.length} Registered Location{providerSurveys.length !== 1 && 's'} - - - - - {/* Badges & Action */} - - - - - - - - +
+
+ {selectedProvider} +
+ + {providerSurveys.length} location{providerSurveys.length !== 1 && 's'} +
+
+
-
+ : } + /> +
- - - - - ALL SERVICED AREAS: - - - {providerSurveys.map((s, idx) => ( - +
+
+ } + hasClear + value={locationSearch} + onChange={(v) => setLocationSearch(v)} /> - ))} - - - +
+ + {filteredSurveys.length} of {providerSurveys.length} serviced location{providerSurveys.length !== 1 ? 's' : ''} + +
+ + {filteredSurveys.length === 0 ? ( + + + + ) : ( +
+ {filteredSurveys.map((s, idx) => { + const title = s.area || s.address || 'Unknown area'; + const subtitle = s.area && s.address ? s.address : null; + const mapQuery = s.plus_code ? `${s.plus_code} ${s.address || ''}`.trim() : (s.address || s.area); + const mapUrl = mapQuery ? `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(mapQuery)}` : undefined; + return ( +
+ } + endContent={mapUrl ? : undefined} + href={mapUrl} + target={mapUrl ? '_blank' : undefined} + rel={mapUrl ? 'noopener noreferrer' : undefined} + /> +
+ ); + })} +
+ )} + + )}
)} - {/* Pricing Data Grid */} - + {/* Pricing Data Matrix */} + +
+
+
+ +
+
+ Rate Matrix + Weight slab × zone pricing grid{selectedProvider ? ` for ${selectedProvider}` : ''} +
+
+ {!loading && providerZones.length > 0 && ( +
+ + +
+ )} +
+ {loading ? ( - - - +
+
+
+ ) : providerZones.length === 0 ? ( + + ) : displaySlabs.length === 0 ? ( + ) : ( - -
- - - Weight Slab - {providerZones.map(zone => ( - - {zone} - - ))} - - - - {providerZones.length === 0 ? ( - - - - No pricing data for this provider yet. - - - - ) : displaySlabs.length === 0 ? ( - - - - No weight slabs match your search. - - - - ) : ( - displaySlabs.map((slab) => ( - - - {slab} - - {providerZones.map(zone => { - // Exact match — use getColKey so DTDC service_type columns work too - const match = sortedProviderRates.find(r => (r.weight_slab || '').trim() === slab && getColKey(r) === zone); - const hasRate = !!match; - const rateStr = match ? `₹${match.rate}` : '—'; - - let tooltipText = ''; - if (match && match.updated_by) { - const updater = users.find(u => u.id === match.updated_by); - tooltipText = `Last updated by: ${updater ? updater.first_name : 'ID ' + match.updated_by}`; - } - - const openCellEdit = () => { - setEditingRecord({ - id: match ? match.id : undefined, - company: selectedProvider, - weight_slab: slab, - zone: providerUsesServiceType ? '' : zone, - service_type: providerUsesServiceType ? zone : (match?.service_type || ''), - rate: match ? String(match.rate) : '' - }); - setIsCellEdit(true); - setFormOpen(true); - }; - - return ( - - - - - {rateStr} - - {isEditMode && ( - hasRate - ? - : - )} - - - - ); - })} - - )) - )} - -
- + )} - + - + + ); } diff --git a/src/pages/survey/Survey.jsx b/src/pages/survey/Survey.jsx index 8245f49..ac1129b 100644 --- a/src/pages/survey/Survey.jsx +++ b/src/pages/survey/Survey.jsx @@ -1,27 +1,43 @@ -import { useState, useEffect, Fragment } from 'react'; +import { useState, useEffect } from 'react'; import { fetchUsers, deleteCompetitorBranch } from '@/utils/apiClient'; import { - Card, Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, - Typography, TextField, InputAdornment, IconButton, Collapse, TablePagination, Stack, CircularProgress, alpha, - Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, MenuItem, Alert, Autocomplete, Snackbar -} from '@mui/material'; -import SearchIcon from '@mui/icons-material/Search'; -import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; -import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; -import PhoneOutlinedIcon from '@mui/icons-material/PhoneOutlined'; -import PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined'; -import AssignmentOutlinedIcon from '@mui/icons-material/AssignmentOutlined'; -import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; -import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; -import StorefrontOutlinedIcon from '@mui/icons-material/StorefrontOutlined'; -import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined'; -import MapOutlinedIcon from '@mui/icons-material/MapOutlined'; -import { Avatar, Button, Chip, Grid } from '@mui/material'; -import AddIcon from '@mui/icons-material/Add'; -import CloseIcon from '@mui/icons-material/Close'; + Search, + Edit, + Trash2, + Phone, + MapPin, + FileText, + ChevronDown, + ChevronUp, + Store, + Truck, + Map, + Plus, + X, + CheckCircle2, + AlertCircle +} from 'lucide-react'; +import { Button } from '@astryxdesign/core/Button'; +import { Card } from '@astryxdesign/core/Card'; +import { Dialog, DialogHeader } from '@astryxdesign/core/Dialog'; +import { Layout, LayoutContent, LayoutFooter } from '@astryxdesign/core/Layout'; +import { FormLayout } from '@astryxdesign/core/FormLayout'; +import { TextInput } from '@astryxdesign/core/TextInput'; +import { TextArea } from '@astryxdesign/core/TextArea'; +import { Selector } from '@astryxdesign/core/Selector'; +import { AlertDialog } from '@astryxdesign/core/AlertDialog'; +import { Text, Heading } from '@astryxdesign/core/Text'; +import { Badge } from '@astryxdesign/core/Badge'; +import { IconButton } from '@astryxdesign/core/IconButton'; +import { Banner } from '@astryxdesign/core/Banner'; import PageHeader from '@/components/PageHeader'; import StatCard from '@/components/StatCard'; +import EmptyState from '@/components/EmptyState'; + +function isQuoted(row) { + return !!(row.rate_per_kg && !String(row.rate_per_kg).toLowerCase().includes('not answered')); +} const API_BASE = import.meta.env.VITE_API_BASE || 'https://api.doormile.com/api/v1'; @@ -33,83 +49,62 @@ const getHeaders = () => { }; }; -const FIELD_LABEL_SX = { textTransform: 'uppercase', letterSpacing: 0.4, fontSize: '0.68rem', fontWeight: 700, color: 'grey.700' }; +function Pill({ label, color = 'neutral', truncate = false }) { + if (!label) return ; + + const badgeStyles = { + success: { bg: '#E3F6EC', fg: '#00773B' }, + warning: { bg: '#FFF7E0', fg: '#8A6500' }, + info: { bg: '#E0F7F8', fg: '#00727B' }, + error: { bg: '#FEEAE9', fg: '#A82216' }, + primary: { bg: '#F8E0E3', fg: '#9E0E20' }, + neutral: { bg: '#f1f5f9', fg: '#475569' } + }; + + const style = badgeStyles[color] || badgeStyles.neutral; -function Pill({ label, color = 'default' }) { - if (!label) return ; return ( - + + {label} + ); } function Field({ label, children }) { return ( - - {label} - {children} - - ); -} - -function MagicStatCard({ title, value, icon: Icon, gradient, percentage }) { - return ( - - - - - {title} - - - - - - - - {value} - - {percentage && ( - - )} - - - - +
+ {label} +
{children}
+
); } function SectionCard({ icon: Icon, title, children }) { return ( - - - - {title} - - {children} - +
+
+ + {title} +
+
{children}
+
); } @@ -138,9 +133,8 @@ async function apiSaveSurvey(data) { } async function apiSavePricing(data) { - const isUpdate = !!data.id; - const url = isUpdate ? `${API_BASE}/admin/carrier-pricing/${data.id}` : `${API_BASE}/admin/carrier-pricing`; - const method = isUpdate ? 'PUT' : 'POST'; + const url = `${API_BASE}/admin/carrier-pricing`; + const method = 'POST'; const res = await fetch(url, { method, headers: getHeaders(), @@ -183,13 +177,13 @@ function SurveyFormDialog({ open, onClose, onSave, initialData }) { } }, [open, initialData]); - const handleChange = (field) => (e) => { - setFormData(prev => ({ ...prev, [field]: e.target.value })); + const set = (field) => (val) => { + setFormData(prev => ({ ...prev, [field]: val })); }; - const handleSlabChange = (index, field) => (e) => { + const handleSlabChange = (index, field) => (val) => { const newSlabs = [...(formData.slabs || [])]; - newSlabs[index] = { ...newSlabs[index], [field]: e.target.value }; + newSlabs[index] = { ...newSlabs[index], [field]: val }; setFormData(prev => ({ ...prev, slabs: newSlabs })); }; @@ -206,9 +200,9 @@ function SurveyFormDialog({ open, onClose, onSave, initialData }) { setFormData(prev => ({ ...prev, slabs: newSlabs })); }; - const handlePincodeChange = (index) => (e) => { + const handlePincodeChange = (index) => (val) => { const newPincodes = [...(formData.pincodes || [])]; - newPincodes[index] = e.target.value; + newPincodes[index] = val; setFormData(prev => ({ ...prev, pincodes: newPincodes })); }; @@ -232,7 +226,7 @@ function SurveyFormDialog({ open, onClose, onSave, initialData }) { if (formData.pincodes && formData.pincodes.length > 0) { const invalidPincodes = formData.pincodes.filter(p => p.trim() && !/^\d{6}$/.test(p.trim())); if (invalidPincodes.length > 0) { - throw new Error(`Invalid pincodes detected: ${invalidPincodes.join(', ')}. All pincodes must be exactly 6 numeric digits.`); + throw new Error(`Invalid pincodes: ${invalidPincodes.join(', ')}. Must be 6 digits.`); } } @@ -269,7 +263,6 @@ function SurveyFormDialog({ open, onClose, onSave, initialData }) { })); } catch (err) { console.error("Failed to save pricing slabs", err); - // Continue anyway since survey was saved } } @@ -283,151 +276,127 @@ function SurveyFormDialog({ open, onClose, onSave, initialData }) { } }; - return ( - <> - setError(null)} - anchorOrigin={{ vertical: 'top', horizontal: 'right' }} - sx={{ zIndex: 9999 }} - > - setError(null)} severity="error" variant="filled" sx={{ width: '100%', boxShadow: 3 }}> - {error} - - + const YES_NO = [{ value: 'yes', label: 'Yes' }, { value: 'no', label: 'No' }]; + const FREQUENCY_OPTIONS = ['Daily', 'Weekly', 'Bi-weekly', 'Monthly', 'On-demand']; + const WEIGHT_SLAB_OPTIONS = WEIGHT_SLABS.map((s) => ({ value: s, label: s })); + const ZONE_OPTIONS = ZONES.map((z) => ({ value: z, label: z })); - - {initialData ? 'Edit Survey Record' : 'Add New Survey'} - - - - - - - - - - - - - - - - - Yes - No - - - - - Yes - No - - - - - - - - - - - Daily - Weekly - Bi-weekly - Monthly - On-demand - - - - - - - - - SERVICEABLE PINCODES - - - {(formData.pincodes || []).map((pincode, index) => ( - - - - - - removePincode(index)}> - - - - - ))} - {(!formData.pincodes || formData.pincodes.length === 0) && ( - - Click 'Add Pincode' to assign locations to this competitor. - - )} - - - - - - - - - - PRICING SLABS (OPTIONAL) - - - {(formData.slabs || []).map((slab, index) => ( - - - - {WEIGHT_SLABS.map(s => {s})} - - - - - {ZONES.map(z => {z})} - - - - - - - - - - removeSlab(index)}> - - - - - ))} - {(!formData.slabs || formData.slabs.length === 0) && ( - - Click 'Add Slab' to add pricing entries for this competitor. - - )} - - - - - - - - + return ( + { if (!o) onClose(); }} width={600} maxHeight="90dvh" purpose="form"> + { if (!o) onClose(); }} />} + content={ + + + {error && } + + + + + + + + + + + + + + + + + + + set('frequency')(v ?? '')} + /> + + + +
+
+ SERVICEABLE PINCODES +
+ {(formData.pincodes || []).map((pincode, index) => ( +
+
+ +
+ +
+ ))} +
+ +