From a162fa89e541208c0c961a3af5464ca1bef0ad4d Mon Sep 17 00:00:00 2001 From: Thiru-tenext Date: Fri, 5 Jun 2026 17:28:05 +0530 Subject: [PATCH] first commit --- .gitignore | 27 + README.md | 50 + index.html | 21 + package-lock.json | 2667 +++++++++++++++++++ package.json | 28 + public/Doormile-logo.png | Bin 0 -> 13651 bytes public/preloader.png | Bin 0 -> 8207 bytes src/App.jsx | 76 + src/components/EmptyState.jsx | 33 + src/components/Logo.jsx | 51 + src/components/MainCard.jsx | 17 + src/components/MapPlaceholder.jsx | 59 + src/components/PageHeader.jsx | 42 + src/components/StatCard.jsx | 55 + src/components/StatusChip.jsx | 54 + src/components/TabLabelCount.jsx | 30 + src/components/UserAvatar.jsx | 12 + src/components/charts/AreaChart.jsx | 66 + src/components/charts/DonutChart.jsx | 59 + src/data/mock.js | 181 ++ src/layout/MainLayout/Header.jsx | 261 ++ src/layout/MainLayout/Sidebar.jsx | 200 ++ src/layout/MainLayout/index.jsx | 45 + src/layout/MinimalLayout.jsx | 11 + src/main.jsx | 22 + src/menu/navItems.jsx | 55 + src/pages/Dashboard.jsx | 124 + src/pages/Deliveries.jsx | 279 ++ src/pages/Pricing.jsx | 93 + src/pages/Profile.jsx | 69 + src/pages/Requests.jsx | 162 ++ src/pages/Settings.jsx | 206 ++ src/pages/auth/Login.jsx | 124 + src/pages/customers/CreateCustomer.jsx | 95 + src/pages/customers/Customers.jsx | 245 ++ src/pages/invoice/InvoicePreview.jsx | 180 ++ src/pages/invoice/Invoices.jsx | 154 ++ src/pages/maintenance/ComingSoon.jsx | 80 + src/pages/maintenance/Error404.jsx | 34 + src/pages/maintenance/Error500.jsx | 34 + src/pages/maintenance/UnderConstruction.jsx | 45 + src/pages/orders/AssignOrders.jsx | 135 + src/pages/orders/CreateMultipleOrders.jsx | 254 ++ src/pages/orders/CreateOrder.jsx | 142 + src/pages/orders/OrderDetails.jsx | 170 ++ src/pages/orders/OrdersList.jsx | 195 ++ src/pages/reports/OrdersDetails.jsx | 190 ++ src/pages/reports/OrdersSummary.jsx | 195 ++ src/pages/reports/RidersLogs.jsx | 105 + src/pages/reports/RidersSummary.jsx | 167 ++ src/pages/riders/CreateRider.jsx | 71 + src/pages/riders/EditRider.jsx | 180 ++ src/pages/riders/Riders.jsx | 221 ++ src/pages/tenants/CreateClient.jsx | 81 + src/pages/tenants/Tenants.jsx | 258 ++ src/theme/componentsOverride.js | 121 + src/theme/index.js | 20 + src/theme/palette.js | 102 + src/theme/shadows.js | 14 + src/theme/typography.js | 25 + src/utils/format.js | 20 + vite.config.js | 17 + 62 files changed, 8729 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 public/Doormile-logo.png create mode 100644 public/preloader.png create mode 100644 src/App.jsx create mode 100644 src/components/EmptyState.jsx create mode 100644 src/components/Logo.jsx create mode 100644 src/components/MainCard.jsx create mode 100644 src/components/MapPlaceholder.jsx create mode 100644 src/components/PageHeader.jsx create mode 100644 src/components/StatCard.jsx create mode 100644 src/components/StatusChip.jsx create mode 100644 src/components/TabLabelCount.jsx create mode 100644 src/components/UserAvatar.jsx create mode 100644 src/components/charts/AreaChart.jsx create mode 100644 src/components/charts/DonutChart.jsx create mode 100644 src/data/mock.js create mode 100644 src/layout/MainLayout/Header.jsx create mode 100644 src/layout/MainLayout/Sidebar.jsx create mode 100644 src/layout/MainLayout/index.jsx create mode 100644 src/layout/MinimalLayout.jsx create mode 100644 src/main.jsx create mode 100644 src/menu/navItems.jsx create mode 100644 src/pages/Dashboard.jsx create mode 100644 src/pages/Deliveries.jsx create mode 100644 src/pages/Pricing.jsx create mode 100644 src/pages/Profile.jsx create mode 100644 src/pages/Requests.jsx create mode 100644 src/pages/Settings.jsx create mode 100644 src/pages/auth/Login.jsx create mode 100644 src/pages/customers/CreateCustomer.jsx create mode 100644 src/pages/customers/Customers.jsx create mode 100644 src/pages/invoice/InvoicePreview.jsx create mode 100644 src/pages/invoice/Invoices.jsx create mode 100644 src/pages/maintenance/ComingSoon.jsx create mode 100644 src/pages/maintenance/Error404.jsx create mode 100644 src/pages/maintenance/Error500.jsx create mode 100644 src/pages/maintenance/UnderConstruction.jsx create mode 100644 src/pages/orders/AssignOrders.jsx create mode 100644 src/pages/orders/CreateMultipleOrders.jsx create mode 100644 src/pages/orders/CreateOrder.jsx create mode 100644 src/pages/orders/OrderDetails.jsx create mode 100644 src/pages/orders/OrdersList.jsx create mode 100644 src/pages/reports/OrdersDetails.jsx create mode 100644 src/pages/reports/OrdersSummary.jsx create mode 100644 src/pages/reports/RidersLogs.jsx create mode 100644 src/pages/reports/RidersSummary.jsx create mode 100644 src/pages/riders/CreateRider.jsx create mode 100644 src/pages/riders/EditRider.jsx create mode 100644 src/pages/riders/Riders.jsx create mode 100644 src/pages/tenants/CreateClient.jsx create mode 100644 src/pages/tenants/Tenants.jsx create mode 100644 src/theme/componentsOverride.js create mode 100644 src/theme/index.js create mode 100644 src/theme/palette.js create mode 100644 src/theme/shadows.js create mode 100644 src/theme/typography.js create mode 100644 src/utils/format.js create mode 100644 vite.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ef869ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Dependencies +node_modules/ + +# Build output +dist/ +dist-ssr/ + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Environment +.env +.env.local +.env.*.local + +# Editor / OS +.DS_Store +.vscode/ +.idea/ +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/README.md b/README.md new file mode 100644 index 0000000..3ced04c --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Doormile Console — Admin + +A modern, corporate logistics console for **Doormile**, a last-mile / courier delivery operation. Built with **React 18 + Vite + Material-UI v5**, themed around the Doormile brand red `#C01227`. + +## Quick start + +```bash +npm install +npm run dev # http://localhost:3000 +npm run build # production build → dist/ +npm run preview # preview the production build +``` + +## What's inside + +A clean, data-dense but breathable corporate shell — fixed **red top header** (search, notifications, messages, profile) + **collapsible red sidebar** (260px ↔ 78px icon rail) + light `#FAFAFB` content area — wrapping **27 screens**: + +| Area | Screens | +| --- | --- | +| Overview | Dashboard | +| Orders | Orders list · Order Details (tracking + timeline) · Create Order · Create Multiple Orders · Assign Orders | +| Deliveries | Deliveries (expandable products, status tabs) | +| Network | Tenants · Create Client · Customers · Create Customer · Pricing · Riders · Create Rider · Edit Rider | +| Reports | Order Summary · Order Details · Riders Summary · Riders Logs (live map) | +| Finance | Invoices · Invoice Preview (printable A4) · Requests | +| Account | User Profile | +| Auth & states | Login · 404 · 500 · Under Construction · Coming Soon | + +## Design system + +- **Brand red** `#C01227` (`lighter #F8E0E3 → darker #7E0B17`), white-on-red header/sidebar. +- **Status palette** — success `#00A854`, warning `#FFBF00`, info `#00A2AE`, error `#F04134`. +- **Public Sans** type scale, 6–10px radii, soft `0 1px 4px` card shadows, and a signature **red glow** on primary CTAs. +- All tuning lives in `src/theme/` (`palette.js`, `typography.js`, `shadows.js`, `componentsOverride.js`). + +## Project structure + +``` +src/ + theme/ Doormile red theme + MUI component overrides + layout/ MainLayout (red header + collapsible sidebar) · MinimalLayout + menu/ sidebar nav config + components/ PageHeader, StatCard, StatusChip, MainCard, EmptyState, + UserAvatar, MapPlaceholder, Logo, charts/ (Area, Donut) + data/mock.js static demo data powering every screen + pages/ all 27 screens (lazy-loaded) + App.jsx router · main.jsx app entry +``` + +Charts and maps are dependency-free SVG placeholders (`components/charts`, `MapPlaceholder`) — swap in Recharts / Leaflet / Google Maps when wiring real data. All data is mocked in `src/data/mock.js`; there is no backend. diff --git a/index.html b/index.html new file mode 100644 index 0000000..c64eb87 --- /dev/null +++ b/index.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + Doormile Console + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6df2d0a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2667 @@ +{ + "name": "doormile-console-admin", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "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", + "dayjs": "^1.11.11", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.23.1" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.1", + "vite": "^5.3.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "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": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "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/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "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", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@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", + "integrity": "sha512-4An71tdz9X8+3sI4Qqqd2LWd9vS39J7sqd9EU4Scw7TJE/qB10Flv/UuqbPVgfQV9XoK8Np6jNquZitnZq5i+Q==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", + "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", + "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", + "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", + "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", + "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", + "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", + "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", + "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", + "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", + "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", + "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", + "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", + "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", + "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", + "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", + "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", + "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", + "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", + "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", + "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", + "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", + "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", + "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", + "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", + "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/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", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/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==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "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" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "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/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", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "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==", + "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_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/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": { + "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==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "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" + }, + "engines": { + "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/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.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==", + "license": "MIT", + "dependencies": { + "callsites": "^3.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/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "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==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-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", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.4.tgz", + "integrity": "sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.4.tgz", + "integrity": "sha512-q4HvNl+mmDdkS0g+MqiBZNteQJCuimWoOyHMy4T/RQLAn9Z29+E91QXRaxOujeMl2HTzRSS0KFPd7lxX3PjV0Q==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.3", + "react-router": "6.30.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "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==", + "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_modules/rollup": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", + "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.61.1", + "@rollup/rollup-android-arm64": "4.61.1", + "@rollup/rollup-darwin-arm64": "4.61.1", + "@rollup/rollup-darwin-x64": "4.61.1", + "@rollup/rollup-freebsd-arm64": "4.61.1", + "@rollup/rollup-freebsd-x64": "4.61.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", + "@rollup/rollup-linux-arm-musleabihf": "4.61.1", + "@rollup/rollup-linux-arm64-gnu": "4.61.1", + "@rollup/rollup-linux-arm64-musl": "4.61.1", + "@rollup/rollup-linux-loong64-gnu": "4.61.1", + "@rollup/rollup-linux-loong64-musl": "4.61.1", + "@rollup/rollup-linux-ppc64-gnu": "4.61.1", + "@rollup/rollup-linux-ppc64-musl": "4.61.1", + "@rollup/rollup-linux-riscv64-gnu": "4.61.1", + "@rollup/rollup-linux-riscv64-musl": "4.61.1", + "@rollup/rollup-linux-s390x-gnu": "4.61.1", + "@rollup/rollup-linux-x64-gnu": "4.61.1", + "@rollup/rollup-linux-x64-musl": "4.61.1", + "@rollup/rollup-openbsd-x64": "4.61.1", + "@rollup/rollup-openharmony-arm64": "4.61.1", + "@rollup/rollup-win32-arm64-msvc": "4.61.1", + "@rollup/rollup-win32-ia32-msvc": "4.61.1", + "@rollup/rollup-win32-x64-gnu": "4.61.1", + "@rollup/rollup-win32-x64-msvc": "4.61.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map": { + "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", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "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" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c94e1ab --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "doormile-console-admin", + "version": "1.0.0", + "private": true, + "type": "module", + "description": "Doormile — corporate logistics & last-mile delivery admin console", + "scripts": { + "dev": "vite", + "build": "vite build", + "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", + "dayjs": "^1.11.11", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.23.1" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.1", + "vite": "^5.3.1" + } +} diff --git a/public/Doormile-logo.png b/public/Doormile-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1cbe324359d358f60159f03d6dcd1ed4531b38b4 GIT binary patch literal 13651 zcmX|obyQT}_cln3w19LsC^f*)DIi^cf0wk3^N6U)^Aj z(x04j)#|z|ms4IOSl;MLjHbK|r=(YfR}t59g^!`Ca%^y)bUb$azQ^Qwd3&5r@h1nrkwq0Lw+5=5-HIv%Up7ZmE3+a94BTLV;lxNcvd z-s=UgALi(*@Lr}L95%U<Sg>AB0~nowuz`f*YA08^uvxtZZUmEJ1ZbfqgBie}4h z{v=uvk@5rW|JHXy=`yOBB}Bc2qMMg>ckctak$;G>JSv4 zzQx$B6KOG)yibY5=7Q2%nH404&!Ab$g5gosI+jPMsT(4oWQ+Jjb^rHh(?Q|+I6#~m z0&&tCkllR6N2Jg{xVv-Nnd>%gk(R0SS~Hh3ow*s> zgWW{JI}7uO2fF}GYC|YKD`Jxoxpnj#ulvky>X7GrUP-y;J6}VoPpfak z@98E_uVf={psJMHHrtkre3r8dm98Fj)0G%PrbrZJ_GB5ZVP=1gBy5QId9qh!i`4$p z(2-31AIMqFo@N&I;RN4!V?cw<>HHJ`+p89r+mSm&lkfha1gLY7Kl^GbRyexNKZMsC zaw?y8=aO)!v4<8TCJoi~x#UqUOte3f+wj%+YExV~{$5XkWhQ(;nhH5ca zSc2Py{Tu8eL!+B{UGPie0f4iCz#qAmjO6;!A=$rfe4nWqbZEUN#N=r?8zrQx;hWH- z(80!v82)#IQ>#g;q=h_1?CXidK8|PG*^(->@iIeARjl_PiEe88EYCk{4>`nr0zHd^ z2`_LR6s=XsVdiJ*%1f0d9odU4M{7y%UUY*5(@(BqhK*-g69e4-c?l}&?jLp@UN|5@ zL){FVAnhtCyfrwv^`9jZyJ0MM;BFDnL!4Ar*^@4Zuz~rjNS#^bkHTZdjpk$lS&;$s zOi($Km7N4`50{VuoZxnf5GA)=_JR*9ooMJZGpCQ*-%3R{G8#LB9_R0VvW=epVY_<9 z54I<%X_Y;t5Q@BLmLwPW8~5S=M^$EWNFh z#Ya#X7qqZ9>tfDv7BVC(nXpAc}5W@08!-W!84mi(lero5A>_p<7~ zKUY(ON8CE<65d_Fia$A&mKdMbk0(Sx4xS~nQ)@Vbp5JT19-+j(w-O9CFp`|b`~89{ zy-^#`fG!TIb5x{ZNxMz=i23k?xx0@Bs<32gl^*Tr@T?+HRk)EH1`cYK+$dnL(Hle?cUvx@$X}%fLo^`!ogXLK+qn0> z*z~VY5>O!x$ti~%+;XO4vW+_B%lRlGCde@puuT|nB2bup^w2c?Pf6-P=$}GowsJLc zAE3A0k*I{+g23B$MJ#MHgzsSNKX>3O`+K&PLaUH)#0PRI&z$ zp5V2Bmv>GSWhtguqx_(F6(Gh?MEXgTAE$1JJ9(L$Pf^5h_44P2_Zq115mz)wkyKe^ zXV!BNB-eaHiK>e6GKbMBIPGAAC9I2D6w~dF|2&X2x;&HOt|2CkGaNj36CXOMgZ4yh zb|jEM4L?1j4*5nST&|GjS;#wbOLaYP$bVlfabJ9rRLk%Sr09*b@P&spQ8V0R)>b+~ z6CKMlGVGfS7uyW6;Qc*C6pW_!h+HQGYa*ZwbFa>P7LK_l%w~Tafl5zM$bSL%qF9{9 zpIZ(JFDo*g1UTdyEuiL}p)4p1d9{=lD_p6qYn;!JMCkLYB9(jk zq)V4tG?{H>W?s`Ar|mxXs0Ry2JUko-ywZ=__uzMiPA83+() zZ;j$uP|tmq1WUyefK1D-f3*@`Jz^kpCa*4yb0*g~il0L>oT~7U8E;hU&fQyhy=_s!1Lo=64^F3;ZtzhzcQuYwCQ!rQ?6IksiO!Rgu2#Os z`16mdXv4A{)o+mb@QoY9Qejl|K4b*d@B-jF|NNYt3GCf1_bA;h5P*W48i?%LHUHai zm`OPKvDc|-8EY^|)qcK7U6tt;sUKO871wph|Bfj5l_fNvII(b@3L9f!A)cS3-N$Ua z+chDHEk&g0`2LDlP$?jkvbIpIP$j%snITZC_Bx?2rx-B~qxIViF9fFT+ug2Vnp>vG z+iR)8zVyIoAi$~oH8!K&htp-8d;Se%_sFtEdNqZtr8NxBlX}r^{hr@2>$!>9RtXzc zNEkg zMWhC5L^VEK&*`Wn$APK~kw}fOflW{KWBPqd2K{6a-uzl;*vNZqsr4CiEZ1$i+ORnl zsos!Wp@lwubOS{iAe~khvEVm+1mk<{2+Rw_QYc1rVv;}3ZknnWVpdV7_LP-(pfS{Y zuLcg%s9EE75YcUFheC|6f%OKGcPrD@`$?z&*JXR>EgWy{O9$$u+$&11e#h5)8U@^M z`Nd%GRVKtek8i*(EGsDcr(mnTjk9aH81UYeW`Uni=wtVf($E}`$L%@g(jW9=xW(&@ zoCD+C^=G{(nuWa#Js>x> zJ!bl+VY7_0?jGa5&Xx-BEOpl`VXdB>!YbI2S$7gMUnIDdkQm^f?mqI-sZoJcb@RGf zCR1Fu^DVw|nOx_qY)G1hDY4H~J!513?? zj9lw;t^T8yoA`R^YVYx)1bQfTw1yvdn>Q~MV=AWG()mG%28y&me?X?|uF*&U&I9l? z-pbnNkBDNBccw7GQKpU0%({HCgi-sc zP3(Gtx`ovv!*X7f1@k`-NhyaYG;W6tx9-XATw*Zm?R;jewG@H8I(MKMtj8O!EGH!< zhe#dou}~gnh%RQJwfl9KoV7(vLXw{ZYS(6+M_g-e?i87b0U zp|u*rtgm?Ic+a&XO%Af47{D#nZA=MT{I-bD>oE37WsruO129|ao8sasIWtW3*P_ut zXi61~HFftb^99#rLQ*-Ux#}QntNV}RoWfL++zL}sEq$)Z(@Nh3C+YrIZb;--V3`N8A$!WJ&)K?KI>@Aj91g<#hQ+!C+$enf4+t`txNu-%9sB$o4oLBTC*Ny>h`$`lanBnc!8-D((scD=;oms>HtbrnX@g^8rW^h*?OlY4^FW^T~PV{ z(ijAHHYR}^fA*&+kMgZ7_Q%Zl&txK%?K4zpQ)pOw}AqAxMvf8Xhv+<>KBrY_VU zH&GRBZ95|B^-*;S$Nz&m?!`2)Ux5aZ4~>+!1WN#IPeaRhnULIT#TTbACJDP-EwfvA zt`#e8H{mQR(V*-HvDap?vmWs~?$fuVu9y$_f`#91axA93ya7P`>fQU-U}smx(-Bf} zStne}Y!L+)+KP~EJ4Q0k5X6N($E*Bxgrz1=&q7o{b5Wq?#Jhf!Gb}< z%8lkh2|UA2hgGDnedw9=`=m3i%5mhG*mNa?Lake5V!dnZ!6M0zdUiWJJ+nDtJdCvA z!!?mQW97s4AuGJ1s?J_y;}nWhEk^?sf}5_gI}Bnor?1?#NJbKt@5bC3qWRTFnXmUJ zPLUFJd47Y{nuUm8qIvKX;Fx>*(;{uX{PKGz0lCS%E^7FK4-_E}D_Yvv^FM_w2q&Ia z9j!a@1z=_{Y5bchT$hM|4NiCHu<3(T-cH(hG|ckM$Qrg`SnQRt0K);wbtu0_dxp43<_fqV`wOGkh;yBiXn|h$)VR2F437FLjM8o;6z#E z5A%3ydt&W7k2l7@FD8uQ@^r5cQKh|tXyh#~bS@J~5^qb{o#N-}=ddInIpfnkbL7y$ zb6?^tCTbNq+vF@sQ@v$BxkfZvDs{Jgt5ej5G zt=hurh`GKucJw3Q9jUKj5^7v)*pp#>cQ#}ILw&-BzUm?fOkc=07}?Sr3{xPzuEoeHDnV=XR2uGl_yd;rMINs*kK z>>pRTTLS6CmV|=O+n5&-AxmzgRY)Q_#mneB&NXcxbO7Od5C-W%Xb`QU3n3i1+$U7C zAFxrMMQ`l&nX3(mD8Lr8NXh=U~BOQd9E*`SHvtH{bua@Jgnp3bNrj@hMGx{4} zw+9|s<~xG;5^^gBaV|T{8K_fH_MK{sqaxO$gj{O@R_nKrwvrs3*bOWn=Rl3F!E-7_ z0->Pwp5&*hdg^^>lqiz!=-n2C=WYiQYbaFEN85vt?QSJk<1E$s!a+G@uwA!b-O0B&^YZk-Fm zTL)sm9q{^!E4~e<+o|D1aG{_1$2GPhEjHp02D`n4p-9GFYZ=m!AM8=YfFD3@M-ii{DzI? zzX)ta-;fyXI4;Q92rZ=lzF_C8JYq#<4RWL`^64F!V{PvtGKOWzY{*Fb#o#cm>Xb_+ zE_qv4Wb!b;bvG%0eWQ+fctAl0NA|N~VKDIom+VHcT{)F+r9ti}_-2uE#F70;M5LS{ z2)rBHL%gfOahD1Z2#=zah5E~VvPN_o@~F_AT}}|Y*GhA93ccx$!XjA`6`$)v`(doa zAb>vSAw*W?EX>oHMc?mJ(5}zONPoO*FG#<;tB5uM&}W8>ZZHJktd?O@Ev5PoDK@#(CDap zns5sf4|6-{z%;e?q&QFI&KqxAY{ox2k=iHv*oT?GlZoJCbNS_j9|8RYi+vSefc2Z{ zv}8Z{P4TBi}pib=$FDBkY&8-|19K4~z(1VrJ<2lHPEP#TrncMLKT? zRq722e;;A!5fYxGRO_*>6Q*;H!`O?CJr6_Q`<90Bp_`8&@Z%Zj_blw7ovltK`L`1D z=**qkSeNFf&;m?5G$B{Gg5rlxBOy4JG@B)udR?JNyank`%>oImlcVR449Ek8by<+Ba)^S9LiTv)MDtvXNI9n>-lCZ$#%+$DqoB zuz=JRX?(hh-A~&#dhi5jM zp*0eZkIssG#5d)dTxfJg(0=>Y1$-w2*MN*6S+U9eslU%z3M3ubYXlJX1Q3m zDiOp*V>m)%SQc1F@y9P3?@8?TgAnvN->y_tl`Y|YH|-BY5?UHV%*K|4@OqN_+)fe2 zzqdO@fHyhv?VC@Pg*(e5v+}m6thzF33y#Ht9k6K#?pI1L}N23NBQ#>J#*OhJSQ~Gmo6ZsjV%>>i2g ztPfp3b7>be#);JtQLSwwYB#c&J~w*z;7)L4nmmdD`~Vs^@UkK$QC--E8TP z>|-Q@qqnuY8M?=5W`gA9neAlYvq;+^YaOqh`dtaZ*p>~aw!$5viq5NH#oz2-1ha_G z-5VW16=R(P#Th@nn&FCtWQGUwty;vKPof`_E<0fkdf(50FENn3b)kr2t3m?hEDaSb z=0hHV0x+s84iXnfOnlDbyzIq+>6CPvHSjlCAEoR#dT!Fn;9yVVO@j|=k(3RZc6+(> zXZ-Tnx>hxnb3cI3?b1Apc~5|lu5AM1G6KJq%uYNVKkvQSDl}hQ96(bv;PII{!Qx0} zva}gJUE)?FrrM1_E4ai`wCV@w7CuPha+_hc1~&+B#%Xy}v?#Iwn1JJdR64$=meB3J zceRj49M+lnlF7!vt$^Iq!~J!rt0nTM#6nVe8C*3P0B*1P+kv%ej^!hPSdWMN6hpuw z=PuGd%cZQ^8{MB^>W!B%A;Wi}$h{kYh7;~ghUaaDH+7Jl&-phd0*P923%0-c%yYCv zxk|H@C5*$yv9vlisBYDJ%-)hXNI?m;uuncp@`g4`;*~}&KFy$cClc??AAh1u(^7sx zZE;zv{7BXhFq7xtI*5GSFY6MOfGjVqDqpNE15%E5pGQ+)9L=Yx_K^yAP$Xx$fUoV0 z0Yl-T{DUm3vM3q5A&>L7I((-*!6;iYSw3)6t5hC3az@;tf$2sWec}<>aE5Wl()ZDg zck<;2AEdU8Ln@db#c!3^&)<3-{1JcozGIPqOr(hq%7!26Y-qFL2gg15W2NP!tL(}?w3M984?LoNhu@=R>Hws&5(^(O+ ztU9&!iUyvVY$~RbmV^0`>%q=-n|1Irljqi*?iv=aB@k9rIJ^a+xUqpId?198Pt}ky zdU%Y`B_)aHY^qeDU2RKksOfeTZd8I+@G}jkwJqmXMc`=^)(Ra^>=-Fz7@BYVN2dHn zew(J%)@ET_)LPefdv{aJ-M@kwVwKz?D2u@KO4|$W{8Ju~YWb%pNlG-t4CtN^tx7vJ zbh@DoY|y4AYTFq~SAoP-if2wi9|lJl-yEWapC;s-{}m6mvC zO2uni$|sWrW%8WH!;1<)xghA>1`=o%g_oaiG{e$}>$IP2J}+Q=>4B7Mj^2*Nxhv&9 zJoJopOIY7|q4WymJ`C?Hd)lO(xuH^i854Czj5*6QPX1ftt-_al2x`VXtDm)ybGKeI zjLV89qlmee2Ga5ZaC72GP03uJ(&j^1BXfPnc%uSGE( z)WIqTodh4EA+?zWeoM^&(#}cOp~o3LG`^wk_a8oG9VKo32-~tBL?Z1klQRr1>v(xo z3>Vn3U1OPZe2!BXl3cE@L#nf0<_)grydEj=)(-P!%Pu$VM$k3CfCYsW-B~kkLd>n4 z`2Cr(6IWAOu5z`p|8;ajQES}g_r#$LWg%Vj2gDE4JBa496I}ij@eRCFlauPXH_--t zbtqDH=d~yj64z%HxMvF-EFS(3^OVie(fFjwq<)2c=G7ewI$6Uq9PVQld{^422HAp(&|4v) z0IZmNd8Co(y-A{I^FYkxs@>qlsI-?m8dZSKy#!Z5!}hP#Z|N9D5L^>I-TZDYx8&=- z*`U^#LhF4bKhOHqn&!@nDuOLelJhPC(}OZdy(sj-J{(`-t5W`~RL6GO9SRxuFugj< ziMQr04`2dX9W0^H91Gge2$$3``_Lqmj~3yD_lL&)o}&V7f+)&Ef64h&$2^|ZJrk$pGy^f zMXYt__GXnwFaZ=&*oQTK~2j2Cr`5*->}Na>mL z#~_k^T-`2{C1BkyeINum)@ic@gKK!ANCB=$Q8~&DQpNu%VtTWSihTxZGA_(jai1d8 z00n2dW6D7=GEnknr8)me5COZ>?yqyJ-RT%gdPm^x<*5p`-aSJ2dJrh^G-yBvOddw8dJG@ft*604phJ|R z`V|&;1cM2#>pF6#lie_q7w5o;K75#D*e7`#GkyGuQB|5F?R4=#2!--0v4b7DTq!L57%(19*AC@X_y}|hK2UIXQw5J`0 zGgH8Dmrq|xS(8wHW|DSFf&XZD$Vx^eHY4=>ti((9@%eic#!7eXzeY>qpl?~~(g-AD zBg(yAj_8`&IqPJTd-NAIcPb7^{*Y0k?fFa>J{3PYzvH~yd~))?MgJQz5`3KyI_{YF zCIT-|_myzm{Ze**uqaG?<)~XX1`g)F%&E?;?LJ4mXUtD$uv@r35ImnrW9IFFzkTw* za{asLDI#)S7Dsgj%Pm(5=@I*KwjF7NXOgs4Dqo|#wb0s0{sPeKB51z#Ci(0vZpbox z&i?-w`&Aq0$$<7}WartVSJ!2dQ=8Bp2n}14k2gFI`VTmNTtCDqaUJf>_-tpu?>-zr zgak&1wy9pi#uUgp*sIUf3-=u|-0Zeuqa>2XQt>fzGbv?8Uk^3x3o9^U)oLyDbKU+k zOJZDO@kKm7!TwYWBxN01B%K=QQkde8>qn`p$&9W|EqI<9HO4b{N+A;#H}Yv~z@6k? zi{ux|=-C=YBaBX|*grX&jIu^{)Dzt!=MuR~t)}DM(gVREIxyR-aWuSsn+smLFTaez zAwjJdl{{RrMW!MzMdpcZ#26W7i2r<7X3Eu&pl%SM z3(wdL<2*;q!#ajLGa;MWgj!2cy5UC3{MO~m;L*e3ajW77VhzLHJKZ_5CZMC&>&=-Z z=e^XIrhC(Z#OV`9Fs-RXAkJSb?-p)DrG9D>vOK|@BC(q8f@TMT1BZ)za6UUrdl8Pe zF$LY#4+T*^S>IU?aA0FRKs&Yl8Y6Ytp( zC#wZUn~T&Hez$`cz-SM=ptKW5yH7hYRondwP@2X@+`BT(S(AJpV?MMxYzGX&-OSu# zg(oRE%9P#hrR8l5X1kS}3Z}b70G?z;yw%^cLiyn8Y#_?^b`lZygjy4YFZPOj1(g;0 zK4K>f1)k&0?=~)`zAszmuQ|h;U%0fEv@xAL(9YBY_tO_3aICJ52D1F?V$RkLHqMR@ zgF{Srcbh6)wN(cQq)7X=E1|akag1=kd(>P0+RtLDkV)b>*qb3j2L1 z6E{HWvzFK_TfMQ&1D7K+>5^Rp3%S)c-Xi|8y8}yks^p{?&s2Kl)zAplqMPhE#+PBL z-297m%s>Bovsnxez2rdIT47OvtGd+Z6_u_sM= zIB;bin{k0yRxeiix*SsooGj?1+!=Pyq`N7N`1;3hLUB?;M-;?3dNAbResM;4Z*F56 zhSQ>q%l`uTyx;83@L1X_R*`SCbdna2SlPwOEn*+?RKi=z7)liw9Rx0*n1zs zrpMERpWpw|P+y?kZ`9js4A=t~yWih1lHAlgo+TgaA~qpdI^2zj^PhRB4<9&alF}3G z?B)9!jucq2yu1z?9EL(hh-E7+H5Gb(UG6YjBZGnjf7@8ZdDBlU-u8a?N{?DAn#*&( zFV}`VW)R9HRQwA>>FiL)4}bQ$C(c5zJlD-VfhJvn0-k0F0>G>aI4@Y;iKB4qKud#i z&rm+XdaXF+7$lK}Fh?X;sl*>^wqb3x1qx3w8~z1HeMw5WRO&1FiZg8|nr~MyOq+(8 zw>sjs#V9@x*Z@Lh#g%b=|8rw};+lGJ{C+#5ukMa~ zi(q{4zfg}1SKd>2pI%f}-~+C`9-g?S96Wzbs5C65zmtBPF*=Tg5FS7tk4v^Xc$hnR z;4H42dbh6qFVHc@yIIejism88B^NbhwFJ&(Qlqt!zlVY-eh4_d9LiXWJYzh(m%k!* z9_niyT1eh1nznI&#V@Ehd*xM82I!){=QtYe$^TrPY<$7-Ie9VPb6EGX$g6|&7lz;r zHr2gwCC_SeMaX_hKHSzLm{2EkPSa0{$^S`uslGy?d;bt^$BCU#%K>H1WVV+I1$|kurJ5ZySjph>lX%V>I7;+|MKPHjeWTUyQI8_ zR@>lOieAS7VeG@J&#nksGP?dLu_th_&9~5B`IRO02Lu4?mEQv+nqWF^7TCrE(XV(n zGpIuU&Gx3T!4ZvY;+4KAD=f5{@L$x?JDsJChyB8_^Mups?epq*ZtO|PC4^L1?A9pl zZT;E&G`;}gQN%t&VT9(%<>8^X6L>th0g7(wXa&iBRX$xF!nEy149^S%M zcLGMGbN$dl!E6+8g~oJ9$hLmWdKyE%Gq#;_&UChC!MKNYtLfA*_!~v`Q6KlzeKTxV z9yA_a(>wwk_w59(RL6HQJEGpFST|FZwiMU#)}vIgN>Px5CP-O{+OI@TH03&r(W|}_ zK$FGz>i{dbH}KB<;p%-wOK@}7X-VQ)`A=qN$qy$)s6V?O)-AjNX)Gt>X34yD+k8n{Zn8B>DmtmCQ* z$6n!j_E{Mp5oNMA!eV{CoCU65MQ2>9J>>`5s)`mwgr@xdNW3co-dO8( zpREB&*|Az40ke_9%2|z#2NW+mH3eM``cmH@W z3}Qz6cx(ugi{-S=97;v$9n!)yrO=+^2dkq)TR`N{C#Y?w%BZ77<+Gof1UBdpbF`yM z;cl+r0ueF=Q`p{UsprTz6#!*tzCEhK>RpfNVCU+I+bDNOIb~=;0)Dkzxsng2xP0Bz z^QA}i{M!CcNHdZ#S!{Hj84@44{Y*#5wqZgS+`5fh20RL_k%O0<}+Gy6$_;5@?4 z%I<5@|I5|#2yXokW2;o4_a%5Tb%^yz{o3>4i@wwakSs$rW3N97od|u z^UNG-8kfFQKqDxY0+T1IvVLtL{MA-eSah4R?Br~XI3shBig{~%y1Idu6g`h>QzMY_ z?&asIN0|OO7hF@%6$syQ`Y80@!%cl_nR9x#l2B-R?q(f{Kl)hA(h1P1)&t8+W;nH{ z9timBohN^SzmtIxwvAGd9hSbcY_`HDGPAs6O{ri7vNV;ypkdkd#V9_a3LaVy)950o zGz0DQQ7gCix`qm8S~+@FSgb5G5=#QL+azZTs6pwrmix}xoIN3gn-qo#TdhWlL(BPx zo3lORsmCAPX$?ne3mz|(f3mF3&Wr_}j~|$KuS)5tl7*h}1+%99;=grg{rHbu%g4b^ zLwJg!TFrfEdn}`2VhK?T^<6=i^M%x)h!5RSeMRu8-`YSq+v9E zCCNLe$xzN6uKw_n-4kRsdoZ0xYbL3VX|-E_hzW#a9#{9sMpw7Bt@?^`D-A=j$259I zQbM8i<)H8JYk(G6-yLCTJCBT*eeUx+=iyCF>(?Dv1rgO=eCSHHZrL7eKW`RR%zjV+ z9ryW+%< z?jew^lj!v;|{%D{w7 z_(}2Xwhy_oNE6)~&Yw^1O!N=1snux+Y`>WcDDWdmzW z!%ntOytST;Tg4Vnh3k@SR=*MvXqgCnUtsVwB-rNGa7lXl)-Sf=c_i6#L)|BX+yuy$ z5d?;6kE3K=){W`rZ9Y2ON7oxn_93OiOz|sZ79OP@vWxUowIvqF)`wZE@~lZ*D764Q zGx^q+-A@&pSmr1Iri7_W%F@ literal 0 HcmV?d00001 diff --git a/public/preloader.png b/public/preloader.png new file mode 100644 index 0000000000000000000000000000000000000000..92cb60e0c521d2b4166ebcbbee8e1addec4d9c47 GIT binary patch literal 8207 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 Bdj$Xh literal 0 HcmV?d00001 diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..a50e7b9 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,76 @@ +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'; + +const load = (factory) => { + const C = lazy(factory); + return ( + + + + } + > + + + ); +}; + +export default function App() { + return ( + + {/* Shell pages */} + }> + import('@/pages/Dashboard'))} /> + + import('@/pages/orders/OrdersList'))} /> + import('@/pages/orders/CreateOrder'))} /> + import('@/pages/orders/CreateMultipleOrders'))} /> + import('@/pages/orders/AssignOrders'))} /> + import('@/pages/orders/OrderDetails'))} /> + + import('@/pages/Deliveries'))} /> + + import('@/pages/tenants/Tenants'))} /> + import('@/pages/tenants/CreateClient'))} /> + + import('@/pages/customers/Customers'))} /> + import('@/pages/customers/CreateCustomer'))} /> + + import('@/pages/Pricing'))} /> + + import('@/pages/riders/Riders'))} /> + import('@/pages/riders/CreateRider'))} /> + import('@/pages/riders/EditRider'))} /> + + import('@/pages/reports/OrdersSummary'))} /> + import('@/pages/reports/OrdersDetails'))} /> + import('@/pages/reports/RidersSummary'))} /> + import('@/pages/reports/RidersLogs'))} /> + + import('@/pages/invoice/Invoices'))} /> + import('@/pages/invoice/InvoicePreview'))} /> + + import('@/pages/Requests'))} /> + import('@/pages/Profile'))} /> + import('@/pages/Settings'))} /> + + + {/* Full-bleed pages */} + }> + import('@/pages/auth/Login'))} /> + import('@/pages/maintenance/UnderConstruction'))} /> + import('@/pages/maintenance/ComingSoon'))} /> + import('@/pages/maintenance/Error500'))} /> + import('@/pages/maintenance/Error404'))} /> + + + } /> + import('@/pages/maintenance/Error404'))} /> + + ); +} diff --git a/src/components/EmptyState.jsx b/src/components/EmptyState.jsx new file mode 100644 index 0000000..32b6c5d --- /dev/null +++ b/src/components/EmptyState.jsx @@ -0,0 +1,33 @@ +import { Box, Typography } from '@mui/material'; +import InboxOutlinedIcon from '@mui/icons-material/InboxOutlined'; + +// ==============================|| EMPTY STATE ||============================== // + +export default function EmptyState({ icon: Icon = InboxOutlinedIcon, title = 'No records found', caption, sx }) { + return ( + + + + + + {title} + + {caption && ( + + {caption} + + )} + + ); +} diff --git a/src/components/Logo.jsx b/src/components/Logo.jsx new file mode 100644 index 0000000..723d4bd --- /dev/null +++ b/src/components/Logo.jsx @@ -0,0 +1,51 @@ +import { Box, Typography } from '@mui/material'; + +// ==============================|| DOORMILE WORDMARK LOGO ||============================== // +// Uses the brand wordmark asset (white PNG). `onDark` shows it as-is on dark/red +// surfaces; on light surfaces it is recoloured to near-black. `compact` (e.g. the +// collapsed sidebar) renders just the square "D" badge, since the wordmark won't fit. + +const LOGO_SRC = '/Doormile-logo.png'; + +export default function Logo({ onDark = false, compact = false, height = 26, sx }) { + if (compact) { + const mark = onDark ? '#FFFFFF' : '#C01227'; + const markText = onDark ? '#C01227' : '#FFFFFF'; + return ( + + + D + + + ); + } + + return ( + + + + ); +} diff --git a/src/components/MainCard.jsx b/src/components/MainCard.jsx new file mode 100644 index 0000000..b86272d --- /dev/null +++ b/src/components/MainCard.jsx @@ -0,0 +1,17 @@ +import { Card, CardHeader, CardContent, Divider, Box } from '@mui/material'; + +// ==============================|| MAIN CARD (titled surface) ||============================== // + +export default function MainCard({ title, action, children, divider = true, contentSx, sx, noPadding = false }) { + return ( + + {title && ( + <> + + {divider && } + + )} + {noPadding ? {children} : {children}} + + ); +} diff --git a/src/components/MapPlaceholder.jsx b/src/components/MapPlaceholder.jsx new file mode 100644 index 0000000..0f5e2e1 --- /dev/null +++ b/src/components/MapPlaceholder.jsx @@ -0,0 +1,59 @@ +import { Box, Typography, Chip } from '@mui/material'; +import RoomIcon from '@mui/icons-material/Room'; +import TwoWheelerIcon from '@mui/icons-material/TwoWheeler'; + +// Stylised static "map" surface — pins + red route line. Swap for Leaflet/Google later. +export default function MapPlaceholder({ height = 360, pins = [], showRoute = true, label = 'Live Tracking', riders = [] }) { + return ( + + {/* faux roads */} + + + + + {showRoute && ( + + + + )} + + {(pins.length ? pins : [ + { x: '18%', y: '78%', label: 'Pickup', color: '#00A854' }, + { x: '78%', y: '22%', label: 'Drop', color: '#C01227' } + ]).map((p, i) => ( + + + {p.label && ( + + )} + + ))} + + {riders.map((r, i) => ( + + + + + + ))} + + + + Map data © Doormile demo + + + ); +} diff --git a/src/components/PageHeader.jsx b/src/components/PageHeader.jsx new file mode 100644 index 0000000..2cc3a61 --- /dev/null +++ b/src/components/PageHeader.jsx @@ -0,0 +1,42 @@ +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 + breadcrumb + actions) ||============================== // + +export default function PageHeader({ title, breadcrumbs = [], 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}} + + ); +} diff --git a/src/components/StatCard.jsx b/src/components/StatCard.jsx new file mode 100644 index 0000000..5adb385 --- /dev/null +++ b/src/components/StatCard.jsx @@ -0,0 +1,55 @@ +import { Card, CardContent, Box, Typography, Avatar, Stack } from '@mui/material'; +import ArrowUpwardRoundedIcon from '@mui/icons-material/ArrowUpwardRounded'; +import ArrowDownwardRoundedIcon from '@mui/icons-material/ArrowDownwardRounded'; + +// ==============================|| STAT / KPI CARD ||============================== // + +export default function StatCard({ title, value, icon: Icon, color = 'primary', trend, caption }) { + const trendUp = typeof trend === 'number' ? trend >= 0 : null; + + return ( + + + + + + {title} + + + {value} + + + {Icon && ( + + + + )} + + {(trendUp !== null || caption) && ( + + {trendUp !== null && ( + <> + {trendUp ? ( + + ) : ( + + )} + + {Math.abs(trend)}% + + + )} + {caption && ( + + {caption} + + )} + + )} + + + ); +} diff --git a/src/components/StatusChip.jsx b/src/components/StatusChip.jsx new file mode 100644 index 0000000..ce201e2 --- /dev/null +++ b/src/components/StatusChip.jsx @@ -0,0 +1,54 @@ +import { Chip } from '@mui/material'; + +// ==============================|| 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' }, + // riders / tenants + online: { color: 'success', label: 'Online' }, + offline: { color: 'default', label: 'Offline' }, + 'on-delivery': { color: 'info', label: 'On Delivery' }, + inactive: { color: 'default', 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' } +}; + +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 }) { + const key = String(status || '').toLowerCase().replace(/\s+/g, '-'); + const cfg = MAP[key] || { color: 'default', label: status }; + const tone = TONE[cfg.color] || TONE.default; + return ( + + ); +} diff --git a/src/components/TabLabelCount.jsx b/src/components/TabLabelCount.jsx new file mode 100644 index 0000000..8fe1b99 --- /dev/null +++ b/src/components/TabLabelCount.jsx @@ -0,0 +1,30 @@ +import { Stack, Box } from '@mui/material'; + +// ==============================|| TAB LABEL WITH INLINE COUNT PILL ||============================== // +// Renders a tab label with the count laid out inline (not an overlapping Badge), +// so adjacent tabs never clip the number. The pill highlights when its tab is active. + +export default function TabLabelCount({ label, count, active = false }) { + return ( + + {label} + + {count} + + + ); +} diff --git a/src/components/UserAvatar.jsx b/src/components/UserAvatar.jsx new file mode 100644 index 0000000..1e150f6 --- /dev/null +++ b/src/components/UserAvatar.jsx @@ -0,0 +1,12 @@ +import { Avatar } from '@mui/material'; +import { stringToColor, initials } from '@/utils/format'; + +// ==============================|| INITIALS AVATAR ||============================== // + +export default function UserAvatar({ name = '', size = 32, sx }) { + return ( + + {initials(name)} + + ); +} diff --git a/src/components/charts/AreaChart.jsx b/src/components/charts/AreaChart.jsx new file mode 100644 index 0000000..96615b5 --- /dev/null +++ b/src/components/charts/AreaChart.jsx @@ -0,0 +1,66 @@ +import { Box, useTheme } from '@mui/material'; + +// Lightweight dependency-free area/line chart. +// series: [{ name, color, data: number[] }], labels: string[] +export default function AreaChart({ series = [], labels = [], height = 260 }) { + const theme = useTheme(); + const W = 720; + const H = height; + const pad = { l: 40, r: 16, t: 16, b: 28 }; + const innerW = W - pad.l - pad.r; + const innerH = H - pad.t - pad.b; + + const all = series.flatMap((s) => s.data); + const max = Math.max(...all, 1); + const min = 0; + const n = labels.length; + + const x = (i) => pad.l + (n <= 1 ? 0 : (i / (n - 1)) * innerW); + const y = (v) => pad.t + innerH - ((v - min) / (max - min)) * innerH; + + const ticks = 4; + + return ( + + + {Array.from({ length: ticks + 1 }).map((_, i) => { + const gy = pad.t + (i / ticks) * innerH; + const val = Math.round(max - (i / ticks) * (max - min)); + return ( + + + + {val} + + + ); + })} + {labels.map((lb, i) => ( + + {lb} + + ))} + {series.map((s) => { + const line = s.data.map((v, i) => `${i === 0 ? 'M' : 'L'} ${x(i)} ${y(v)}`).join(' '); + const area = `${line} L ${x(s.data.length - 1)} ${pad.t + innerH} L ${x(0)} ${pad.t + innerH} Z`; + const gid = `grad-${s.name.replace(/\s/g, '')}`; + return ( + + + + + + + + {s.fill !== false && } + + {s.data.map((v, i) => ( + + ))} + + ); + })} + + + ); +} diff --git a/src/components/charts/DonutChart.jsx b/src/components/charts/DonutChart.jsx new file mode 100644 index 0000000..b85c52a --- /dev/null +++ b/src/components/charts/DonutChart.jsx @@ -0,0 +1,59 @@ +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; + const r = (size - thickness) / 2; + const c = 2 * Math.PI * r; + let offset = 0; + + return ( + + + + + + {data.map((d, i) => { + const len = (d.value / total) * c; + const seg = ( + + ); + offset += len; + return seg; + })} + + + + {centerValue ?? total} + {centerLabel ?? 'Total'} + + + + {data.map((d) => ( + + + {d.label} + {d.value.toLocaleString('en-IN')} + + ))} + + + ); +} diff --git a/src/data/mock.js b/src/data/mock.js new file mode 100644 index 0000000..72b9b53 --- /dev/null +++ b/src/data/mock.js @@ -0,0 +1,181 @@ +// ==============================|| DOORMILE - MOCK DATA ||============================== // +// Static demo data powering every screen. + +export const locations = ['Bengaluru', 'Mumbai', 'Delhi NCR', 'Hyderabad', 'Chennai', 'Pune']; + +export const tenantsList = [ + 'Freshly Foods', + 'UrbanCart', + 'MediQuick Pharma', + 'BloomBox Florists', + 'TechNova Retail', + 'GreenLeaf Organics' +]; + +export const orders = [ + { id: 'DM-10241', tenant: 'Freshly Foods', location: 'Bengaluru', pickup: 'Koramangala Hub', drop: 'HSR Layout, Sec 2', customer: 'Riya Sharma', qty: 3, cod: 0, kms: 4.2, charges: 86, notes: 'Leave at door', status: 'delivered', date: '2026-06-04', payment: 'prepaid' }, + { id: 'DM-10242', tenant: 'UrbanCart', location: 'Bengaluru', pickup: 'Indiranagar Store', drop: 'Whitefield, Phase 1', customer: 'Karthik Rao', qty: 1, cod: 1299, kms: 12.8, charges: 142, notes: 'Call on arrival', status: 'active', date: '2026-06-05', payment: 'cod' }, + { id: 'DM-10243', tenant: 'MediQuick Pharma', location: 'Mumbai', pickup: 'Andheri West', drop: 'Bandra East', customer: 'Neha Kulkarni', qty: 2, cod: 0, kms: 6.5, charges: 98, notes: '', status: 'picked', date: '2026-06-05', payment: 'prepaid' }, + { id: 'DM-10244', tenant: 'BloomBox Florists', location: 'Delhi NCR', pickup: 'Connaught Place', drop: 'Saket, Block C', customer: 'Arjun Mehta', qty: 1, cod: 450, kms: 9.1, charges: 110, notes: 'Fragile', status: 'pending', date: '2026-06-05', payment: 'cod' }, + { id: 'DM-10245', tenant: 'Freshly Foods', location: 'Bengaluru', pickup: 'Koramangala Hub', drop: 'BTM Layout', customer: 'Sneha Iyer', qty: 4, cod: 0, kms: 3.4, charges: 74, notes: '', status: 'cancelled', date: '2026-06-03', payment: 'prepaid' }, + { id: 'DM-10246', tenant: 'TechNova Retail', location: 'Hyderabad', pickup: 'Hitech City', drop: 'Gachibowli', customer: 'Vikram Reddy', qty: 1, cod: 4999, kms: 5.6, charges: 120, notes: 'ID required', status: 'created', date: '2026-06-05', payment: 'cod' }, + { id: 'DM-10247', tenant: 'GreenLeaf Organics', location: 'Pune', pickup: 'Koregaon Park', drop: 'Viman Nagar', customer: 'Pooja Desai', qty: 2, cod: 0, kms: 7.2, charges: 102, notes: '', status: 'delivered', date: '2026-06-04', payment: 'prepaid' }, + { id: 'DM-10248', tenant: 'UrbanCart', location: 'Chennai', pickup: 'T. Nagar', drop: 'Adyar', customer: 'Suresh Kumar', qty: 1, cod: 799, kms: 8.3, charges: 108, notes: 'Weekend slot', status: 'pending', date: '2026-06-05', payment: 'cod' } +]; + +export const orderTimeline = [ + { label: 'Order Placed', time: '09:12 AM', done: true }, + { label: 'Picked Up', time: '09:48 AM', done: true }, + { label: 'In Transit', time: '10:05 AM', done: true }, + { label: 'Delivered', time: '—', done: false } +]; + +export const deliveries = orders.map((o, i) => ({ + ...o, + rider: ['Mohan Das', 'Imran Sheikh', 'Ravi Teja', 'Sandeep Roy', 'Faisal Khan'][i % 5], + amount: o.charges, + products: [ + { name: 'Item A', description: 'Standard pack', qty: o.qty, cost: 40, price: 60, tax: 5, amount: 60 * o.qty } + ] +})); + +export const riders = [ + { id: 'RDR-001', userId: 'U1043', name: 'Mohan Das', phone: '+91 98450 11223', address: 'Koramangala, Bengaluru', vehicle: 'Honda Activa', vehicleNo: 'KA-05-HJ-4521', shift: 'Morning', start: '08:00', end: '16:00', fare: 320, fuel: 110, status: 'online', deliveries: 14, rating: 4.8 }, + { id: 'RDR-002', userId: 'U1044', name: 'Imran Sheikh', phone: '+91 98860 44781', address: 'Andheri, Mumbai', vehicle: 'TVS Jupiter', vehicleNo: 'MH-02-CD-9087', shift: 'Evening', start: '14:00', end: '22:00', fare: 280, fuel: 95, status: 'on-delivery', deliveries: 11, rating: 4.6 }, + { id: 'RDR-003', userId: 'U1045', name: 'Ravi Teja', phone: '+91 99000 22119', address: 'Gachibowli, Hyderabad', vehicle: 'Hero Splendor', vehicleNo: 'TS-09-AB-3344', shift: 'Morning', start: '08:00', end: '16:00', fare: 300, fuel: 120, status: 'online', deliveries: 9, rating: 4.9 }, + { id: 'RDR-004', userId: 'U1046', name: 'Sandeep Roy', phone: '+91 90080 55672', address: 'Saket, Delhi', vehicle: 'Bajaj Pulsar', vehicleNo: 'DL-3C-XY-1290', shift: 'Night', start: '22:00', end: '06:00', fare: 350, fuel: 130, status: 'offline', deliveries: 0, rating: 4.4 }, + { id: 'RDR-005', userId: 'U1047', name: 'Faisal Khan', phone: '+91 97410 88123', address: 'Adyar, Chennai', vehicle: 'Honda Dio', vehicleNo: 'TN-07-PQ-6655', shift: 'Evening', start: '14:00', end: '22:00', fare: 290, fuel: 100, status: 'on-delivery', deliveries: 7, rating: 4.7 } +]; + +export const riderLogs = [ + { location: '12.9352, 77.6245', battery: '82%', charging: 'No', speed: '24 km/h', accuracy: '5 m', time: '10:42 AM', order: 'DM-10242', status: 'active' }, + { location: '12.9298, 77.6848', battery: '80%', charging: 'No', speed: '0 km/h', accuracy: '8 m', time: '10:38 AM', order: 'DM-10242', status: 'arrived' }, + { location: '12.9101, 77.6446', battery: '84%', charging: 'No', speed: '31 km/h', accuracy: '4 m', time: '10:25 AM', order: 'DM-10242', status: 'picked' } +]; + +export const customers = [ + { id: 1, name: 'Riya Sharma', phone: '+91 98111 22334', email: 'riya.sharma@mail.com', address: '24, 1st Cross, HSR Layout', location: 'Bengaluru', city: 'Bengaluru', state: 'Karnataka', postcode: '560102', totalOrders: 18, joined: '2025-03-12' }, + { id: 2, name: 'Karthik Rao', phone: '+91 98222 33445', email: 'karthik.rao@mail.com', address: '8, Palm Meadows, Whitefield', location: 'Bengaluru', city: 'Bengaluru', state: 'Karnataka', postcode: '560066', totalOrders: 7, joined: '2025-07-21' }, + { id: 3, name: 'Neha Kulkarni', phone: '+91 98333 44556', email: 'neha.k@mail.com', address: '102, Sea View, Bandra East', location: 'Mumbai', city: 'Mumbai', state: 'Maharashtra', postcode: '400051', totalOrders: 31, joined: '2024-11-02' }, + { id: 4, name: 'Arjun Mehta', phone: '+91 98444 55667', email: 'arjun.m@mail.com', address: 'C-14, Saket', location: 'Delhi NCR', city: 'New Delhi', state: 'Delhi', postcode: '110017', totalOrders: 5, joined: '2025-09-18' }, + { id: 5, name: 'Pooja Desai', phone: '+91 98555 66778', email: 'pooja.d@mail.com', address: '4, Lane 5, Koregaon Park', location: 'Pune', city: 'Pune', state: 'Maharashtra', postcode: '411001', totalOrders: 12, joined: '2025-01-30' } +]; + +export const tenants = [ + { id: 1, name: 'Freshly Foods', contact: 'Anil Gupta', phone: '+91 80451 22000', email: 'ops@freshlyfoods.in', address: 'Koramangala 4th Block', city: 'Bengaluru', postcode: '560034', lat: '12.9352', lng: '77.6245', status: 'active', volume: 1240 }, + { id: 2, name: 'UrbanCart', contact: 'Meera Nair', phone: '+91 22480 11233', email: 'logistics@urbancart.com', address: 'Indiranagar 100ft Rd', city: 'Bengaluru', postcode: '560038', lat: '12.9719', lng: '77.6412', status: 'active', volume: 980 }, + { id: 3, name: 'MediQuick Pharma', contact: 'Rohit Sen', phone: '+91 22556 99001', email: 'dispatch@mediquick.in', address: 'Andheri West', city: 'Mumbai', postcode: '400058', lat: '19.1351', lng: '72.8290', status: 'pending', volume: 410 }, + { id: 4, name: 'BloomBox Florists', contact: 'Tina Roy', phone: '+91 11409 33221', email: 'hello@bloombox.in', address: 'Connaught Place', city: 'New Delhi', postcode: '110001', lat: '28.6315', lng: '77.2167', status: 'inactive', volume: 120 }, + { id: 5, name: 'TechNova Retail', contact: 'Sameer Joshi', phone: '+91 40229 77654', email: 'ops@technova.in', address: 'Hitech City', city: 'Hyderabad', postcode: '500081', lat: '17.4435', lng: '78.3772', status: 'active', volume: 760 } +]; + +export const tenantPricing = [ + { date: '2025-04-01', slab: '0-5 km', basePrice: 40, minKms: 2, pricePerKm: 9, otherCharges: 0 }, + { date: '2025-04-01', slab: '5-10 km', basePrice: 60, minKms: 5, pricePerKm: 8, otherCharges: 10 }, + { date: '2025-04-01', slab: '10+ km', basePrice: 90, minKms: 10, pricePerKm: 7, otherCharges: 15 } +]; + +export const pricing = [ + { id: 1, location: 'Bengaluru', pricingId: 'PR-001', name: 'Standard', slab: '0-5 km', basePrice: 40, minKm: 2, pricePerKm: 9, maxKm: 5, minOrders: 1 }, + { id: 2, location: 'Bengaluru', pricingId: 'PR-002', name: 'Express', slab: '5-10 km', basePrice: 65, minKm: 5, pricePerKm: 8, maxKm: 10, minOrders: 1 }, + { id: 3, location: 'Mumbai', pricingId: 'PR-003', name: 'Standard', slab: '0-5 km', basePrice: 45, minKm: 2, pricePerKm: 10, maxKm: 5, minOrders: 1 }, + { id: 4, location: 'Delhi NCR', pricingId: 'PR-004', name: 'Bulk', slab: '10+ km', basePrice: 95, minKm: 10, pricePerKm: 7, maxKm: 25, minOrders: 5 } +]; + +export const invoices = [ + { id: 1, invoiceId: 'INV-2026-0041', client: 'Freshly Foods', invoiceDate: '2026-06-01', dueDate: '2026-06-15', period: 'May 2026', count: 1240, amount: 106800, status: 'paid' }, + { id: 2, invoiceId: 'INV-2026-0042', client: 'UrbanCart', invoiceDate: '2026-06-01', dueDate: '2026-06-15', period: 'May 2026', count: 980, amount: 84300, status: 'open' }, + { id: 3, invoiceId: 'INV-2026-0043', client: 'MediQuick Pharma', invoiceDate: '2026-05-01', dueDate: '2026-05-15', period: 'Apr 2026', count: 410, amount: 35200, status: 'overdue' }, + { id: 4, invoiceId: 'INV-2026-0044', client: 'TechNova Retail', invoiceDate: '2026-06-01', dueDate: '2026-06-20', period: 'May 2026', count: 760, amount: 65400, status: 'open' }, + { id: 5, invoiceId: 'INV-2026-0045', client: 'GreenLeaf Organics', invoiceDate: '2026-05-01', dueDate: '2026-05-15', period: 'Apr 2026', count: 540, amount: 46900, status: 'paid' } +]; + +export const invoiceLineItems = [ + { particulars: 'Standard delivery (0-5 km)', unit: 'order', qty: 720, rate: 74, other: 0, amount: 53280 }, + { particulars: 'Express delivery (5-10 km)', unit: 'order', qty: 380, rate: 108, other: 0, amount: 41040 }, + { particulars: 'Bulk delivery (10+ km)', unit: 'order', qty: 140, rate: 142, other: 8, amount: 21000 }, + { particulars: 'COD handling fee', unit: 'order', qty: 260, rate: 6, other: 0, amount: 1560 } +]; + +export const requests = [ + { id: 1, requestor: 'Freshly Foods', bank: 'HDFC Bank', ifsc: 'HDFC0001234', refNo: 'RQ-88121', amount: 24500, reason: 'Weekly settlement payout', contact: 'Anil Gupta', address: 'Koramangala 4th Block', city: 'Bengaluru', zip: '560034', accountNo: '5012 3344 7788', pricing: [{ category: 'Standard', skill: 'Bike', cost: 9 }, { category: 'Express', skill: 'Bike', cost: 12 }] }, + { id: 2, requestor: 'UrbanCart', bank: 'ICICI Bank', ifsc: 'ICIC0004567', refNo: 'RQ-88122', amount: 18900, reason: 'Fuel reimbursement', contact: 'Meera Nair', address: 'Indiranagar', city: 'Bengaluru', zip: '560038', accountNo: '6022 1199 4455', pricing: [{ category: 'Standard', skill: 'Bike', cost: 8 }] }, + { id: 3, requestor: 'MediQuick Pharma', bank: 'Axis Bank', ifsc: 'UTIB0007788', refNo: 'RQ-88123', amount: 9700, reason: 'Adjustment - April', contact: 'Rohit Sen', address: 'Andheri West', city: 'Mumbai', zip: '400058', accountNo: '7033 5566 1122', pricing: [{ category: 'Standard', skill: 'Bike', cost: 10 }] } +]; + +// reports +export const ordersSummary = tenants.map((t, i) => ({ + id: t.id, + tenant: t.name, + location: t.city, + orders: { pending: [2, 1, 0, 3, 1][i], cancelled: [1, 0, 2, 0, 1][i], completed: [38, 27, 11, 4, 22][i] }, + deliveries: { pending: [1, 1, 0, 2, 0][i], cancelled: [0, 0, 1, 0, 1][i], completed: [38, 26, 10, 4, 22][i] }, + collection: [12400, 8800, 3200, 900, 6700][i], + kms: [184, 142, 66, 28, 121][i], + actualKms: [190, 148, 70, 30, 124][i], + amount: [4280, 3120, 1420, 520, 2410][i], + riders: [ + { rider: 'Mohan Das', orders: 14, deliveries: 14, pending: 1, cancelled: 0, completed: 13, collection: 5200, kms: 64, actualKms: 66, charges: 1480 }, + { rider: 'Imran Sheikh', orders: 11, deliveries: 10, pending: 0, cancelled: 1, completed: 9, collection: 4100, kms: 58, actualKms: 60, charges: 1280 } + ] +})); + +export const ordersDetailReport = orders.map((o, i) => ({ + id: o.id, + client: o.tenant, + pickup: o.pickup, + drop: o.drop, + status: o.status, + assigned: '09:12', + accepted: i % 5 === 3 ? '—' : '09:14', + arrived: ['09:40', '—', '09:35', '—', '—', '—', '09:50', '—'][i], + picked: ['09:48', '—', '09:42', '—', '—', '—', '09:58', '—'][i], + active: ['10:05', '—', '10:00', '—', '—', '—', '10:12', '—'][i], + delivered: o.status === 'delivered' ? '10:30' : '—', + cancelled: o.status === 'cancelled' ? '09:20' : '—', + notes: o.notes, + kms: o.kms, + charges: o.charges +})); + +export const ridersSummary = riders.map((r, i) => ({ + id: r.id, + rider: r.name, + orders: r.deliveries + [2, 1, 1, 0, 1][i], + pending: [1, 0, 1, 0, 0][i], + cancelled: [0, 1, 0, 0, 1][i], + delivered: r.deliveries, + kms: [64, 58, 47, 0, 39][i], + actualKms: [66, 60, 49, 0, 41][i], + amount: [1480, 1280, 1120, 0, 980][i], + clients: [ + { client: 'Freshly Foods', all: 8, pending: 1, completed: 7, cancelled: 0, kms: 32, actualKms: 33, amount: 740 }, + { client: 'UrbanCart', all: 6, pending: 0, completed: 6, cancelled: 0, kms: 32, actualKms: 33, amount: 740 } + ] +})); + +export const ridersLive = riders.map((r, i) => ({ + ...r, + userid: r.userId, + lastLog: ['10:42 AM', '10:39 AM', '10:40 AM', 'Yesterday', '10:35 AM'][i], + active: r.status !== 'offline', + lat: [12.9352, 19.1351, 17.4435, 28.6315, 13.0067][i], + lng: [77.6245, 72.829, 78.3772, 77.2167, 80.2206][i] +})); + +// dashboard chart series (monthly orders) +export const ordersTrend = [ + { m: 'Jan', orders: 820, delivered: 760 }, + { m: 'Feb', orders: 932, delivered: 880 }, + { m: 'Mar', orders: 1010, delivered: 965 }, + { m: 'Apr', orders: 1180, delivered: 1120 }, + { m: 'May', orders: 1290, delivered: 1240 }, + { m: 'Jun', orders: 1402, delivered: 1330 } +]; + +export const statusBreakdown = [ + { label: 'Delivered', value: 1240, color: '#00A854' }, + { label: 'In Transit', value: 96, color: '#00A2AE' }, + { label: 'Pending', value: 48, color: '#FFBF00' }, + { label: 'Cancelled', value: 18, color: '#F04134' } +]; diff --git a/src/layout/MainLayout/Header.jsx b/src/layout/MainLayout/Header.jsx new file mode 100644 index 0000000..46a9e16 --- /dev/null +++ b/src/layout/MainLayout/Header.jsx @@ -0,0 +1,261 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + AppBar, + Toolbar, + IconButton, + Box, + InputBase, + Badge, + Avatar, + Typography, + Menu, + MenuItem, + Divider, + ListItemIcon, + ListItemText, + Tooltip, + Button, + Stack, + alpha +} from '@mui/material'; +import MenuIcon from '@mui/icons-material/Menu'; +import SearchIcon from '@mui/icons-material/Search'; +import NotificationsNoneIcon from '@mui/icons-material/NotificationsNone'; +import ChatBubbleOutlineIcon from '@mui/icons-material/ChatBubbleOutline'; +import PersonOutlineIcon from '@mui/icons-material/PersonOutline'; +import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined'; +import LogoutIcon from '@mui/icons-material/Logout'; +import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined'; +import TwoWheelerOutlinedIcon from '@mui/icons-material/TwoWheelerOutlined'; +import PaymentsOutlinedIcon from '@mui/icons-material/PaymentsOutlined'; +import AssignmentOutlinedIcon from '@mui/icons-material/AssignmentOutlined'; +import DoneAllIcon from '@mui/icons-material/DoneAll'; + +import Logo from '@/components/Logo'; + +const RED = '#C01227'; + +const INITIAL_NOTIFICATIONS = [ + { id: 1, icon: Inventory2OutlinedIcon, title: 'New order #ORD-10482 placed', time: '2 min ago', to: '/orders', read: false }, + { id: 2, icon: TwoWheelerOutlinedIcon, title: 'Rider Imran went online', time: '18 min ago', to: '/riders', read: false }, + { id: 3, icon: PaymentsOutlinedIcon, title: 'Invoice INV-2041 marked paid', time: '1 hr ago', to: '/invoice', read: false }, + { id: 4, icon: AssignmentOutlinedIcon, title: '3 new onboarding requests', time: '3 hrs ago', to: '/requests', read: true } +]; + +const MESSAGES = [ + { id: 1, name: 'Priya Nair', text: 'Can we reroute the MG Road batch?', time: '5 min ago', initials: 'PN' }, + { id: 2, name: 'Imran Khan', text: 'Reached the warehouse, loading now.', time: '22 min ago', initials: 'IK' }, + { id: 3, name: 'Acme Logistics', text: 'Please confirm the revised pricing.', time: '2 hrs ago', initials: 'AL' } +]; + +export default function Header({ onToggle }) { + const navigate = useNavigate(); + const [account, setAccount] = useState(null); + const [notifAnchor, setNotifAnchor] = useState(null); + const [msgAnchor, setMsgAnchor] = useState(null); + const [notifications, setNotifications] = useState(INITIAL_NOTIFICATIONS); + const [search, setSearch] = useState(''); + + const unread = notifications.filter((n) => !n.read).length; + + const openNotif = (e) => setNotifAnchor(e.currentTarget); + const closeNotif = () => setNotifAnchor(null); + const markAllRead = () => setNotifications((prev) => prev.map((n) => ({ ...n, read: true }))); + const onNotifClick = (n) => { + setNotifications((prev) => prev.map((x) => (x.id === n.id ? { ...x, read: true } : x))); + closeNotif(); + navigate(n.to); + }; + + const submitSearch = (e) => { + e.preventDefault(); + const q = search.trim(); + if (q) navigate(`/orders?q=${encodeURIComponent(q)}`); + }; + + return ( + t.zIndex.drawer + 1, boxShadow: '0 1px 0 rgba(0,0,0,0.06)' }} + > + + + + + + {/* Brand wordmark — left side */} + navigate('/dashboard')} + sx={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }} + > + + + + + + {/* Search — moved to the right */} + + + setSearch(e.target.value)} + placeholder="Search orders, riders, customers…" + sx={{ color: '#fff', fontSize: '0.875rem', flex: 1, '&::placeholder': { color: '#fff' } }} + inputProps={{ style: { color: '#fff' }, 'aria-label': 'search' }} + /> + + + + setMsgAnchor(e.currentTarget)}> + + + + + + + + + + + + + + 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: alpha('#fff', 0.14) } }} + > + AD + + + Aman Deshmukh + + + Operations Admin + + + + + {/* Notifications dropdown */} + + + + Notifications + + + + + {notifications.length === 0 && ( + + + + )} + {notifications.map((n) => { + const Icon = n.icon; + return ( + onNotifClick(n)} sx={{ py: 1.25, whiteSpace: 'normal', alignItems: 'flex-start' }}> + + + + + + + {!n.read && } + + ); + })} + + { closeNotif(); navigate('/requests'); }} sx={{ justifyContent: 'center', color: 'primary.main', fontWeight: 600 }}> + View all activity + + + + {/* Messages dropdown */} + setMsgAnchor(null)} + transformOrigin={{ horizontal: 'right', vertical: 'top' }} + anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} + PaperProps={{ sx: { mt: 1, width: 340, maxWidth: '90vw' } }} + > + + Messages + + + {MESSAGES.map((m) => ( + setMsgAnchor(null)} sx={{ py: 1.25, whiteSpace: 'normal', alignItems: 'flex-start' }}> + + + {m.initials} + + + + + {m.time} + + + ))} + + + {/* Account dropdown */} + setAccount(null)} + transformOrigin={{ horizontal: 'right', vertical: 'top' }} + anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} + PaperProps={{ sx: { mt: 1, minWidth: 200 } }} + > + { setAccount(null); navigate('/profile'); }}> + + View Profile + + { setAccount(null); navigate('/settings'); }}> + + Settings + + + { setAccount(null); navigate('/login'); }} sx={{ color: 'error.main' }}> + + Logout + + + + + ); +} diff --git a/src/layout/MainLayout/Sidebar.jsx b/src/layout/MainLayout/Sidebar.jsx new file mode 100644 index 0000000..1be9cab --- /dev/null +++ b/src/layout/MainLayout/Sidebar.jsx @@ -0,0 +1,200 @@ +import { useState } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { + Drawer, + Box, + List, + ListItemButton, + ListItemIcon, + ListItemText, + Typography, + Collapse, + Tooltip, + Toolbar +} 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 navItems from '@/menu/navItems'; +import Logo from '@/components/Logo'; + +export const DRAWER_WIDTH = 264; +export const MINI_WIDTH = 78; + +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 }) { + 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 ? '#fff' : 'rgba(255,255,255,0.78)', + bgcolor: childActive && !opened ? 'rgba(255,255,255,0.12)' : 'transparent', + '&:hover': { bgcolor: 'rgba(255,255,255,0.12)', color: '#fff' } + }} + > + + + + {expanded && ( + <> + + {opened ? : } + + )} + + ); + return ( + + {expanded ? head : {head}} + {expanded && ( + + + {item.children.map((c) => ( + go(c.url)} /> + ))} + + + )} + + ); + } + return ( + go(item.url)} /> + ); + })} + + + ))} + + {expanded && ( + + + Doormile Console v1.0 + + + )} + + ); + + if (isMobile) { + return ( + + {content} + + ); + } + + return ( + t.transitions.create('width', { duration: t.transitions.duration.standard }) + } + }} + open={open} + > + {content} + + ); +} diff --git a/src/layout/MainLayout/index.jsx b/src/layout/MainLayout/index.jsx new file mode 100644 index 0000000..5caab1b --- /dev/null +++ b/src/layout/MainLayout/index.jsx @@ -0,0 +1,45 @@ +import { useState } from 'react'; +import { Outlet } from 'react-router-dom'; +import { Box, Toolbar, useMediaQuery } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; + +import Header from './Header'; +import Sidebar, { DRAWER_WIDTH, MINI_WIDTH } from './Sidebar'; + +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)} + /> + + + + + + + + ); +} diff --git a/src/layout/MinimalLayout.jsx b/src/layout/MinimalLayout.jsx new file mode 100644 index 0000000..d1357cf --- /dev/null +++ b/src/layout/MinimalLayout.jsx @@ -0,0 +1,11 @@ +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 new file mode 100644 index 0000000..3997aa9 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,22 @@ +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 theme from '@/theme'; +import App from '@/App'; + +ReactDOM.createRoot(document.getElementById('root')).render( + + + + + + + + + + +); diff --git a/src/menu/navItems.jsx b/src/menu/navItems.jsx new file mode 100644 index 0000000..571e8c5 --- /dev/null +++ b/src/menu/navItems.jsx @@ -0,0 +1,55 @@ +import DashboardOutlinedIcon from '@mui/icons-material/DashboardOutlined'; +import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined'; +import MopedOutlinedIcon from '@mui/icons-material/MopedOutlined'; +import ApartmentOutlinedIcon from '@mui/icons-material/ApartmentOutlined'; +import PaymentsOutlinedIcon from '@mui/icons-material/PaymentsOutlined'; +import GroupsOutlinedIcon from '@mui/icons-material/GroupsOutlined'; +import TwoWheelerOutlinedIcon from '@mui/icons-material/TwoWheelerOutlined'; +import BarChartOutlinedIcon from '@mui/icons-material/BarChartOutlined'; +import ReceiptLongOutlinedIcon from '@mui/icons-material/ReceiptLongOutlined'; +import AssignmentOutlinedIcon from '@mui/icons-material/AssignmentOutlined'; +import SummarizeOutlinedIcon from '@mui/icons-material/SummarizeOutlined'; +import FactCheckOutlinedIcon from '@mui/icons-material/FactCheckOutlined'; +import MapOutlinedIcon from '@mui/icons-material/MapOutlined'; + +// ==============================|| DOORMILE - SIDEBAR NAV CONFIG ||============================== // + +const navItems = [ + { + group: 'Operations', + items: [ + { id: 'dashboard', title: 'Dashboard', url: '/dashboard', icon: DashboardOutlinedIcon }, + { id: 'orders', title: 'Orders', url: '/orders', icon: Inventory2OutlinedIcon }, + { id: 'deliveries', title: 'Deliveries', url: '/deliveries', icon: MopedOutlinedIcon } + ] + }, + { + group: 'Network', + items: [ + { id: 'tenants', title: 'Tenants', url: '/tenants', icon: ApartmentOutlinedIcon }, + { id: 'pricing', title: 'Pricing', url: '/pricing', icon: PaymentsOutlinedIcon }, + { id: 'customers', title: 'Customers', url: '/customers', icon: GroupsOutlinedIcon }, + { id: 'riders', title: 'Riders', url: '/riders', icon: TwoWheelerOutlinedIcon } + ] + }, + { + group: 'Finance & Insights', + items: [ + { + id: 'reports', + title: 'Reports', + icon: BarChartOutlinedIcon, + children: [ + { id: 'orders-summary', title: 'Order Summary', url: '/reports/orders-summary', icon: SummarizeOutlinedIcon }, + { id: 'orders-details', title: 'Order Details', url: '/reports/orders-details', icon: FactCheckOutlinedIcon }, + { id: 'riders-summary', title: 'Riders Summary', url: '/reports/riders-summary', icon: TwoWheelerOutlinedIcon }, + { id: 'riders-logs', title: 'Riders Logs', url: '/reports/riders-logs', icon: MapOutlinedIcon } + ] + }, + { id: 'invoice', title: 'Invoice', url: '/invoice', icon: ReceiptLongOutlinedIcon }, + { id: 'requests', title: 'Requests', url: '/requests', icon: AssignmentOutlinedIcon } + ] + } +]; + +export default navItems; diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx new file mode 100644 index 0000000..838ea5b --- /dev/null +++ b/src/pages/Dashboard.jsx @@ -0,0 +1,124 @@ +import { Grid, Card, CardContent, Stack, Typography, Box, Button, Divider, Table, TableBody, TableCell, TableHead, TableRow, Avatar, MenuItem, TextField } from '@mui/material'; +import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined'; +import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined'; +import TwoWheelerOutlinedIcon from '@mui/icons-material/TwoWheelerOutlined'; +import CurrencyRupeeIcon from '@mui/icons-material/CurrencyRupee'; +import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined'; + +import PageHeader from '@/components/PageHeader'; +import StatCard from '@/components/StatCard'; +import MainCard from '@/components/MainCard'; +import StatusChip from '@/components/StatusChip'; +import AreaChart from '@/components/charts/AreaChart'; +import DonutChart from '@/components/charts/DonutChart'; +import UserAvatar from '@/components/UserAvatar'; +import { ordersTrend, statusBreakdown, orders, riders } from '@/data/mock'; +import { inr } from '@/utils/format'; + +export default function Dashboard() { + return ( + <> + + + All Locations + Bengaluru + Mumbai + + + + } + /> + + + + + + + + + } + > + d.m)} + series={[ + { name: 'Orders', color: '#C01227', data: ordersTrend.map((d) => d.orders) }, + { name: 'Delivered', color: '#00A854', data: ordersTrend.map((d) => d.delivered) } + ]} + /> + + + + + + + + + + + + + + + + Order ID + Customer + Route + Status + Amount + + + + {orders.slice(0, 6).map((o) => ( + + {o.id} + {o.customer} + + {o.pickup} → {o.drop} + + + {inr(o.charges)} + + ))} + +
+
+
+ + + } spacing={0}> + {riders.slice(0, 5).map((r, i) => ( + + {i + 1} + + + {r.name} + {r.vehicle} · ⭐ {r.rating} + + + {r.deliveries} + deliveries + + + ))} + + + +
+ + ); +} + +function Legend({ color, label }) { + return ( + + + {label} + + ); +} diff --git a/src/pages/Deliveries.jsx b/src/pages/Deliveries.jsx new file mode 100644 index 0000000..ae51448 --- /dev/null +++ b/src/pages/Deliveries.jsx @@ -0,0 +1,279 @@ +import { useState, useMemo, Fragment } from 'react'; +import { + Grid, Card, Stack, Button, TextField, MenuItem, InputAdornment, Box, Tabs, Tab, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton, + Tooltip, TablePagination, Typography, Collapse, Menu +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import CalendarTodayOutlinedIcon from '@mui/icons-material/CalendarTodayOutlined'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import NotificationsActiveOutlinedIcon from '@mui/icons-material/NotificationsActiveOutlined'; +import SwapHorizOutlinedIcon from '@mui/icons-material/SwapHorizOutlined'; +import EditLocationAltOutlinedIcon from '@mui/icons-material/EditLocationAltOutlined'; +import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined'; +import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined'; +import HourglassEmptyOutlinedIcon from '@mui/icons-material/HourglassEmptyOutlined'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; + +import PageHeader from '@/components/PageHeader'; +import StatCard from '@/components/StatCard'; +import EmptyState from '@/components/EmptyState'; +import UserAvatar from '@/components/UserAvatar'; +import TabLabelCount from '@/components/TabLabelCount'; +import { deliveries, locations, tenantsList, riders } from '@/data/mock'; +import { inr } from '@/utils/format'; + +const TABS = [ + { key: 'assigned', label: 'Assigned' }, + { key: 'accepted', label: 'Accepted' }, + { key: 'arrived', label: 'Arrived' }, + { key: 'picked', label: 'Picked' }, + { key: 'active', label: 'Active' }, + { key: 'skipped', label: 'Skipped' }, + { key: 'delivered', label: 'Delivered' }, + { key: 'cancelled', label: 'Cancelled' } +]; + +function ProductTable({ products = [] }) { + return ( + + + Products + + + + + S.No + Product Name + Description + Quantity + Cost + Price + Tax + Amount + + + + {products.map((p, i) => ( + + {i + 1} + {p.name} + + {p.description} + + {p.qty} + {inr(p.cost)} + {inr(p.price)} + {p.tax}% + {inr(p.amount)} + + ))} + +
+
+ ); +} + +function DeliveryRow({ row, index }) { + const [open, setOpen] = useState(false); + const [anchor, setAnchor] = useState(null); + + return ( + + *': { borderBottom: open ? 'unset' : undefined } }}> + + setOpen((o) => !o)}> + {open ? : } + + + {index + 1} + {row.tenant} + {row.location} + {row.pickup} + {row.drop} + + + + {row.rider} + + + + {row.notes || '—'} + + {row.qty} + {row.cod ? inr(row.cod) : '—'} + {row.kms} + {inr(row.amount)} + + + setAnchor(e.currentTarget)}> + + + + setAnchor(null)}> + setAnchor(null)}> + Notify Rider + + setAnchor(null)}> + Change Rider + + setAnchor(null)}> + Update Delivery Status + + setAnchor(null)} sx={{ color: 'error.main' }}> + Cancel Delivery + + + + + + + + + + + + + ); +} + +export default function Deliveries() { + const [tab, setTab] = useState(0); + const [search, setSearch] = useState(''); + const [tenant, setTenant] = useState('all'); + const [location, setLocation] = useState('all'); + const [rider, setRider] = useState('all'); + const [headerLocation, setHeaderLocation] = useState('all'); + const [page, setPage] = useState(0); + const [rpp, setRpp] = useState(5); + + const tabKey = TABS[tab].key; + + const counts = useMemo(() => { + const c = {}; + TABS.forEach((t) => { c[t.key] = deliveries.filter((d) => d.status === t.key).length; }); + return c; + }, []); + + const stats = useMemo(() => ({ + created: deliveries.length, + pending: deliveries.filter((d) => d.status === 'pending').length, + delivered: deliveries.filter((d) => d.status === 'delivered').length, + cancelled: deliveries.filter((d) => d.status === 'cancelled').length + }), []); + + const filtered = useMemo( + () => + deliveries.filter((d) => { + const matchTab = d.status === tabKey; + const matchTenant = tenant === 'all' || d.tenant === tenant; + const matchLocation = location === 'all' || d.location === location; + const matchRider = rider === 'all' || d.rider === rider; + const matchSearch = + !search || + [d.id, d.tenant, d.pickup, d.drop, d.rider, d.location].join(' ').toLowerCase().includes(search.toLowerCase()); + return matchTab && matchTenant && matchLocation && matchRider && matchSearch; + }), + [tabKey, tenant, location, rider, search] + ); + + const paged = filtered.slice(page * rpp, page * rpp + rpp); + + return ( + <> + setHeaderLocation(e.target.value)} + sx={{ minWidth: 180 }} label="Location" + > + All Locations + {locations.map((l) => {l})} + + } + /> + + + + + + + + + + + + { setTenant(e.target.value); setPage(0); }} sx={{ minWidth: 160 }} label="Tenant"> + All Tenants + {tenantsList.map((t) => {t})} + + { setLocation(e.target.value); setPage(0); }} sx={{ minWidth: 150 }} label="Location"> + All Locations + {locations.map((l) => {l})} + + { setRider(e.target.value); setPage(0); }} sx={{ minWidth: 150 }} label="Rider"> + All Riders + {riders.map((r) => {r.name})} + + + { setSearch(e.target.value); setPage(0); }} + sx={{ minWidth: 220 }} + InputProps={{ startAdornment: }} + /> + + + + { setTab(v); setPage(0); }} variant="scrollable" scrollButtons="auto"> + {TABS.map((t, i) => ( + } /> + ))} + + + + + + + + + S.No + Tenant + Order Location + Pickup + Drop + Rider + Notes + Qty + COD + Kms + Amount + Action + + + + {paged.length === 0 ? ( + + + + + + ) : ( + paged.map((row, i) => ) + )} + +
+
+ setPage(p)} + rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[5, 10, 25]} + /> +
+ + ); +} diff --git a/src/pages/Pricing.jsx b/src/pages/Pricing.jsx new file mode 100644 index 0000000..231bffe --- /dev/null +++ b/src/pages/Pricing.jsx @@ -0,0 +1,93 @@ +import { useState, useMemo } from 'react'; +import { + Autocomplete, TextField, Box, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Chip, Typography +} from '@mui/material'; + +import PageHeader from '@/components/PageHeader'; +import MainCard from '@/components/MainCard'; +import EmptyState from '@/components/EmptyState'; +import { pricing, locations } from '@/data/mock'; +import { inr } from '@/utils/format'; + +const SLAB_COLORS = { '0-5 km': 'info', '5-10 km': 'warning', '10+ km': 'success' }; +const NAME_COLORS = { Standard: 'primary', Express: 'warning', Bulk: 'success' }; + +export default function Pricing() { + const [location, setLocation] = useState(null); + + const filtered = useMemo( + () => (location ? pricing.filter((p) => p.location === location) : pricing), + [location] + ); + + return ( + <> + setLocation(v)} + sx={{ minWidth: 240 }} + renderInput={(params) => } + /> + } + /> + + + {filtered.length === 0 ? ( + + ) : ( + + + + + S.No + Location + Pricing Id + Name + Slab + Base Price + MinKm + Price/Km + MaxKm + Min Orders + + + + {filtered.map((p, i) => ( + + {i + 1} + + + + + {p.pricingId} + + + + + + + + {inr(p.basePrice)} + {p.minKm} + {inr(p.pricePerKm)} + {p.maxKm} + + + + + ))} + +
+
+ )} +
+ + ); +} diff --git a/src/pages/Profile.jsx b/src/pages/Profile.jsx new file mode 100644 index 0000000..a49e2b0 --- /dev/null +++ b/src/pages/Profile.jsx @@ -0,0 +1,69 @@ +import { Grid, Card, CardContent, Stack, Typography, TextField } from '@mui/material'; + +import PageHeader from '@/components/PageHeader'; +import MainCard from '@/components/MainCard'; +import UserAvatar from '@/components/UserAvatar'; + +const PROFILE = { + userId: 'U1001', + userName: 'Aarav Menon', + appLocation: 'Bengaluru', + authName: 'admin@doormile.in', + contactNo: '+91 98450 11223', + email: 'aarav.menon@doormile.in', + address: 'No. 7, Brigade Road, MG Road Area', + location: 'Bengaluru', + city: 'Bengaluru', + state: 'Karnataka', + postcode: '560001', + role: 'Operations Administrator' +}; + +const ro = (value) => ({ + fullWidth: true, + variant: 'standard', + value, + InputProps: { readOnly: true } +}); + +export default function Profile() { + return ( + <> + + + + + + + + +
+ {PROFILE.userName} + {PROFILE.role} +
+
+
+
+
+ + + + + + + + + + + + + + + + + + +
+ + ); +} diff --git a/src/pages/Requests.jsx b/src/pages/Requests.jsx new file mode 100644 index 0000000..fe1a5b5 --- /dev/null +++ b/src/pages/Requests.jsx @@ -0,0 +1,162 @@ +import { useState } from 'react'; +import { + Card, Stack, Button, Box, Collapse, Tabs, Tab, Typography, Grid, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton, + TablePagination, Dialog, DialogTitle, DialogContent, DialogActions, TextField +} from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; + +import PageHeader from '@/components/PageHeader'; +import { requests } from '@/data/mock'; +import { inr } from '@/utils/format'; + +function RequestRow({ row, index }) { + const [open, setOpen] = useState(false); + const [tab, setTab] = useState(0); + + return ( + <> + *': { borderBottom: open ? 'unset' : undefined } }}> + {index + 1} + {row.requestor} + {row.bank} + {row.ifsc} + {row.refNo} + {inr(row.amount)} + {row.reason} + + setOpen((o) => !o)}> + {open ? : } + + + + + + + + setTab(v)} sx={{ mb: 2 }}> + + + + + {tab === 0 && ( + + + Contact Name + {row.contact} + + + Address + {row.address} + + + City + {row.city} + + + Zip Code + {row.zip} + + + )} + + {tab === 1 && ( + + + + # + Category + Skill + Cost/Hr + + + + {row.pricing.map((p, i) => ( + + {i + 1} + {p.category} + {p.skill} + {inr(p.cost)} + + ))} + +
+ )} +
+
+
+
+ + ); +} + +export default function Requests() { + const [page, setPage] = useState(0); + const [rpp, setRpp] = useState(10); + const [open, setOpen] = useState(false); + + const paged = requests.slice(page * rpp, page * rpp + rpp); + + return ( + <> + } onClick={() => setOpen(true)}> + Create Request + + } + /> + + + + + + + # + Requestor + Bank + IFSC + Ref No + Amount + Reason + + + + + {paged.map((row, idx) => ( + + ))} + +
+
+ setPage(p)} + rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[5, 10, 25, 100]} + /> +
+ + setOpen(false)} maxWidth="sm" fullWidth> + Create Request + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/pages/Settings.jsx b/src/pages/Settings.jsx new file mode 100644 index 0000000..2bac162 --- /dev/null +++ b/src/pages/Settings.jsx @@ -0,0 +1,206 @@ +import { useState } from 'react'; +import { + Grid, + Tabs, + Tab, + Box, + Stack, + TextField, + MenuItem, + Switch, + FormControlLabel, + Button, + Typography, + Divider, + Snackbar, + Alert +} 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 PageHeader from '@/components/PageHeader'; +import MainCard from '@/components/MainCard'; + +const TIMEZONES = ['Asia/Kolkata (IST)', 'Asia/Dubai (GST)', 'UTC', 'America/New_York (EST)']; +const LANGUAGES = ['English', 'हिन्दी (Hindi)', 'العربية (Arabic)']; + +function TabPanel({ value, index, children }) { + if (value !== index) return null; + return {children}; +} + +export default function Settings() { + const [tab, setTab] = useState(0); + const [toast, setToast] = useState(false); + + // General + const [general, setGeneral] = useState({ + orgName: 'Doormile Logistics Pvt. Ltd.', + supportEmail: 'support@doormile.in', + contact: '+91 98450 11223', + timezone: TIMEZONES[0], + language: LANGUAGES[0] + }); + + // Notifications + const [notify, setNotify] = useState({ + newOrders: true, + riderStatus: true, + invoicePaid: true, + weeklyDigest: false, + emailAlerts: true, + smsAlerts: false + }); + + // Security + const [security, setSecurity] = useState({ + currentPassword: '', + newPassword: '', + confirmPassword: '', + twoFactor: false + }); + + const setG = (k) => (e) => setGeneral((p) => ({ ...p, [k]: e.target.value })); + const setN = (k) => (e) => setNotify((p) => ({ ...p, [k]: e.target.checked })); + const setS = (k) => (e) => setSecurity((p) => ({ ...p, [k]: e.target.value ?? e.target.checked })); + + const save = () => setToast(true); + + return ( + <> + } onClick={save}> + Save Changes + + } + /> + + + + + setTab(v)} + sx={{ + '& .MuiTab-root': { alignItems: 'flex-start', textTransform: 'none', minHeight: 52, fontWeight: 600 } + }} + > + } iconPosition="start" label="General" /> + } iconPosition="start" label="Notifications" /> + } iconPosition="start" label="Security" /> + + + + + + {/* General */} + + + + + + + + + + + + + + + {TIMEZONES.map((t) => ( + {t} + ))} + + + + + {LANGUAGES.map((l) => ( + {l} + ))} + + + + + + + {/* Notifications */} + + + } spacing={0}> + {[ + { k: 'newOrders', t: 'New orders', d: 'Notify when a new order is placed' }, + { k: 'riderStatus', t: 'Rider status', d: 'When a rider goes online or offline' }, + { k: 'invoicePaid', t: 'Invoice paid', d: 'When a client settles an invoice' }, + { k: 'weeklyDigest', t: 'Weekly digest', d: 'A summary of operations every Monday' } + ].map((row) => ( + + + {row.t} + {row.d} + + + + ))} + + + Channels + + } label="Email alerts" /> + } label="SMS alerts" /> + + + + + {/* Security */} + + + + + + + + + + + + + + + + + + + + Authenticator app + + Require a one-time code at sign-in for extra security. + + + setSecurity((p) => ({ ...p, twoFactor: e.target.checked }))} /> + + + + + + + + setToast(false)} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + > + setToast(false)} sx={{ width: '100%' }}> + Settings saved successfully. + + + + ); +} diff --git a/src/pages/auth/Login.jsx b/src/pages/auth/Login.jsx new file mode 100644 index 0000000..6b98b22 --- /dev/null +++ b/src/pages/auth/Login.jsx @@ -0,0 +1,124 @@ +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 BoltIcon from '@mui/icons-material/Bolt'; +import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined'; +import VerifiedOutlinedIcon from '@mui/icons-material/VerifiedOutlined'; + +import Logo from '@/components/Logo'; + +export default function Login() { + const navigate = useNavigate(); + const [show, setShow] = useState(false); + const [auth, setAuth] = useState('admin@doormile.in'); + const [pwd, setPwd] = useState(''); + + return ( + + {/* Brand panel */} + + + + + + + Move every parcel, +
on time, every time. +
+ + The command center for your last-mile operation — orders, riders, pricing and settlements in one corporate console. + + + {[ + { icon: BoltIcon, t: 'AI-assisted route optimisation' }, + { icon: LocalShippingOutlinedIcon, t: 'Real-time rider & delivery tracking' }, + { icon: VerifiedOutlinedIcon, t: 'Automated client invoicing & payouts' } + ].map((f) => ( + + + + + {f.t} + + ))} + +
+ + © 2026 Doormile Logistics Pvt. Ltd. + +
+ + {/* Form panel */} + + + + Welcome back + + Sign in to your Doormile operations account. + + + + + Auth Name + setAuth(e.target.value)} /> + + + Password + setPwd(e.target.value)} + InputProps={{ + endAdornment: ( + + setShow((s) => !s)} edge="end" size="small"> + {show ? : } + + + ) + }} + /> + + + } label={Remember me} /> + Forgot password? + + + + + +
+ ); +} diff --git a/src/pages/customers/CreateCustomer.jsx b/src/pages/customers/CreateCustomer.jsx new file mode 100644 index 0000000..e5eb1cf --- /dev/null +++ b/src/pages/customers/CreateCustomer.jsx @@ -0,0 +1,95 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Grid, Stack, Button, TextField, MenuItem, InputAdornment } from '@mui/material'; + +import PageHeader from '@/components/PageHeader'; +import MainCard from '@/components/MainCard'; +import { locations, tenantsList } from '@/data/mock'; + +const COUNTRY_CODES = ['+91', '+1', '+44', '+61', '+971']; + +export default function CreateCustomer() { + const navigate = useNavigate(); + const [code, setCode] = useState('+91'); + const [topLocation, setTopLocation] = useState(''); + const [client, setClient] = useState(''); + const [form, setForm] = useState({ + name: '', phone: '', email: '', doorNo: '', address: '', + location: '', city: '', state: '', postcode: '', landmark: '' + }); + const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value })); + + return ( + <> + + + + setTopLocation(e.target.value)} sx={{ minWidth: 220 }}> + {locations.map((l) => {l})} + + setClient(e.target.value)} sx={{ minWidth: 220 }}> + {tenantsList.map((t) => {t})} + + + + + + + + + + + setCode(e.target.value)} + InputProps={{ disableUnderline: true }} sx={{ minWidth: 56 }} + > + {COUNTRY_CODES.map((c) => {c})} + + + ) + }} + /> + + + + + + + + + + + + + {locations.map((l) => {l})} + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/pages/customers/Customers.jsx b/src/pages/customers/Customers.jsx new file mode 100644 index 0000000..514a3be --- /dev/null +++ b/src/pages/customers/Customers.jsx @@ -0,0 +1,245 @@ +import { useState, useMemo } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Grid, Stack, Button, TextField, MenuItem, InputAdornment, Box, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton, + Tooltip, TablePagination, Typography, + Dialog, DialogTitle, DialogContent, DialogActions, Divider +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import AddIcon from '@mui/icons-material/Add'; +import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; + +import PageHeader from '@/components/PageHeader'; +import MainCard from '@/components/MainCard'; +import EmptyState from '@/components/EmptyState'; +import UserAvatar from '@/components/UserAvatar'; +import { customers, locations } from '@/data/mock'; + +const EMPTY_FORM = { + name: '', phone: '', address: '', location: '', city: '', state: '', + postcode: '', landmark: '', lat: '', lng: '' +}; + +export default function Customers() { + const navigate = useNavigate(); + const [search, setSearch] = useState(''); + const [location, setLocation] = useState('all'); + const [page, setPage] = useState(0); + const [rpp, setRpp] = useState(5); + const [editOpen, setEditOpen] = useState(false); + const [form, setForm] = useState(EMPTY_FORM); + const [viewOpen, setViewOpen] = useState(false); + const [viewer, setViewer] = useState(null); + + const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value })); + + const filtered = useMemo( + () => + customers.filter((c) => { + const matchLocation = location === 'all' || c.location === location; + const matchSearch = + !search || + [c.name, c.phone, c.email, c.address, c.location, c.city] + .join(' ') + .toLowerCase() + .includes(search.toLowerCase()); + return matchLocation && matchSearch; + }), + [search, location] + ); + + const paged = filtered.slice(page * rpp, page * rpp + rpp); + + const openEdit = (c) => { + setForm({ + name: c.name, phone: c.phone, address: c.address, location: c.location, + city: c.city, state: c.state, postcode: c.postcode, landmark: c.landmark || '', + lat: c.lat || '', lng: c.lng || '' + }); + setEditOpen(true); + }; + + const openView = (c) => { + setViewer(c); + setViewOpen(true); + }; + + return ( + <> + + { setLocation(e.target.value); setPage(0); }} + sx={{ minWidth: 160 }} label="Location" + > + All Locations + {locations.map((l) => {l})} + + { setSearch(e.target.value); setPage(0); }} sx={{ minWidth: 220 }} + InputProps={{ startAdornment: }} + /> + + + } + /> + + + {filtered.length === 0 ? ( + + ) : ( + <> + + + + + # + Name + Contact + Address + Location + Action + + + + {paged.map((c, i) => ( + + {page * rpp + i + 1} + + + + {c.name} + + + + {c.phone} + {c.email} + + + {c.address} + {c.city}, {c.state} {c.postcode} + + {c.location} + + openView(c)}> + openEdit(c)}> + + + + ))} + +
+
+ setPage(p)} + rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} + rowsPerPageOptions={[5, 10, 25]} + /> + + )} +
+ + setViewOpen(false)} maxWidth="sm" fullWidth> + Customer Details + + + {viewer && ( + <> + + + + {viewer.name} + {viewer.location} + + + + {[ + ['Phone', viewer.phone], + ['Email', viewer.email], + ['Address', viewer.address], + ['City', viewer.city], + ['State', viewer.state], + ['Postcode', viewer.postcode], + ['Landmark', viewer.landmark || '—'], + ['Coordinates', viewer.lat && viewer.lng ? `${viewer.lat}, ${viewer.lng}` : '—'] + ].map(([label, value]) => ( + + {label} + {value} + + ))} + + + )} + + + + + + + + setEditOpen(false)} maxWidth="md" fullWidth> + Edit Customer + + + + + + + + +91 }} + /> + + + + + + + {locations.map((l) => {l})} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/pages/invoice/InvoicePreview.jsx b/src/pages/invoice/InvoicePreview.jsx new file mode 100644 index 0000000..6f02edc --- /dev/null +++ b/src/pages/invoice/InvoicePreview.jsx @@ -0,0 +1,180 @@ +import { useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { + Box, Card, Stack, Button, Typography, Chip, Divider, IconButton, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Grid, + Dialog, DialogTitle, DialogContent, DialogActions, TextField +} from '@mui/material'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import PrintOutlinedIcon from '@mui/icons-material/PrintOutlined'; +import PaymentsOutlinedIcon from '@mui/icons-material/PaymentsOutlined'; + +import PageHeader from '@/components/PageHeader'; +import Logo from '@/components/Logo'; +import { invoices, invoiceLineItems, tenants } from '@/data/mock'; +import { inr } from '@/utils/format'; + +export default function InvoicePreview() { + const navigate = useNavigate(); + const { id } = useParams(); + const [payOpen, setPayOpen] = useState(false); + + const invoice = invoices.find((i) => String(i.id) === String(id)) || invoices[0]; + const tenant = tenants[0]; + + const subTotal = invoiceLineItems.reduce((a, l) => a + l.amount, 0); + const discount = Math.round(subTotal * 0.05); + const taxable = subTotal - discount; + const tax = Math.round(taxable * 0.18); + const grandTotal = taxable + tax; + + return ( + <> + + navigate('/invoice')} sx={{ border: 1, borderColor: 'grey.300' }}> + + + + + + + } + /> + + + + {/* Top band */} + + + + From + Doormile Logistics Pvt. Ltd. + No. 7, Brigade Road + Bengaluru, Karnataka 560001 + GSTIN: 29ABCDE1234F1Z5 + billing@doormile.in + + + INVOICE + To + {tenant.name} + {tenant.contact} + {tenant.address} + {tenant.city} {tenant.postcode} + {tenant.email} + + + + + + {/* Invoice meta */} + + + Invoice No + {invoice.invoiceId} + + + Date + {invoice.invoiceDate} + + + Due Date + {invoice.dueDate} + + + Period + {invoice.period} + + + + {/* Line items */} + + + + + S.No + Particulars + Unit + Quantity + Rate + Other Charges + Amount + + + + {invoiceLineItems.map((l, idx) => ( + + {idx + 1} + {l.particulars} + {l.unit} + {l.qty} + {inr(l.rate)} + {inr(l.other)} + {inr(l.amount)} + + ))} + +
+
+ + {/* Totals */} + + + + Sub Total + {inr(subTotal)} + + + Discount (5%) + - {inr(discount)} + + + Tax (18% GST) + {inr(tax)} + + + + Grand Total + {inr(grandTotal)} + + + + + {/* Notes + accent */} + + Notes & Terms + + Payment is due within 15 days of the invoice date. Please make payments via NEFT/RTGS to the + registered account. A 1.5% monthly interest applies to overdue balances. This is a + computer-generated invoice and does not require a signature. + + + +
+
+ + {/* Update Payment Dialog */} + setPayOpen(false)} maxWidth="xs" fullWidth> + Update Payment + + + + + + + + + + + + + ); +} diff --git a/src/pages/invoice/Invoices.jsx b/src/pages/invoice/Invoices.jsx new file mode 100644 index 0000000..30df382 --- /dev/null +++ b/src/pages/invoice/Invoices.jsx @@ -0,0 +1,154 @@ +import { useState, useMemo } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Grid, Card, Stack, Button, Box, Tabs, Tab, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton, + Tooltip, TablePagination, Dialog, DialogTitle, DialogContent, DialogActions, Typography +} from '@mui/material'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import dayjs from 'dayjs'; +import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; +import CalendarTodayOutlinedIcon from '@mui/icons-material/CalendarTodayOutlined'; +import ReceiptLongOutlinedIcon from '@mui/icons-material/ReceiptLongOutlined'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; +import HourglassEmptyOutlinedIcon from '@mui/icons-material/HourglassEmptyOutlined'; +import ErrorOutlineOutlinedIcon from '@mui/icons-material/ErrorOutlineOutlined'; + +import PageHeader from '@/components/PageHeader'; +import StatCard from '@/components/StatCard'; +import StatusChip from '@/components/StatusChip'; +import TabLabelCount from '@/components/TabLabelCount'; +import { invoices } from '@/data/mock'; +import { inr } from '@/utils/format'; + +const TABS = [ + { key: 'all', label: 'All' }, + { key: 'open', label: 'Open' }, + { key: 'overdue', label: 'Overdue' }, + { key: 'paid', label: 'Paid' } +]; + +export default function Invoices() { + const navigate = useNavigate(); + const [tab, setTab] = useState(0); + const [page, setPage] = useState(0); + const [rpp, setRpp] = useState(10); + const [dateOpen, setDateOpen] = useState(false); + const [from, setFrom] = useState(dayjs().startOf('month')); + const [to, setTo] = useState(dayjs()); + + const totals = useMemo(() => { + const sum = (s) => invoices.filter((i) => i.status === s).reduce((a, i) => a + i.amount, 0); + return { + billed: invoices.reduce((a, i) => a + i.amount, 0), + paid: sum('paid'), + open: sum('open'), + overdue: sum('overdue') + }; + }, []); + + const counts = { + all: invoices.length, + open: invoices.filter((i) => i.status === 'open').length, + overdue: invoices.filter((i) => i.status === 'overdue').length, + paid: invoices.filter((i) => i.status === 'paid').length + }; + + const tabKey = TABS[tab].key; + const filtered = useMemo( + () => invoices.filter((i) => (tabKey === 'all' ? true : i.status === tabKey)), + [tabKey] + ); + const paged = filtered.slice(page * rpp, page * rpp + rpp); + + return ( + <> + } onClick={() => setDateOpen(true)} sx={{ color: 'text.secondary', borderColor: 'grey.300' }}> + {from.format('MMM DD')} – {to.format('MMM DD')} + + } + /> + + + + + + + + + + + { setTab(v); setPage(0); }}> + {TABS.map((t, i) => ( + } /> + ))} + + + + + + + + S.No + Client + Invoice Id + Invoice Date + Due Date + Count + Amount + Status + Action + + + + {paged.map((row, idx) => ( + + {page * rpp + idx + 1} + {row.client} + navigate(`/invoice/${row.id}`)}>{row.invoiceId} + {row.invoiceDate} + {row.dueDate} + {row.count} + {inr(row.amount)} + + + + navigate(`/invoice/${row.id}`)}> + + + + + + ))} + +
+
+ setPage(p)} + rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[5, 10, 25, 100]} + /> +
+ + setDateOpen(false)} maxWidth="xs" fullWidth> + Filter by Date + + + Select a date range to filter invoices. + + + v && setFrom(v)} slotProps={{ textField: { fullWidth: true } }} /> + v && setTo(v)} slotProps={{ textField: { fullWidth: true } }} /> + + + + + + + + + ); +} diff --git a/src/pages/maintenance/ComingSoon.jsx b/src/pages/maintenance/ComingSoon.jsx new file mode 100644 index 0000000..de12bf3 --- /dev/null +++ b/src/pages/maintenance/ComingSoon.jsx @@ -0,0 +1,80 @@ +import { useState } from 'react'; +import { Box, Stack, Typography, TextField, Button } from '@mui/material'; + +import Logo from '@/components/Logo'; + +const COUNTDOWN = [ + { label: 'Days', value: 12 }, + { label: 'Hours', value: 8 }, + { label: 'Minutes', value: 45 }, + { label: 'Seconds', value: 30 } +]; + +export default function ComingSoon() { + const [email, setEmail] = useState(''); + + return ( + + + + 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)} + /> + + + + + + ); +} diff --git a/src/pages/maintenance/Error404.jsx b/src/pages/maintenance/Error404.jsx new file mode 100644 index 0000000..afd0b4b --- /dev/null +++ b/src/pages/maintenance/Error404.jsx @@ -0,0 +1,34 @@ +import { useNavigate } from 'react-router-dom'; +import { Box, Stack, Typography, Button } from '@mui/material'; +import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined'; + +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! + + + + + ); +} diff --git a/src/pages/maintenance/Error500.jsx b/src/pages/maintenance/Error500.jsx new file mode 100644 index 0000000..62998d9 --- /dev/null +++ b/src/pages/maintenance/Error500.jsx @@ -0,0 +1,34 @@ +import { useNavigate } from 'react-router-dom'; +import { Box, Stack, Typography, Button } from '@mui/material'; +import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined'; + +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. + + + + + ); +} diff --git a/src/pages/maintenance/UnderConstruction.jsx b/src/pages/maintenance/UnderConstruction.jsx new file mode 100644 index 0000000..be57252 --- /dev/null +++ b/src/pages/maintenance/UnderConstruction.jsx @@ -0,0 +1,45 @@ +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'; + +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. + + + + + ); +} diff --git a/src/pages/orders/AssignOrders.jsx b/src/pages/orders/AssignOrders.jsx new file mode 100644 index 0000000..d01b7dd --- /dev/null +++ b/src/pages/orders/AssignOrders.jsx @@ -0,0 +1,135 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Grid, Stack, Button, IconButton, Typography, Box, TextField, MenuItem, Chip, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow +} from '@mui/material'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import AutorenewIcon from '@mui/icons-material/Autorenew'; +import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined'; +import TwoWheelerOutlinedIcon from '@mui/icons-material/TwoWheelerOutlined'; +import MapOutlinedIcon from '@mui/icons-material/MapOutlined'; +import RouteOutlinedIcon from '@mui/icons-material/RouteOutlined'; +import PersonAddAltOutlinedIcon from '@mui/icons-material/PersonAddAltOutlined'; + +import PageHeader from '@/components/PageHeader'; +import StatCard from '@/components/StatCard'; +import MainCard from '@/components/MainCard'; +import StatusChip from '@/components/StatusChip'; +import UserAvatar from '@/components/UserAvatar'; +import { orders, riders } from '@/data/mock'; +import { inr } from '@/utils/format'; + +const ZONES = ['Zone A', 'Zone B', 'Zone C', 'Zone D']; +const PROFIT = [42, 58, 36, 50, 28, 64]; + +// derive assignment rows from orders + riders mock +const ROWS = orders.slice(0, 6).map((o, i) => ({ + ...o, + zone: ZONES[i % ZONES.length], + rider: riders[i % riders.length].name, + profit: PROFIT[i % PROFIT.length] +})); + +export default function AssignOrders() { + const navigate = useNavigate(); + const [payment, setPayment] = useState('all'); + const [rider, setRider] = useState('auto'); + + return ( + <> + + navigate('/orders')} size="small"> + Assign Orders + + } + breadcrumbs={[{ label: 'Orders', to: '/orders' }, { label: 'Assign Orders' }]} + action={ + + } + /> + + + + + + + + + + + + + + # + Zone + Tenant + Order Location + Pickup + Delivery + Notes + Rider + Type + Profit + Charges + KMS + + + + {ROWS.map((r) => ( + + {r.id} + + + + {r.tenant} + {r.location} + {r.pickup} + {r.drop} + {r.notes || '—'} + + + + {r.rider} + + + + {inr(r.profit)} + {inr(r.charges)} + {r.kms} + + ))} + +
+
+
+ + + + + setPayment(e.target.value)}> + All Payments + Prepaid + COD + + + + setRider(e.target.value)}> + Auto Assign + {riders.map((rd) => {rd.name})} + + + + + + + + + + + ); +} diff --git a/src/pages/orders/CreateMultipleOrders.jsx b/src/pages/orders/CreateMultipleOrders.jsx new file mode 100644 index 0000000..b5d948d --- /dev/null +++ b/src/pages/orders/CreateMultipleOrders.jsx @@ -0,0 +1,254 @@ +import { useState, useMemo } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Grid, Card, CardContent, Stack, Button, TextField, MenuItem, Typography, Divider, Box, + RadioGroup, Radio, FormControlLabel, FormControl, FormLabel, Chip, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton, Tooltip, + Dialog, DialogTitle, DialogContent, DialogActions, List, ListItem, ListItemButton, + ListItemIcon, ListItemText, Checkbox, InputAdornment +} from '@mui/material'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import dayjs from 'dayjs'; +import UploadFileOutlinedIcon from '@mui/icons-material/UploadFileOutlined'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import SearchIcon from '@mui/icons-material/Search'; +import AddIcon from '@mui/icons-material/Add'; +import PersonAddAltOutlinedIcon from '@mui/icons-material/PersonAddAltOutlined'; + +import PageHeader from '@/components/PageHeader'; +import MainCard from '@/components/MainCard'; +import UserAvatar from '@/components/UserAvatar'; +import { locations, tenantsList, tenants, customers } from '@/data/mock'; +import { inr } from '@/utils/format'; + +const TIME_SLOTS = ['09:00 - 11:00', '11:00 - 13:00', '13:00 - 15:00', '15:00 - 17:00', '17:00 - 19:00']; + +// derive table rows from the customers mock +const buildRows = () => + customers.slice(0, 4).map((c, i) => ({ + id: c.id, + customer: c.name, + address: c.address, + qty: [2, 1, 3, 1][i], + cash: [0, 1299, 0, 450][i], + kms: [4.2, 12.8, 6.5, 9.1][i], + charge: [86, 142, 98, 110][i] + })); + +export default function CreateMultipleOrders() { + const navigate = useNavigate(); + const [date, setDate] = useState(dayjs()); + const [slot, setSlot] = useState(''); + const [dropMode, setDropMode] = useState('csv'); + const [rows, setRows] = useState(buildRows); + const [dialogOpen, setDialogOpen] = useState(false); + const [search, setSearch] = useState(''); + const [picked, setPicked] = useState([]); + + const totals = useMemo( + () => + rows.reduce( + (acc, r) => ({ + qty: acc.qty + r.qty, + cash: acc.cash + r.cash, + kms: +(acc.kms + r.kms).toFixed(1), + charge: acc.charge + r.charge + }), + { qty: 0, cash: 0, kms: 0, charge: 0 } + ), + [rows] + ); + + const removeRow = (id) => setRows((p) => p.filter((r) => r.id !== id)); + + const filteredCustomers = customers.filter( + (c) => !search || `${c.name} ${c.location}`.toLowerCase().includes(search.toLowerCase()) + ); + const toggle = (id) => setPicked((p) => (p.includes(id) ? p.filter((x) => x !== id) : [...p, id])); + + const addSelected = () => { + const existing = new Set(rows.map((r) => r.id)); + const additions = customers + .filter((c) => picked.includes(c.id) && !existing.has(c.id)) + .map((c) => ({ id: c.id, customer: c.name, address: c.address, qty: 1, cash: 0, kms: 5, charge: 90 })); + setRows((p) => [...p, ...additions]); + setPicked([]); + setDialogOpen(false); + }; + + return ( + <> + + + + + + {/* top selects */} + + + + {locations.map((l) => {l})} + + + + + {tenantsList.map((t) => {t})} + + + + + {tenants.map((t) => {t.address}, {t.city})} + + + + + + + + + + + setSlot(e.target.value)}> + {TIME_SLOTS.map((s) => {s})} + + + + + + Drop + + + Add drop points using + setDropMode(e.target.value)}> + } label="Excel / CSV" /> + } label="Selection" /> + + + + + {dropMode === 'csv' ? ( + + + {}} + sx={{ bgcolor: 'primary.lighter', color: 'primary.dark', fontWeight: 600 }} + /> + + ) : ( + + )} + + + + + + + + + + + S.No + Customer + Address + Quantity + Cash Collect + Kms + Charge + Action + + + + {rows.map((r, i) => ( + + {i + 1} + + + + {r.customer} + + + {r.address} + {r.qty} + {r.cash ? inr(r.cash) : '—'} + {r.kms} + {inr(r.charge)} + + + removeRow(r.id)}> + + + + + + ))} + + Total + {totals.qty} + {inr(totals.cash)} + {totals.kms} + {inr(totals.charge)} + + + +
+
+
+ + + + + + + + + +
+ + {/* Customer selection dialog */} + setDialogOpen(false)} fullWidth maxWidth="sm"> + Select Customers + + setSearch(e.target.value)} + sx={{ mb: 1.5 }} + InputProps={{ startAdornment: }} + /> + + {filteredCustomers.map((c) => ( + + toggle(c.id)}> + + + + + + + + ))} + + + + + + + + + ); +} diff --git a/src/pages/orders/CreateOrder.jsx b/src/pages/orders/CreateOrder.jsx new file mode 100644 index 0000000..5613814 --- /dev/null +++ b/src/pages/orders/CreateOrder.jsx @@ -0,0 +1,142 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Grid, Card, CardContent, Stack, Button, TextField, MenuItem, Typography, Divider, + Autocomplete, FormControlLabel, Checkbox, Box +} from '@mui/material'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import dayjs from 'dayjs'; +import AddIcon from '@mui/icons-material/Add'; + +import PageHeader from '@/components/PageHeader'; +import { locations, tenantsList, tenants } from '@/data/mock'; + +const TIME_SLOTS = ['09:00 - 11:00', '11:00 - 13:00', '13:00 - 15:00', '15:00 - 17:00', '17:00 - 19:00']; + +const SectionTitle = ({ children }) => ( + <> + {children} + + +); + +function AddressFields({ saveForLater, onSaveForLater }) { + return ( + + + + + + + + + + + + + + + + {locations.map((l) => {l})} + + + + + + + + + + onSaveForLater(e.target.checked)} />} + label="Save for later" + /> + + + ); +} + +export default function CreateOrder() { + const navigate = useNavigate(); + const [deliveryDate, setDeliveryDate] = useState(dayjs()); + const [slot, setSlot] = useState(''); + const [savePickup, setSavePickup] = useState(false); + const [saveDrop, setSaveDrop] = useState(false); + + return ( + <> + + + + + {/* Top row */} + + + } + /> + + + } + /> + + + `${t.name} — ${t.address}`)} + renderInput={(params) => } + /> + + + + + Pickup Details + + + + + Drop Details + + + + + Schedule + + + + + + setSlot(e.target.value)}> + {TIME_SLOTS.map((s) => {s})} + + + + + + + + + + + + + + + + + ); +} diff --git a/src/pages/orders/OrderDetails.jsx b/src/pages/orders/OrderDetails.jsx new file mode 100644 index 0000000..cab61d6 --- /dev/null +++ b/src/pages/orders/OrderDetails.jsx @@ -0,0 +1,170 @@ +import { useParams, useNavigate } from 'react-router-dom'; +import { + Grid, Card, CardContent, Stack, Typography, Box, Button, Divider, IconButton, Avatar, + Step, Stepper, StepLabel, StepConnector, stepConnectorClasses, styled, Chip +} from '@mui/material'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import CallOutlinedIcon from '@mui/icons-material/CallOutlined'; +import PhoneOutlinedIcon from '@mui/icons-material/PhoneOutlined'; +import LocationOnOutlinedIcon from '@mui/icons-material/LocationOnOutlined'; +import PersonOutlineIcon from '@mui/icons-material/PersonOutline'; +import CheckIcon from '@mui/icons-material/Check'; + +import PageHeader from '@/components/PageHeader'; +import MainCard from '@/components/MainCard'; +import StatusChip from '@/components/StatusChip'; +import MapPlaceholder from '@/components/MapPlaceholder'; +import UserAvatar from '@/components/UserAvatar'; +import { orders, orderTimeline, deliveries } from '@/data/mock'; +import { inr } from '@/utils/format'; + +const RedConnector = styled(StepConnector)(({ theme }) => ({ + [`& .${stepConnectorClasses.line}`]: { borderColor: theme.palette.grey[300], borderLeftWidth: 2, minHeight: 28 }, + [`&.${stepConnectorClasses.active} .${stepConnectorClasses.line}, &.${stepConnectorClasses.completed} .${stepConnectorClasses.line}`]: + { borderColor: theme.palette.primary.main } +})); + +function Dot({ active }) { + return ( + + {active ? : } + + ); +} + +export default function OrderDetails() { + const { id } = useParams(); + const navigate = useNavigate(); + const order = orders.find((o) => o.id === id) || orders[1]; + const delivery = deliveries.find((d) => d.id === order.id) || deliveries[1]; + + return ( + <> + + navigate('/orders')} size="small"> + Order Details + + } + breadcrumbs={[{ label: 'Orders', to: '/orders' }, { label: order.id }]} + action={ + + + + + } + /> + + + {/* Left column */} + + + + + + + Order ID + {order.id} + + + + + + + + } /> + + {inr(order.charges)}} /> + + + + + + + + + {order.customer} + Recipient + + + + + + + + + + } sx={{ ml: 0.5 }}> + {orderTimeline.map((s) => ( + + }> + + {s.label} + {s.time} + + + + ))} + + + + + + {/* Right column */} + + + + + + + + + + + + + + + + + + {delivery.rider} + Rider · +91 98450 11223 + + + + + + + + + + + ); +} + +const Row = ({ label, value }) => ( + + {label} + {typeof value === 'string' ? {value} : value} + +); + +const IconRow = ({ icon: Icon, text }) => ( + + + {text} + +); + +const RouteEnd = ({ color, title, text }) => ( + + + + {title} + {text} + + +); diff --git a/src/pages/orders/OrdersList.jsx b/src/pages/orders/OrdersList.jsx new file mode 100644 index 0000000..62a42bf --- /dev/null +++ b/src/pages/orders/OrdersList.jsx @@ -0,0 +1,195 @@ +import { useState, useMemo } from 'react'; +import { useNavigate, useSearchParams } from 'react-router-dom'; +import { + Grid, Card, Stack, Button, TextField, MenuItem, InputAdornment, Box, Tabs, Tab, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Checkbox, IconButton, + Tooltip, TablePagination, Typography, SpeedDial, SpeedDialAction, Chip +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import AddIcon from '@mui/icons-material/Add'; +import PostAddOutlinedIcon from '@mui/icons-material/PostAddOutlined'; +import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import CalendarTodayOutlinedIcon from '@mui/icons-material/CalendarTodayOutlined'; +import AutoAwesomeOutlinedIcon from '@mui/icons-material/AutoAwesomeOutlined'; +import PersonAddAltOutlinedIcon from '@mui/icons-material/PersonAddAltOutlined'; +import TuneIcon from '@mui/icons-material/Tune'; + +import PageHeader from '@/components/PageHeader'; +import StatCard from '@/components/StatCard'; +import StatusChip from '@/components/StatusChip'; +import TabLabelCount from '@/components/TabLabelCount'; +import { orders } from '@/data/mock'; +import { inr } from '@/utils/format'; + +import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined'; +import HourglassEmptyOutlinedIcon from '@mui/icons-material/HourglassEmptyOutlined'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; +import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined'; + +const TABS = [ + { key: 'created', label: 'Created' }, + { key: 'pending', label: 'Pending' }, + { key: 'delivered', label: 'Delivered' }, + { key: 'cancelled', label: 'Cancelled' } +]; + +export default function OrdersList() { + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const [tab, setTab] = useState(0); + const [search, setSearch] = useState(searchParams.get('q') || ''); + const [tenant, setTenant] = useState('all'); + const [page, setPage] = useState(0); + const [rpp, setRpp] = useState(5); + const [selected, setSelected] = useState([]); + + const tabKey = TABS[tab].key; + const filtered = useMemo( + () => + orders.filter((o) => { + const matchTab = tabKey === 'created' ? true : o.status === tabKey; + const matchTenant = tenant === 'all' || o.tenant === tenant; + const matchSearch = + !search || + [o.id, o.customer, o.pickup, o.drop, o.tenant].join(' ').toLowerCase().includes(search.toLowerCase()); + return matchTab && matchTenant && matchSearch; + }), + [tabKey, tenant, search] + ); + + const paged = filtered.slice(page * rpp, page * rpp + rpp); + const toggle = (id) => setSelected((p) => (p.includes(id) ? p.filter((x) => x !== id) : [...p, id])); + const counts = { + created: orders.length, + pending: orders.filter((o) => o.status === 'pending').length, + delivered: orders.filter((o) => o.status === 'delivered').length, + cancelled: orders.filter((o) => o.status === 'cancelled').length + }; + + return ( + <> + + + + + } + /> + + + + + + + + + + {/* filter toolbar */} + + setSearch(e.target.value)} + sx={{ minWidth: 240 }} + InputProps={{ startAdornment: }} + /> + + + setTenant(e.target.value)} sx={{ minWidth: 170 }} label="Tenant"> + All Tenants + {[...new Set(orders.map((o) => o.tenant))].map((t) => {t})} + + + All Locations + {[...new Set(orders.map((o) => o.location))].map((l) => {l})} + + + + + { setTab(v); setPage(0); }}> + {TABS.map((t, i) => ( + } /> + ))} + + + + {selected.length > 0 && ( + + {selected.length} selected + + + )} + + + + + + + 0 && selected.length < paged.length} + checked={paged.length > 0 && selected.length === paged.length} + onChange={(e) => setSelected(e.target.checked ? paged.map((o) => o.id) : [])} + /> + + # + Tenant + Location + Pickup + Drop + QTY + COD + KMS + Charges + Notes + Status + Actions + + + + {paged.map((o) => ( + + toggle(o.id)} /> + navigate(`/orders/${o.id}`)}>{o.id} + {o.tenant} + {o.location} + {o.pickup} + {o.drop} + {o.qty} + {o.cod ? inr(o.cod) : '—'} + {o.kms} + {inr(o.charges)} + {o.notes || '—'} + + + navigate(`/orders/${o.id}`)}> + + + + + ))} + +
+
+ setPage(p)} + rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[5, 10, 25]} + /> +
+ + } sx={{ position: 'fixed', bottom: 28, right: 28 }} FabProps={{ color: 'primary' }}> + } tooltipTitle="AI Optimisation" onClick={() => navigate('/orders/assign')} /> + } tooltipTitle="Manual Assign" onClick={() => navigate('/orders/assign')} /> + } tooltipTitle="Delete" /> + + + ); +} diff --git a/src/pages/reports/OrdersDetails.jsx b/src/pages/reports/OrdersDetails.jsx new file mode 100644 index 0000000..e052756 --- /dev/null +++ b/src/pages/reports/OrdersDetails.jsx @@ -0,0 +1,190 @@ +import { useState, useMemo } from 'react'; +import { + Grid, Card, Stack, Button, TextField, MenuItem, InputAdornment, Box, IconButton, Tooltip, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TablePagination, Typography, + Dialog, DialogTitle, DialogContent, DialogActions +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import CalendarTodayOutlinedIcon from '@mui/icons-material/CalendarTodayOutlined'; +import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined'; +import MapOutlinedIcon from '@mui/icons-material/MapOutlined'; +import CloseIcon from '@mui/icons-material/Close'; + +import PageHeader from '@/components/PageHeader'; +import StatusChip from '@/components/StatusChip'; +import MapPlaceholder from '@/components/MapPlaceholder'; +import { ordersDetailReport, locations, tenantsList } from '@/data/mock'; +import { inr } from '@/utils/format'; + +const STATUSES = ['all', 'created', 'pending', 'picked', 'active', 'delivered', 'cancelled']; + +export default function OrdersDetails() { + const [location, setLocation] = useState('all'); + const [tenant, setTenant] = useState('all'); + const [loc2, setLoc2] = useState('all'); + const [status, setStatus] = useState('all'); + const [search, setSearch] = useState(''); + const [page, setPage] = useState(0); + const [rpp, setRpp] = useState(10); + const [mapRow, setMapRow] = useState(null); + const [exportOpen, setExportOpen] = useState(false); + + const filtered = useMemo( + () => + ordersDetailReport.filter((o) => { + const matchStatus = status === 'all' || o.status === status; + const matchTenant = tenant === 'all' || o.client === tenant; + const matchSearch = + !search || + [o.id, o.client, o.pickup, o.drop].join(' ').toLowerCase().includes(search.toLowerCase()); + return matchStatus && matchTenant && matchSearch; + }), + [status, tenant, search] + ); + + const paged = filtered.slice(page * rpp, page * rpp + rpp); + + return ( + <> + setLocation(e.target.value)} sx={{ minWidth: 160 }} label="Location"> + All Locations + {locations.map((l) => {l})} +
+ } + /> + + + + setTenant(e.target.value)} sx={{ minWidth: 170 }} label="Tenant"> + All Tenants + {tenantsList.map((t) => {t})} + + setLoc2(e.target.value)} sx={{ minWidth: 160 }} label="Location"> + All Locations + {locations.map((l) => {l})} + + + { setStatus(e.target.value); setPage(0); }} sx={{ minWidth: 150 }} label="Status"> + {STATUSES.map((s) => {s === 'all' ? 'All Status' : s[0].toUpperCase() + s.slice(1)})} + + { setSearch(e.target.value); setPage(0); }} sx={{ minWidth: 220 }} + InputProps={{ startAdornment: }} + /> + + + + + + + + + # + + Client + Pickup + Drop + Status + Assigned + Accepted + Arrived + Picked + Active + Delivered + Cancelled + Notes + KMS + Charges + + + + {paged.map((o, i) => ( + + {page * rpp + i + 1} + + + setMapRow(o)}> + + + {o.client} + {o.pickup} + {o.drop} + + {o.assigned} + {o.accepted} + {o.arrived} + {o.picked} + {o.active} + {o.delivered} + {o.cancelled} + {o.notes || '—'} + {o.kms} + {inr(o.charges)} + + ))} + +
+
+ setPage(p)} + rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[10, 25, 50]} + /> +
+ + {/* Map dialog */} + setMapRow(null)} fullScreen> + + + Route — {mapRow?.id} + + {mapRow?.pickup} → {mapRow?.drop} + + + setMapRow(null)}> + + + + + + + {/* Export dialog */} + setExportOpen(false)} maxWidth="xs" fullWidth> + Export Report + + + The export will include {filtered.length} record(s) matching the current filters: + + + + + + + + + + + + + + + + + ); +} + +function Filter({ label, value }) { + return ( + + {label} + {value} + + ); +} diff --git a/src/pages/reports/OrdersSummary.jsx b/src/pages/reports/OrdersSummary.jsx new file mode 100644 index 0000000..f97320a --- /dev/null +++ b/src/pages/reports/OrdersSummary.jsx @@ -0,0 +1,195 @@ +import { useState, Fragment } from 'react'; +import { + Grid, Card, Stack, Button, TextField, MenuItem, Box, Collapse, IconButton, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography +} from '@mui/material'; +import CalendarTodayOutlinedIcon from '@mui/icons-material/CalendarTodayOutlined'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; + +import PageHeader from '@/components/PageHeader'; +import { ordersSummary, locations, tenantsList } from '@/data/mock'; +import { inr } from '@/utils/format'; + +// Show 0 in red, anything else normally. +function NumCell({ value, align = 'center', bold = false }) { + const zero = Number(value) === 0; + return ( + + {value} + + ); +} + +export default function OrdersSummary() { + const [open, setOpen] = useState({}); + const [location, setLocation] = useState('all'); + const [tenant, setTenant] = useState('all'); + const [loc2, setLoc2] = useState('all'); + + const toggle = (id) => setOpen((p) => ({ ...p, [id]: !p[id] })); + + const totals = ordersSummary.reduce( + (a, r) => ({ + oPending: a.oPending + r.orders.pending, + oCancelled: a.oCancelled + r.orders.cancelled, + oCompleted: a.oCompleted + r.orders.completed, + dPending: a.dPending + r.deliveries.pending, + dCancelled: a.dCancelled + r.deliveries.cancelled, + dCompleted: a.dCompleted + r.deliveries.completed, + collection: a.collection + r.collection, + kms: a.kms + r.kms, + actualKms: a.actualKms + r.actualKms, + amount: a.amount + r.amount + }), + { oPending: 0, oCancelled: 0, oCompleted: 0, dPending: 0, dCancelled: 0, dCompleted: 0, collection: 0, kms: 0, actualKms: 0, amount: 0 } + ); + + const headSx = { bgcolor: 'primary.lighter', fontWeight: 700, color: 'primary.dark', whiteSpace: 'nowrap' }; + + return ( + <> + setLocation(e.target.value)} sx={{ minWidth: 160 }} label="Location"> + All Locations + {locations.map((l) => {l})} +
+ } + /> + + + + + + setTenant(e.target.value)} sx={{ minWidth: 170 }} label="Tenant"> + All Tenants + {tenantsList.map((t) => {t})} + + setLoc2(e.target.value)} sx={{ minWidth: 160 }} label="Location"> + All Locations + {locations.map((l) => {l})} + + + + + + + + + # + Tenant / Location + Orders + Deliveries + Collection Amt + Kms / Actual + Amount + Action + + + Pending + Cancelled + Completed + Pending + Cancelled + Completed + + + + {ordersSummary.map((r, idx) => ( + + + + toggle(r.id)}> + {open[r.id] ? : } + + + {idx + 1} + + {r.tenant} + {r.location} + + + + + + + + {inr(r.collection)} + {r.kms} / {r.actualKms} + {inr(r.amount)} + + toggle(r.id)}> + + + + + + + Riders +
+ + + # + Rider + Orders + Deliveries + Pending + Cancelled + Completed + Collection Amt + Kms / Actual + Charges + + + + {r.riders.map((rd, i) => ( + + {i + 1} + {rd.rider} + + + + + + {inr(rd.collection)} + {rd.kms} / {rd.actualKms} + {inr(rd.charges)} + + ))} + +
+ + + + + + ))} + + {/* totals */} + + + Totals + + + + + + + {inr(totals.collection)} + {totals.kms} / {totals.actualKms} + {inr(totals.amount)} + + + + +
+
+ + ); +} diff --git a/src/pages/reports/RidersLogs.jsx b/src/pages/reports/RidersLogs.jsx new file mode 100644 index 0000000..c6ee68b --- /dev/null +++ b/src/pages/reports/RidersLogs.jsx @@ -0,0 +1,105 @@ +import { useState, useMemo } from 'react'; +import { + Box, Paper, Stack, Button, TextField, InputAdornment, IconButton, Checkbox, Typography, Divider, Tooltip +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import MenuIcon from '@mui/icons-material/Menu'; +import RefreshIcon from '@mui/icons-material/Refresh'; + +import PageHeader from '@/components/PageHeader'; +import MapPlaceholder from '@/components/MapPlaceholder'; +import { ridersLive } from '@/data/mock'; + +// spread active riders across the map at distinct positions +const POSITIONS = [ + { x: '24%', y: '32%' }, + { x: '58%', y: '28%' }, + { x: '40%', y: '60%' }, + { x: '72%', y: '55%' }, + { x: '30%', y: '72%' }, + { x: '64%', y: '78%' } +]; + +export default function RidersLogs() { + const [search, setSearch] = useState(''); + const [selected, setSelected] = useState(ridersLive.filter((r) => r.active).map((r) => r.userid)); + + const filtered = useMemo( + () => + ridersLive.filter((r) => + !search || [r.name, r.phone, r.userid].join(' ').toLowerCase().includes(search.toLowerCase()) + ), + [search] + ); + + const allChecked = filtered.length > 0 && filtered.every((r) => selected.includes(r.userid)); + const someChecked = filtered.some((r) => selected.includes(r.userid)) && !allChecked; + + const toggle = (id) => setSelected((p) => (p.includes(id) ? p.filter((x) => x !== id) : [...p, id])); + const toggleAll = (checked) => + setSelected(checked ? [...new Set([...selected, ...filtered.map((r) => r.userid)])] : selected.filter((id) => !filtered.some((r) => r.userid === id))); + + const mapRiders = ridersLive + .filter((r) => r.active && selected.includes(r.userid)) + .map((r, i) => ({ ...POSITIONS[i % POSITIONS.length], active: true })); + + return ( + <> + + + + {/* Left side panel */} + + + setSearch(e.target.value)} + InputProps={{ startAdornment: }} + /> + + + + toggleAll(e.target.checked)} /> + All + + {selected.length} selected + + + + {filtered.map((r) => ( + + toggle(r.userid)} sx={{ mt: -0.5 }} /> + + + + {r.name} + + {r.phone} + + {r.userid} + {r.lastLog} + + + + ))} + {filtered.length === 0 && ( + No riders found + )} + + + + {/* Map area */} + + + + Riders Locations + + + + + + + ); +} diff --git a/src/pages/reports/RidersSummary.jsx b/src/pages/reports/RidersSummary.jsx new file mode 100644 index 0000000..1203e58 --- /dev/null +++ b/src/pages/reports/RidersSummary.jsx @@ -0,0 +1,167 @@ +import { useState, Fragment } from 'react'; +import { + Card, Stack, Button, TextField, MenuItem, Box, Collapse, IconButton, Chip, Tooltip, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, + Dialog, DialogTitle, DialogContent +} from '@mui/material'; +import CalendarTodayOutlinedIcon from '@mui/icons-material/CalendarTodayOutlined'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import RoomOutlinedIcon from '@mui/icons-material/RoomOutlined'; +import CloseIcon from '@mui/icons-material/Close'; + +import PageHeader from '@/components/PageHeader'; +import UserAvatar from '@/components/UserAvatar'; +import MapPlaceholder from '@/components/MapPlaceholder'; +import { ridersSummary, locations } from '@/data/mock'; +import { inr } from '@/utils/format'; + +function NumCell({ value, align = 'center' }) { + const zero = Number(value) === 0; + return {value}; +} + +function KmsChips({ kms, actual }) { + return ( + + + + + ); +} + +export default function RidersSummary() { + const [open, setOpen] = useState({}); + const [location, setLocation] = useState('all'); + const [mapRider, setMapRider] = useState(null); + + const toggle = (id) => setOpen((p) => ({ ...p, [id]: !p[id] })); + const totalAmount = ridersSummary.reduce((a, r) => a + r.amount, 0); + + const headSx = { bgcolor: 'primary.lighter', fontWeight: 700, color: 'primary.dark', whiteSpace: 'nowrap' }; + + return ( + <> + setLocation(e.target.value)} sx={{ minWidth: 160 }} label="Location"> + All Locations + {locations.map((l) => {l})} + + } + /> + + + + + + + + + + + + # + Rider + Orders + Pending + Cancelled + Delivered + KMS + Amount + Action + + + + {ridersSummary.map((r, idx) => ( + + + {idx + 1} + + + + {r.rider} + + + + + + + + {inr(r.amount)} + + + toggle(r.id)}> + {open[r.id] ? : } + + + + setMapRider(r)}> + + + + + + + + Client Summary +
+ + + # + Client + All + Pending + Completed + Cancelled + Kms + Amount + + + + {r.clients.map((c, i) => ( + + {i + 1} + {c.client} + + + + + + {inr(c.amount)} + + ))} + +
+ + + + + + ))} + + +
+ + + Total Amount + {inr(totalAmount)} + +
+ + setMapRider(null)} maxWidth="md" fullWidth> + + {mapRider?.rider} — Location + setMapRider(null)}> + + + + + + + ); +} diff --git a/src/pages/riders/CreateRider.jsx b/src/pages/riders/CreateRider.jsx new file mode 100644 index 0000000..c84c08f --- /dev/null +++ b/src/pages/riders/CreateRider.jsx @@ -0,0 +1,71 @@ +import { useNavigate } from 'react-router-dom'; +import { + Grid, Card, CardContent, Stack, Button, TextField, Divider, InputAdornment +} from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; + +import PageHeader from '@/components/PageHeader'; + +export default function CreateRider() { + const navigate = useNavigate(); + + return ( + <> + + + + + + + + + + +91 }} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/pages/riders/EditRider.jsx b/src/pages/riders/EditRider.jsx new file mode 100644 index 0000000..898694b --- /dev/null +++ b/src/pages/riders/EditRider.jsx @@ -0,0 +1,180 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Grid, Card, CardContent, Stack, Button, TextField, MenuItem, Typography, Divider, Box, IconButton, InputAdornment +} from '@mui/material'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import dayjs from 'dayjs'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; + +import PageHeader from '@/components/PageHeader'; +import { riders, tenantsList } from '@/data/mock'; + +const SHIFTS = ['Morning', 'Evening', 'Night']; +const ACCOUNT_TYPES = ['Savings', 'Current']; +const VEHICLES = ['Honda Activa', 'TVS Jupiter', 'Hero Splendor', 'Bajaj Pulsar', 'Honda Dio']; + +const SectionTitle = ({ children }) => ( + <> + {children} + + +); + +export default function EditRider() { + const navigate = useNavigate(); + const rider = riders[0]; + const [firstName] = rider.name.split(' '); + const lastName = rider.name.split(' ').slice(1).join(' '); + const [insuranceExpiry, setInsuranceExpiry] = useState(dayjs().add(8, 'month')); + + return ( + <> + + navigate('/riders')}> + Edit Rider + + } + breadcrumbs={[{ label: 'Riders', to: '/riders' }, { label: 'Edit Rider' }]} + /> + + + + Contact Information + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {tenantsList.map((t) => {t})} + + + + + + Charges + + + + {SHIFTS.map((s) => {s})} + + + + ₹ }} /> + + + ₹ }} /> + + + ₹ }} /> + + + + + + Bank Details + + + + + + + + + + {ACCOUNT_TYPES.map((a) => {a})} + + + + + + + + + + + + + + + + Vehicle Details + + + + {VEHICLES.map((v) => {v})} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/pages/riders/Riders.jsx b/src/pages/riders/Riders.jsx new file mode 100644 index 0000000..fc03fad --- /dev/null +++ b/src/pages/riders/Riders.jsx @@ -0,0 +1,221 @@ +import { useState, useMemo, Fragment } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Grid, Card, Stack, Button, TextField, MenuItem, InputAdornment, Box, Tabs, Tab, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton, Tooltip, + TablePagination, Typography, Chip, Collapse +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import AddIcon from '@mui/icons-material/Add'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import GroupsOutlinedIcon from '@mui/icons-material/GroupsOutlined'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; +import TwoWheelerOutlinedIcon from '@mui/icons-material/TwoWheelerOutlined'; +import PowerSettingsNewOutlinedIcon from '@mui/icons-material/PowerSettingsNewOutlined'; + +import PageHeader from '@/components/PageHeader'; +import StatCard from '@/components/StatCard'; +import StatusChip from '@/components/StatusChip'; +import UserAvatar from '@/components/UserAvatar'; +import TabLabelCount from '@/components/TabLabelCount'; +import { riders, riderLogs, locations } from '@/data/mock'; +import { inr } from '@/utils/format'; + +const TABS = [ + { key: 'all', label: 'ALL' }, + { key: 'active', label: 'Active' } +]; + +export default function Riders() { + const navigate = useNavigate(); + const [tab, setTab] = useState(0); + const [search, setSearch] = useState(''); + const [location, setLocation] = useState('all'); + const [page, setPage] = useState(0); + const [rpp, setRpp] = useState(5); + const [expanded, setExpanded] = useState(null); + + const tabKey = TABS[tab].key; + const filtered = useMemo( + () => + riders.filter((r) => { + const matchTab = tabKey === 'all' ? true : r.status !== 'offline'; + const matchLocation = location === 'all' || r.address.includes(location); + const matchSearch = + !search || + [r.id, r.userId, r.name, r.phone, r.address, r.vehicle, r.vehicleNo] + .join(' ') + .toLowerCase() + .includes(search.toLowerCase()); + return matchTab && matchLocation && matchSearch; + }), + [tabKey, location, search] + ); + + const paged = filtered.slice(page * rpp, page * rpp + rpp); + + const counts = { + total: riders.length, + active: riders.filter((r) => r.status === 'online').length, + onDelivery: riders.filter((r) => r.status === 'on-delivery').length, + offline: riders.filter((r) => r.status === 'offline').length, + all: riders.length, + activeTab: riders.filter((r) => r.status !== 'offline').length + }; + + return ( + <> + + setLocation(e.target.value)} sx={{ minWidth: 170 }} label="Location"> + All Locations + {locations.map((l) => {l})} + + + + } + /> + + + + + + + + + + + { setSearch(e.target.value); setPage(0); }} + sx={{ minWidth: 240 }} + InputProps={{ startAdornment: }} + /> + + + + + { setTab(v); setPage(0); }}> + {TABS.map((t, i) => ( + } + /> + ))} + + + + + + + + S.NO + User ID + Rider + Address + Vehicle + Shift + Time + Fare + Fuel + Status + Action + + + + {paged.map((r, i) => ( + + + {page * rpp + i + 1} + {r.userId} + + + + + {r.name} + {r.phone} + + + + {r.address} + + {r.vehicle} + {r.vehicleNo} + + {r.shift} + + + + + + + {inr(r.fare)} + {inr(r.fuel)} + + + + navigate(`/riders/${r.id}/edit`)}> + + + setExpanded(expanded === r.id ? null : r.id)}> + {expanded === r.id ? : } + + + + + + + + + Rider Logs +
+ + + Location + Battery + Charging + Speed + Accuracy + Time + Order + Status + + + + {riderLogs.map((log, li) => ( + + {log.location} + {log.battery} + {log.charging} + {log.speed} + {log.accuracy} + {log.time} + {log.order} + + + ))} + +
+ + + + + + ))} + + +
+ setPage(p)} + rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[5, 10, 25]} + /> +
+ + ); +} diff --git a/src/pages/tenants/CreateClient.jsx b/src/pages/tenants/CreateClient.jsx new file mode 100644 index 0000000..d998415 --- /dev/null +++ b/src/pages/tenants/CreateClient.jsx @@ -0,0 +1,81 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Grid, Stack, Button, TextField, MenuItem, InputAdornment } from '@mui/material'; + +import PageHeader from '@/components/PageHeader'; +import MainCard from '@/components/MainCard'; + +const COUNTRY_CODES = ['+91', '+1', '+44', '+61', '+971']; + +export default function CreateClient() { + const navigate = useNavigate(); + const [code, setCode] = useState('+91'); + const [form, setForm] = useState({ + adminName: '', phone: '', email: '', address: '', + suburb: '', city: '', state: '', postcode: '', doorNo: '', landmark: '' + }); + const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value })); + + return ( + <> + + + + + + + + + + setCode(e.target.value)} + InputProps={{ disableUnderline: true }} sx={{ minWidth: 56 }} + > + {COUNTRY_CODES.map((c) => {c})} + + + ) + }} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/pages/tenants/Tenants.jsx b/src/pages/tenants/Tenants.jsx new file mode 100644 index 0000000..6dc9d17 --- /dev/null +++ b/src/pages/tenants/Tenants.jsx @@ -0,0 +1,258 @@ +import { useState, useMemo, Fragment } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Card, Stack, Button, TextField, InputAdornment, Box, Tabs, Tab, + Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton, + Tooltip, TablePagination, Typography, Collapse, Grid +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import AddIcon from '@mui/icons-material/Add'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; + +import PageHeader from '@/components/PageHeader'; +import StatusChip from '@/components/StatusChip'; +import EmptyState from '@/components/EmptyState'; +import UserAvatar from '@/components/UserAvatar'; +import TabLabelCount from '@/components/TabLabelCount'; +import { tenants, tenantPricing } from '@/data/mock'; +import { inr } from '@/utils/format'; + +const TABS = [ + { key: 'active', label: 'Active' }, + { key: 'pending', label: 'Pending' }, + { key: 'inactive', label: 'Inactive' } +]; + +function ReadField({ label, value }) { + return ( + + {label} + {value || '—'} + + ); +} + +function PricingTab() { + return ( + + + + + + + + + Date + Slab + Base Price + Min Kms + Price/Km + Other Charges + + + + {tenantPricing.map((p, i) => ( + + {p.date} + {p.slab} + {inr(p.basePrice)} + {p.minKms} + {inr(p.pricePerKm)} + {inr(p.otherCharges)} + + ))} + +
+
+
+ ); +} + +function EditTab({ tenant }) { + const [form, setForm] = useState({ + name: tenant.name, contact: tenant.contact, phone: tenant.phone, email: tenant.email, + address: tenant.address, city: tenant.city, postcode: tenant.postcode, lat: tenant.lat, lng: tenant.lng + }); + const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value })); + + return ( + + + + + + + + + + + + + + + + + ); +} + +function TenantRow({ row, index }) { + const [open, setOpen] = useState(false); + const [inner, setInner] = useState(0); + + return ( + + *': { borderBottom: open ? 'unset' : undefined } }}> + + setOpen((o) => !o)}> + {open ? : } + + + {index + 1} + + + + + {row.name} + {row.volume} orders / mo + + + + + {row.contact} + {row.phone} + + + {row.address} + {row.city} · {row.postcode} + + + + + + + + + setInner(v)} sx={{ px: 2 }}> + + + + + + + {inner === 0 && ( + + + + + + + + + + + + )} + {inner === 1 && } + {inner === 2 && } + + + + + + + ); +} + +export default function Tenants() { + const navigate = useNavigate(); + const [tab, setTab] = useState(0); + const [search, setSearch] = useState(''); + const [page, setPage] = useState(0); + const [rpp, setRpp] = useState(10); + + const tabKey = TABS[tab].key; + + const counts = useMemo(() => { + const c = {}; + TABS.forEach((t) => { c[t.key] = tenants.filter((d) => d.status === t.key).length; }); + return c; + }, []); + + const filtered = useMemo( + () => + tenants.filter((t) => { + const matchTab = t.status === tabKey; + const matchSearch = + !search || + [t.name, t.contact, t.email, t.phone, t.city].join(' ').toLowerCase().includes(search.toLowerCase()); + return matchTab && matchSearch; + }), + [tabKey, search] + ); + + const paged = filtered.slice(page * rpp, page * rpp + rpp); + + return ( + <> + } onClick={() => navigate('/tenants/create')}> + Create Client + + } + /> + + + + { setSearch(e.target.value); setPage(0); }} + sx={{ minWidth: 260 }} + InputProps={{ startAdornment: }} + /> + + + + + { setTab(v); setPage(0); }}> + {TABS.map((t, i) => ( + } /> + ))} + + + + + + + + + S.No + Client + Contact + Address + Actions + + + + {paged.length === 0 ? ( + + + + + + ) : ( + paged.map((row, i) => ) + )} + +
+
+ setPage(p)} + rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[5, 10, 25]} + /> +
+ + ); +} diff --git a/src/theme/componentsOverride.js b/src/theme/componentsOverride.js new file mode 100644 index 0000000..e3fa678 --- /dev/null +++ b/src/theme/componentsOverride.js @@ -0,0 +1,121 @@ +import customShadows from './shadows'; + +// ==============================|| DOORMILE THEME - COMPONENT OVERRIDES ||============================== // +// Clean, corporate Material Design tuning for the whole console. + +export default function componentsOverride(theme) { + const { palette } = theme; + + return { + MuiCssBaseline: { + styleOverrides: { + body: { backgroundColor: palette.background.default }, + '*::-webkit-scrollbar': { width: 8, height: 8 }, + '*::-webkit-scrollbar-thumb': { background: palette.grey[300], borderRadius: 8 }, + '*::-webkit-scrollbar-thumb:hover': { background: palette.grey[400] } + } + }, + MuiButton: { + defaultProps: { disableElevation: true }, + styleOverrides: { + root: { borderRadius: 6, fontWeight: 600, padding: '7px 18px' }, + containedPrimary: { + boxShadow: customShadows.primaryGlow, + '&:hover': { boxShadow: customShadows.primaryGlowHover, backgroundColor: palette.primary.dark } + }, + outlined: { borderColor: palette.grey[300] }, + sizeLarge: { padding: '10px 22px', fontSize: '0.9375rem' } + } + }, + MuiIconButton: { + styleOverrides: { root: { borderRadius: 8 } } + }, + MuiCard: { + styleOverrides: { + root: { + borderRadius: 10, + border: `1px solid ${palette.grey[200]}`, + boxShadow: customShadows.card, + backgroundImage: 'none' + } + } + }, + MuiCardHeader: { + defaultProps: { titleTypographyProps: { variant: 'h5' }, subheaderTypographyProps: { variant: 'caption' } }, + styleOverrides: { root: { padding: 20 } } + }, + MuiCardContent: { + styleOverrides: { root: { padding: 20, '&:last-child': { paddingBottom: 20 } } } + }, + MuiPaper: { + defaultProps: { elevation: 0 }, + styleOverrides: { rounded: { borderRadius: 10 } } + }, + MuiChip: { + styleOverrides: { + root: { borderRadius: 6, fontWeight: 600, fontSize: '0.75rem' }, + sizeSmall: { height: 22 }, + label: { paddingLeft: 8, paddingRight: 8 } + } + }, + MuiTableCell: { + styleOverrides: { + root: { borderColor: palette.grey[200], padding: '12px 16px', fontSize: '0.8125rem' }, + head: { + fontWeight: 600, + color: palette.grey[600], + backgroundColor: palette.grey[50], + textTransform: 'none', + whiteSpace: 'nowrap' + } + } + }, + MuiTableRow: { + styleOverrides: { + root: { '&:hover': { backgroundColor: palette.primary.lighter + '66' } } + } + }, + MuiOutlinedInput: { + styleOverrides: { + root: { + borderRadius: 8, + backgroundColor: palette.background.paper, + '& .MuiOutlinedInput-notchedOutline': { borderColor: palette.grey[300] }, + '&:hover .MuiOutlinedInput-notchedOutline': { borderColor: palette.grey[400] } + }, + input: { padding: '11px 14px' } + } + }, + MuiInputLabel: { + styleOverrides: { root: { color: palette.grey[600], fontSize: '0.875rem' } } + }, + MuiTab: { + styleOverrides: { + root: { textTransform: 'none', fontWeight: 600, minHeight: 46, fontSize: '0.875rem' } + } + }, + MuiTabs: { + styleOverrides: { indicator: { height: 3, borderRadius: 3 } } + }, + MuiTooltip: { + styleOverrides: { + tooltip: { backgroundColor: palette.grey[800], borderRadius: 6, fontSize: '0.75rem', padding: '6px 10px' } + } + }, + MuiDialog: { + styleOverrides: { paper: { borderRadius: 12 } } + }, + MuiAvatar: { + styleOverrides: { root: { fontWeight: 600, fontSize: '0.875rem' } } + }, + MuiListItemButton: { + styleOverrides: { root: { borderRadius: 8 } } + }, + MuiLinearProgress: { + styleOverrides: { root: { borderRadius: 8, height: 6, backgroundColor: palette.grey[200] } } + }, + MuiMenu: { + styleOverrides: { paper: { borderRadius: 10, boxShadow: customShadows.dropdown, marginTop: 4 } } + } + }; +} diff --git a/src/theme/index.js b/src/theme/index.js new file mode 100644 index 0000000..53e9cb8 --- /dev/null +++ b/src/theme/index.js @@ -0,0 +1,20 @@ +import { createTheme } from '@mui/material/styles'; + +import palette from './palette'; +import typography from './typography'; +import customShadows from './shadows'; +import componentsOverride from './componentsOverride'; + +// ==============================|| DOORMILE THEME - ENTRY ||============================== // + +let theme = createTheme({ + palette, + typography, + shape: { borderRadius: 6 }, + customShadows, + mixins: { toolbar: { minHeight: 64 } } +}); + +theme.components = componentsOverride(theme); + +export default theme; diff --git a/src/theme/palette.js b/src/theme/palette.js new file mode 100644 index 0000000..d4d03a5 --- /dev/null +++ b/src/theme/palette.js @@ -0,0 +1,102 @@ +// ==============================|| DOORMILE THEME - PALETTE ||============================== // +// Corporate red brand palette. Brand red #C01227. + +export const grey = { + 0: '#FFFFFF', + 50: '#FAFAFA', + 100: '#F5F5F5', + 200: '#F0F0F0', + 300: '#D9D9D9', + 400: '#BFBFBF', + 500: '#8C8C8C', + 600: '#595959', + 700: '#434343', + 800: '#262626', + 900: '#141414', + A50: '#FAFAFB', + A100: '#E6EBF1' +}; + +const palette = { + mode: 'light', + common: { black: '#000000', white: '#FFFFFF' }, + primary: { + lighter: '#F8E0E3', + 100: '#EFBBC1', + 200: '#E08A92', + light: '#D6515C', + 400: '#CC2E3C', + main: '#C01227', + dark: '#9E0E20', + 700: '#870C1B', + darker: '#7E0B17', + 900: '#520710', + contrastText: '#FFFFFF' + }, + secondary: { + lighter: grey[100], + 100: grey[100], + 200: grey[200], + light: grey[300], + 400: grey[400], + main: grey[500], + 600: grey[600], + dark: grey[700], + 800: grey[800], + darker: grey[900], + A100: grey[0], + A200: grey[400], + A300: grey[700], + contrastText: grey[0] + }, + error: { + lighter: '#FEEAE9', + light: '#F88078', + main: '#F04134', + dark: '#A82216', + darker: '#7A150C', + contrastText: '#FFFFFF' + }, + warning: { + lighter: '#FFF7E0', + light: '#FFD666', + main: '#FFBF00', + dark: '#B38600', + darker: '#805F00', + contrastText: '#262626' + }, + info: { + lighter: '#E0F7F8', + light: '#66CBD2', + main: '#00A2AE', + dark: '#00727B', + darker: '#005159', + contrastText: '#FFFFFF' + }, + success: { + lighter: '#E3F6EC', + light: '#5CC98C', + main: '#00A854', + dark: '#00773B', + darker: '#00552A', + contrastText: '#FFFFFF' + }, + grey, + text: { + primary: grey[800], + secondary: grey[600], + disabled: grey[400] + }, + action: { + disabled: grey[300], + hover: 'rgba(192, 18, 39, 0.04)', + selected: 'rgba(192, 18, 39, 0.08)' + }, + divider: grey[200], + background: { + paper: '#FFFFFF', + default: grey.A50 + } +}; + +export default palette; diff --git a/src/theme/shadows.js b/src/theme/shadows.js new file mode 100644 index 0000000..08abe00 --- /dev/null +++ b/src/theme/shadows.js @@ -0,0 +1,14 @@ +// ==============================|| DOORMILE THEME - CUSTOM SHADOWS ||============================== // +// Soft, subtle corporate elevation + a branded red glow for primary CTAs. + +const customShadows = { + card: '0px 1px 4px rgba(0, 0, 0, 0.08)', + cardHover: '0px 4px 16px rgba(0, 0, 0, 0.10)', + widget: '0px 2px 14px rgba(38, 38, 38, 0.06)', + dropdown: '0px 8px 24px rgba(38, 38, 38, 0.12)', + primaryGlow: '0px 6px 16px rgba(192, 18, 39, 0.28)', + primaryGlowHover: '0px 8px 20px rgba(192, 18, 39, 0.36)', + header: '0px 1px 0px rgba(0, 0, 0, 0.06)' +}; + +export default customShadows; diff --git a/src/theme/typography.js b/src/theme/typography.js new file mode 100644 index 0000000..89b32d7 --- /dev/null +++ b/src/theme/typography.js @@ -0,0 +1,25 @@ +// ==============================|| DOORMILE THEME - TYPOGRAPHY ||============================== // + +const typography = { + fontFamily: '"Public Sans", "Inter", "Helvetica", "Arial", sans-serif', + htmlFontSize: 16, + fontWeightLight: 300, + fontWeightRegular: 400, + fontWeightMedium: 500, + fontWeightBold: 600, + h1: { fontWeight: 700, fontSize: '2.375rem', lineHeight: 1.21 }, + h2: { fontWeight: 700, fontSize: '1.875rem', lineHeight: 1.27 }, + h3: { fontWeight: 600, fontSize: '1.5rem', lineHeight: 1.33 }, + h4: { fontWeight: 600, fontSize: '1.25rem', lineHeight: 1.4 }, + h5: { fontWeight: 600, fontSize: '1rem', lineHeight: 1.5 }, + h6: { fontWeight: 500, fontSize: '0.875rem', lineHeight: 1.57 }, + caption: { fontWeight: 400, fontSize: '0.75rem', lineHeight: 1.66 }, + body1: { fontSize: '0.875rem', lineHeight: 1.57 }, + body2: { fontSize: '0.75rem', lineHeight: 1.66 }, + subtitle1: { fontSize: '0.875rem', fontWeight: 600, lineHeight: 1.57 }, + subtitle2: { fontSize: '0.75rem', fontWeight: 500, lineHeight: 1.66 }, + overline: { fontSize: '0.6875rem', fontWeight: 600, letterSpacing: '0.08em', textTransform: 'uppercase' }, + button: { textTransform: 'capitalize', fontWeight: 600 } +}; + +export default typography; diff --git a/src/utils/format.js b/src/utils/format.js new file mode 100644 index 0000000..e5e76cb --- /dev/null +++ b/src/utils/format.js @@ -0,0 +1,20 @@ +// ==============================|| FORMAT HELPERS ||============================== // + +export const inr = (n) => + '₹' + Number(n || 0).toLocaleString('en-IN', { minimumFractionDigits: 0, maximumFractionDigits: 2 }); + +export const stringToColor = (string = '') => { + let hash = 0; + for (let i = 0; i < string.length; i += 1) hash = string.charCodeAt(i) + ((hash << 5) - hash); + const palette = ['#C01227', '#00A854', '#00A2AE', '#FFBF00', '#662582', '#1565C0', '#EF6C00', '#5C6BC0']; + return palette[Math.abs(hash) % palette.length]; +}; + +export const initials = (name = '') => + name + .split(' ') + .filter(Boolean) + .slice(0, 2) + .map((w) => w[0]) + .join('') + .toUpperCase(); diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..26d9664 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,17 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, 'src') + } + }, + server: { + port: 3000, + open: true + } +});