diff --git a/.claude/skills/nearlexpress-docs/SKILL.md b/.claude/skills/nearlexpress-docs/SKILL.md index d1abb00..a84d00c 100644 --- a/.claude/skills/nearlexpress-docs/SKILL.md +++ b/.claude/skills/nearlexpress-docs/SKILL.md @@ -15,11 +15,11 @@ This skill is the **knowledge** Claude needs about the system (what each page do ## 1. Architecture at a glance -- **Brand colour: `#662582` (NearlExpress purple)** — defined in `src/themes/theme/default.js` as `primary.main`. Used by the sidebar, page headers, dialog/popup headers, KPI primary tiles, search bars, edit-action buttons. Gradient pair `#662582 → #9255AB`. Status badges use a separate semantic palette (amber/indigo/cyan/teal/emerald/red/orange) — see root `CLAUDE.md` §6. +- **Brand colour: `#C01227` (NearlExpress red)** — defined in `src/themes/theme/default.js` as `primary.main`. Used by the sidebar, page headers, dialog/popup headers, KPI primary tiles, search bars, edit-action buttons. Gradient pair `#C01227 → #D25463`. Status badges use a separate semantic palette (amber/indigo/cyan/teal/emerald/red/orange) — see root `CLAUDE.md` §6. - **Stack:** React 18.2 + react-app-rewired (CRA), MUI 5, TanStack Query 5, Redux Toolkit, react-router 6, axios, dayjs, leaflet, firebase 10 (FCM), notistack, formik+yup, react-dnd. - **Entry:** `src/index.js` → providers (Redux store, QueryClient, Router, ThemeCustomization, Notistack) → `src/App.js`. - **Auth gate:** `App.js` checks `localStorage.getItem('authname')` — empty redirects to `/login`. Login posts to `jupiter.nearle.app/users/console/login` with `configid: 9` and the device's FCM token. -- **Routing:** `src/routes/MainRoutes.js` declares all `/nearle/*` routes, lazy-loaded via `components/Loadable`. Sidebar items are declared in `src/menu-items/nearle.js`. +- **Routing:** `src/routes/MainRoutes.js` declares all `/doormile/*` routes, lazy-loaded via `components/Loadable`. Sidebar items are declared in `src/menu-items/nearle.js`. - **Data layer:** every server call lives in `src/pages/api/api.js`. Pages call those exports via `useQuery` / `useInfiniteQuery` / `useMutation`. Query keys MUST include every filter parameter so caching invalidates correctly. - **State:** Redux Toolkit slices (`fcmSlice`, `loginUserSlice`, `menu`, `snackbar`, `toastSlice`, `auth`) for cross-page state; per-page UI state stays in `useState`. - **Two API bases:** `process.env.REACT_APP_URL` (primary) and `process.env.REACT_APP_URL2` (used for `/users/update`, `/tenants/update`, archival `/orders/getorders`, rider logs). @@ -70,16 +70,16 @@ flowchart TD Sidebar --> Invoice Sidebar --> ReportsHub - Dispatch[/"/nearle/dispatch
Live Map · Riders · Batches"/]:::core - Orders[/"/nearle/orders
Orders Dashboard"/]:::core - Deliveries[/"/nearle/deliveries
Dispatched Deliveries"/]:::core - Tenants[/"/nearle/tenants
Client/Tenant Management"/]:::core - Pricing[/"/nearle/pricing
Pricing Matrix (master-detail)"/]:::core - Customers[/"/nearle/customers
Customer Directory"/]:::core - Riders[/"/nearle/riders
Rider Pool"/]:::core - Invoice[/"/nearle/invoice
Billing"/]:::core - Requests[/"/nearle/requests
Expense Approvals"/]:::core - ReportsHub[/"/nearle/reports/*
BI Suite"/]:::core + Dispatch[/"/doormile/dispatch
Live Map · Riders · Batches"/]:::core + Orders[/"/doormile/orders
Orders Dashboard"/]:::core + Deliveries[/"/doormile/deliveries
Dispatched Deliveries"/]:::core + Tenants[/"/doormile/tenants
Client/Tenant Management"/]:::core + Pricing[/"/doormile/pricing
Pricing Matrix (master-detail)"/]:::core + Customers[/"/doormile/customers
Customer Directory"/]:::core + Riders[/"/doormile/riders
Rider Pool"/]:::core + Invoice[/"/doormile/invoice
Billing"/]:::core + Requests[/"/doormile/requests
Expense Approvals"/]:::core + ReportsHub[/"/doormile/reports/*
BI Suite"/]:::core OrdersCreate[/"/orders/create"/]:::sub OrdersMulti[/"/orders/createorders"/]:::sub @@ -164,12 +164,12 @@ flowchart TD sequenceDiagram autonumber participant U as Operator - participant O as /nearle/orders + participant O as /doormile/orders participant S as Solver (routes/routemate.workolik.com) - participant P as /nearle/dispatch/preview + participant P as /doormile/dispatch/preview participant R as reconcile-steps participant J as jupiter.nearle.app - participant D as /nearle/deliveries + participant D as /doormile/deliveries participant F as FCM (rider device) U->>O: Select pending orders (checkbox) @@ -327,29 +327,29 @@ sequenceDiagram | Route | File | Lazy import name | | --- | --- | --- | | `/login` | `pages/nearle/login1.js` | `Login` | -| `/nearle/dispatch` | `pages/nearle/dispatch/Dispatch.js` | `Dispatch` | -| `/nearle/dispatch/preview` | `pages/nearle/dispatch/Preview.js` | `DispatchPreview` | -| `/nearle/orders` | `pages/nearle/orders/orders.js` | `Orders` | -| `/nearle/orders/preview` | `pages/nearle/orders/OrdersPreview.js` | `OrdersPreview` | -| `/nearle/orders/create` | `pages/nearle/orders/createorder1.js` | `Createorder1` | -| `/nearle/orders/createorders` | `pages/nearle/orders/multipleOrders.js` | `MultipleOrders` | -| `/nearle/orders/details` | `pages/nearle/orders/details.js` | `Details` | -| `/nearle/deliveries` | `pages/nearle/deliveries/deliveries.js` | `Deliveries` | -| `/nearle/tenants` | `pages/nearle/clients/Tenants.js` | `Tenants` | -| `/nearle/clients/create` | `pages/nearle/clients/createclient.js` | `Createclient` | -| `/nearle/pricing` | `pages/nearle/clientPricing/clientPricing.js` | `ClientsPricing` | -| `/nearle/customers` | `pages/nearle/customers/customers.js` | `Customers` | -| `/nearle/customer/create` | `pages/nearle/clients/createCustomer.js` | `CreateCustomer` | -| `/nearle/riders` | `pages/nearle/riders/riders.js` | `Riders` | -| `/nearle/riders/create` | `pages/nearle/riders/createrider.js` | `Createrider` | -| `/nearle/riders/edit` | `pages/nearle/riders/editRider.js` | `EditRider` | -| `/nearle/invoice` | `pages/nearle/invoice/invoice.js` | `Invoice` | -| `/nearle/invoice/preview` | `pages/nearle/invoice/invoicePreview.js` | `InvoicePreview` | -| `/nearle/requests` | `pages/nearle/requests/requests.js` | `Requests` | -| `/nearle/reports/orderssummary` | `pages/nearle/reports/ordersSummary.js` | `OrdersSummary` | -| `/nearle/reports/ordersdetails` | `pages/nearle/reports/ordersDetails.js` | `OrdersDetails` | -| `/nearle/reports/riderssummary` | `pages/nearle/reports/ridersSummary.js` | `RidersSummary` | -| `/nearle/reports/riderslogs` | `pages/nearle/reports/ridersLogs.js` | `RidersLogs` | +| `/doormile/dispatch` | `pages/nearle/dispatch/Dispatch.js` | `Dispatch` | +| `/doormile/dispatch/preview` | `pages/nearle/dispatch/Preview.js` | `DispatchPreview` | +| `/doormile/orders` | `pages/nearle/orders/orders.js` | `Orders` | +| `/doormile/orders/preview` | `pages/nearle/orders/OrdersPreview.js` | `OrdersPreview` | +| `/doormile/orders/create` | `pages/nearle/orders/createorder1.js` | `Createorder1` | +| `/doormile/orders/createorders` | `pages/nearle/orders/multipleOrders.js` | `MultipleOrders` | +| `/doormile/orders/details` | `pages/nearle/orders/details.js` | `Details` | +| `/doormile/deliveries` | `pages/nearle/deliveries/deliveries.js` | `Deliveries` | +| `/doormile/tenants` | `pages/nearle/clients/Tenants.js` | `Tenants` | +| `/doormile/clients/create` | `pages/nearle/clients/createclient.js` | `Createclient` | +| `/doormile/pricing` | `pages/nearle/clientPricing/clientPricing.js` | `ClientsPricing` | +| `/doormile/customers` | `pages/nearle/customers/customers.js` | `Customers` | +| `/doormile/customer/create` | `pages/nearle/clients/createCustomer.js` | `CreateCustomer` | +| `/doormile/riders` | `pages/nearle/riders/riders.js` | `Riders` | +| `/doormile/riders/create` | `pages/nearle/riders/createrider.js` | `Createrider` | +| `/doormile/riders/edit` | `pages/nearle/riders/editRider.js` | `EditRider` | +| `/doormile/invoice` | `pages/nearle/invoice/invoice.js` | `Invoice` | +| `/doormile/invoice/preview` | `pages/nearle/invoice/invoicePreview.js` | `InvoicePreview` | +| `/doormile/requests` | `pages/nearle/requests/requests.js` | `Requests` | +| `/doormile/reports/orderssummary` | `pages/nearle/reports/ordersSummary.js` | `OrdersSummary` | +| `/doormile/reports/ordersdetails` | `pages/nearle/reports/ordersDetails.js` | `OrdersDetails` | +| `/doormile/reports/riderssummary` | `pages/nearle/reports/ridersSummary.js` | `RidersSummary` | +| `/doormile/reports/riderslogs` | `pages/nearle/reports/ridersLogs.js` | `RidersLogs` | | `/viewprofile` | `pages/nearle/viewProfile.js` | `ViewProfile` | | `/maintenance/{404,500,under-construction,coming-soon}` | `pages/maintenance/*` | — | diff --git a/API_ENDPOINTS.md b/API_ENDPOINTS.md new file mode 100644 index 0000000..98e9198 --- /dev/null +++ b/API_ENDPOINTS.md @@ -0,0 +1,95 @@ +# NearlExpress Console — API Endpoints Reference + +> Auto-generated reference of every API call in `src/pages/api/api.js`. +> Base URLs are resolved from the env files at the repo root. + +## Base URLs + +| Env variable | Resolved value | +|---|---| +| `REACT_APP_URL` | `https://jupiter.nearle.app/live/api/v1` | +| `REACT_APP_URL2` | `https://jupiter.nearle.app/live/api/v2` | +| Bike solver (hardcoded) | `https://routes.workolik.com` | +| Auto solver (hardcoded) | `https://routemate.workolik.com` | +| Commit service (hardcoded) | `https://jupiter.nearle.app` | + +> **Note:** In `env.staging`, `REACT_APP_URL2` is empty — URL2 endpoints resolve to relative paths in that build. + +--- + +## Base `REACT_APP_URL` → `https://jupiter.nearle.app/live/api/v1` + +| # | Function | Method | Full endpoint | +|---|---|---|---| +| 1 | `getRiderPeriodicLogs` | GET | `https://jupiter.nearle.app/live/api/v1/utils/getriderperiodiclogs?userid={userid}` | +| 2 | `fetchAppLocations` | GET | `https://jupiter.nearle.app/live/api/v1/partners/getlocations/?userid={userid}` | +| 3 | `fetchPercentageData` | GET | `https://jupiter.nearle.app/live/api/v1/orders/getordersummary/?applocationid={appId}&tenantid={tenantid}&locationid={locationid}&fromdate={startdate}&todate={enddate}` | +| 4 | `getTenants` | GET | `https://jupiter.nearle.app/live/api/v1/tenants/gettenants/?applocationid={appId}&status=active` | +| 5 | `gettenantlocations` | GET | `https://jupiter.nearle.app/live/api/v1/tenants/gettenantlocations/?tenantid={appId}` | +| 6 | `fetchorderscount` | GET | `https://jupiter.nearle.app/live/api/v1/orders/getordersummary/?applocationid={appId}&tenantid={tenantid}&locationid={locationid}&fromdate={startdate}&todate={enddate}&status={currentStatus}` | +| 7 | `fetchOrders` | GET | `https://jupiter.nearle.app/live/api/v1/orders/tenant/getorders/?applocationid={appId}&tenantid={tenantid}&locationid={locationid}&status={currentStatus}&fromdate={startdate}&todate={enddate}&keyword={search}&pageno={pageParam}&pagesize={rowsPerPage}` | +| 8 | `fetchPaymentType` | GET | `https://jupiter.nearle.app/live/api/v1/utils/getapptypes/?tag=paymentmode` | +| 9 | `fetchRidersList` | GET | `https://jupiter.nearle.app/live/api/v1/partners/getriders/?applocationid={appId}` | +| 10 | `notifyRider` | POST | `https://jupiter.nearle.app/live/api/v1/utils/notifyuser` | +| 11 | `cancelOrder` | PUT | `https://jupiter.nearle.app/live/api/v1/orders/updateorder` | +| 12 | `cancelMultipleOrder` | PUT | `https://jupiter.nearle.app/live/api/v1/orders/updatemultipleorders` | +| 13 | `fetchDeliveries` (all zones) | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/getdeliveries/?appuserid={userid}&status={status}&fromdate={startdate}&todate={enddate}&pageno={pageParam}&pagesize={rowsPerPage}&keyword={search}&tenantid={tenantid}&locationid={locationid}&userid={riderid}` | +| 14 | `fetchDeliveries` (by zone) | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/getdeliveries/?applocationid={appId}&status={status}&fromdate={startdate}&todate={enddate}&pageno={pageParam}&pagesize={rowsPerPage}&keyword={search}&tenantid={tenantid}&locationid={locationid}&userid={riderid}` | +| 15 | `fetchPercentageAPI` | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/deliverysummary/?applocationid={appId}` | +| 16 | `fetchCountAPI` (all zones) | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/deliverysummary/?appuserid={userid}&fromdate={startdate}&todate={enddate}` | +| 17 | `fetchCountAPI` (by zone) | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/deliverysummary/?applocationid={appId}&fromdate={startdate}&todate={enddate}&tenantid={tenantid}&locationid={locationid}&userid={riderid}` | +| 18 | `cancelDeliveryAPI` | PUT | `https://jupiter.nearle.app/live/api/v1/deliveries/updatedelivery` | +| 19 | `getorderdetails` | GET | `https://jupiter.nearle.app/live/api/v1/orders/getorderdetails?orderheaderid={orderHeaderid}` | +| 20 | `changeRiderAPI` | PUT | `https://jupiter.nearle.app/live/api/v1/deliveries/updatedelivery` | +| 21 | `updateDeliveryAPI` | PUT | `https://jupiter.nearle.app/live/api/v1/deliveries/updatedelivery` | +| 22 | `fetchAllRiders` | GET | `https://jupiter.nearle.app/live/api/v1/partners/getallriders/?applocationid={appId}&pageno={pageParam}&pagesize=20&keyword={search}&status={''|Active}` | +| 23 | `getallridersummary` | GET | `https://jupiter.nearle.app/live/api/v1/partners/getallridersummary/?applocationid={appId}&status={''|Active}` | +| 24 | `fetchRiders` | GET | `https://jupiter.nearle.app/live/api/v1/partners/getriders/?applocationid={appId}&pageno={pageParam}&pagesize=20&keyword={search}` | +| 25 | `getriderstatus` | GET | `https://jupiter.nearle.app/live/api/v1/utils/getriderstatus` | +| 26 | `getreportsummary` | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/getreportsummary/?applocationid={appId}&tenantid={tenantid}&locationid={locationid}&fromdate={startdate}&todate={enddate}` | +| 27 | `getreportlocationsummary` | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/getreportlocationsummary/?applocationid={appId}&tenantid={tenantid}&locationid={locationid}&fromdate={startdate}&todate={enddate}` | +| 28 | `getriderbydelivery` | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/getriderbydelivery/?applocationid={appId}&tenantid={tenantid}&locationid={locationid}&fromdate={startdate}&todate={enddate}` | +| 29 | `fetchCount` (all zones) | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/deliverysummary/?fromdate={startdate}&todate={enddate}` | +| 30 | `fetchCount` (by zone) | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/deliverysummary/?applocationid={appId}&fromdate={startdate}&todate={enddate}` | +| 31 | `fetchRidersSummary` | GET | `https://jupiter.nearle.app/live/api/v1/deliveries/getridersummary/?applocationid={appId}&fromdate={startdate}&todate={enddate}` | +| 32 | `fetchLocations` | GET | `https://jupiter.nearle.app/live/api/v1/partners/getpartners` | +| 33 | `fetchOrders1` | GET | `https://jupiter.nearle.app/live/api/v1/orders/tenant/getorders/?tenantid={tenantid}&locationid={locationid}&status={status}&fromdate={startdate}&todate={enddate}&pageno={pageParam}&pagesize={rowsPerPage}&keyword={search}` | +| 34 | `getusers` | GET | `https://jupiter.nearle.app/live/api/v1/users/getusers/?configid=9&userid={userid}` | +| 35 | `getallriders` | GET | `https://jupiter.nearle.app/live/api/v1/partners/getallriders?partnerid=64` | + +--- + +## Base `REACT_APP_URL2` → `https://jupiter.nearle.app/live/api/v2` + +| # | Function | Method | Full endpoint | +|---|---|---|---| +| 36 | `fetchorderdetails` (all zones) | GET | `https://jupiter.nearle.app/live/api/v2/orders/getorders/?appuserid={userid}&fromdate={startdate}&todate={enddate}&pageno={page+1}&pagesize={rowsPerPage}` | +| 37 | `fetchorderdetails` (by zone) | GET | `https://jupiter.nearle.app/live/api/v2/orders/getorders/?fromdate={startdate}&todate={enddate}&applocationid={appId}&pageno={page}&pagesize={rowsPerPage}` | +| 38 | `fetchRidersLogs` | GET | `https://jupiter.nearle.app/live/api/v2/partners/getriderlogs/?applocationid={appId}&fromdate={startdate}&todate={startdate}&keyword={search}` | + +--- + +## Hardcoded — Bike solver `https://routes.workolik.com` + +| # | Function | Method | Full endpoint | +|---|---|---|---| +| 39 | `createOptimisationDeliveries` | POST | `https://routes.workolik.com/api/v1/optimization/createdeliveries` | +| 40 | `reconcileSteps` | POST | `https://routes.workolik.com/api/v1/optimization/reconcile-steps` | +| 41 | `fetchBatchEfficiency` | POST | `https://routes.workolik.com/api/v1/batch/efficiency` | +| 42 | `createAutomationDeliveries` (bike mode) | POST | `https://routes.workolik.com/api/v1/optimization/riderassign?hypertuning_params={params}` | + +--- + +## Hardcoded — Auto solver `https://routemate.workolik.com` + +| # | Function | Method | Full endpoint | +|---|---|---|---| +| 43 | `createAutomationDeliveries` (auto mode) | POST | `https://routemate.workolik.com/api/v1/optimization/riderassign?strategy=multi_trip` | + +--- + +## Hardcoded — Commit service `https://jupiter.nearle.app` + +| # | Function | Method | Full endpoint | +|---|---|---|---| +| 44 | `finalCreatedeliveries` | POST | `https://jupiter.nearle.app/live/api/v1/deliveries/createdeliveries` | diff --git a/CLAUDE.md b/CLAUDE.md index 021bcad..ef3274d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# CLAUDE.md — NearlExpress Console (xpressconsole) +# CLAUDE.md — Doormile Express Console (xpressconsole) > Project-level rules and conventions for Claude Code when working in this repo. > **Read this in full before editing.** When in doubt about a pattern, copy from `src/pages/nearle/deliveries/deliveries.js` — it is the canonical reference for both the design system and the data layer. @@ -7,7 +7,7 @@ ## 1. What this is -A React 18 operator console for the NearlExpress dispatch platform. Operators use it to manage orders, run the AI dispatch optimiser, watch a live map of riders, edit tenants/pricing/invoices, and pull BI reports. Production users are warehouse staff, not end customers. +A React 18 operator console for the Doormile Express dispatch platform. Operators use it to manage orders, run the AI dispatch optimiser, watch a live map of riders, edit tenants/pricing/invoices, and pull BI reports. Production users are warehouse staff, not end customers. For the per-page API map and architectural flow chart, see the project skill **`nearlexpress-docs`** (`.claude/skills/nearlexpress-docs/SKILL.md`). Do not duplicate that content here. @@ -63,7 +63,7 @@ npm run lint 1. **Do not introduce Next.js / SSR patterns.** No `getServerSideProps`, no `app/` directory, no `next/*` imports. This is CRA. 2. **Do not rewrite shared design tokens.** Every new page that needs the polished UI must reuse the `DT` token block (see §6). Do not invent a parallel palette. 3. **Do not change `package.json` dependency versions** unless explicitly asked. The build is sensitive to webpack/svgr/react-scripts versions (see `resolutions` in `package.json`). -4. **Do not bypass the dispatch reconcile step.** After any manual edit on `/nearle/dispatch/preview` (rider swap, step reorder), the page **must** call `POST /optimization/reconcile-steps` before `POST /deliveries/createdeliveries`. Skipping this corrupts route sequences. +4. **Do not bypass the dispatch reconcile step.** After any manual edit on `/doormile/dispatch/preview` (rider swap, step reorder), the page **must** call `POST /optimization/reconcile-steps` before `POST /deliveries/createdeliveries`. Skipping this corrupts route sequences. 5. **Do not commit `.env*` files** beyond `env.staging` (which is the agreed-shared staging baseline). 6. **Do not introduce TypeScript files** (`.ts` / `.tsx`) into this repo. It is JavaScript; mixing creates lint and tooling friction. 7. **Do not use absolute `http://localhost` URLs** in code. Always read from `process.env.REACT_APP_URL` / `REACT_APP_URL2`. @@ -82,7 +82,7 @@ src/ ├── config.js # Theme constants (DRAWER_WIDTH=260, fontFamily, mode, presetColor, ThemeMode, MenuOrientation, ThemeDirection) ├── routes/ │ ├── index.js # Combines MainRoutes + LoginRoutes -│ ├── MainRoutes.js # All /nearle/* routes, lazy-loaded via Loadable(lazy(...)) +│ ├── MainRoutes.js # All /doormile/* routes, lazy-loaded via Loadable(lazy(...)) │ └── LoginRoutes.js ├── layout/ │ ├── MainLayout/ # Sidebar + header frame (used for all logged-in pages) @@ -117,7 +117,7 @@ src/ ## 6. Design system (the `DT` token block) -The polished pages (`deliveries.js`, `clients/Tenants.js`, `clientPricing/clientPricing.js`) share a token block at the top of the file. Every new operator page must paste and reuse this block — do not invent fresh colours, spacings, or radius numbers. +The polished pages (`deliveries.js`) share a token block at the top of the file. Every new operator page must paste and reuse this block — do not invent fresh colours, spacings, or radius numbers. ```js const DT = { @@ -174,43 +174,43 @@ const AccentAvatar = ({ color, selected, size = 24, children }) => ( ### Page anatomy (every operator page should follow this order) -> **Use brand purple `#662582` (with light variant `#9255AB`) for every brand surface below.** Don't use indigo `#6366f1` — that's reserved for the "Accepted" status badge only. +> **Use brand red `#C01227` (with light variant `#D25463`) for every brand surface below.** Don't use indigo `#6366f1` — that's reserved for the "Accepted" status badge only. -1. **Gradient header ``** — `linear-gradient(135deg, ${tint('#662582')} 0%, ${tint('#9255AB')} 100%)`, 48px filled `#662582` avatar with a page icon, `Typography variant="h3"` title, "Live · {zone}" sub-line with an 8px green pulsing dot, and a pill `LocationAutocomplete` on the right (`pill accentColor="#662582" paperComponent={SoftPaper}`). -2. **KPI tiles row** — `Grid` of 3–4 `Paper` cards, each with a 3px top stripe gradient, uppercase eyebrow label, large bold number, and a soft-tinted avatar holding an icon. The "primary" tile uses brand purple `#662582`; other tiles use semantic status colours. +1. **Gradient header ``** — `linear-gradient(135deg, ${tint('#C01227')} 0%, ${tint('#D25463')} 100%)`, 48px filled `#C01227` avatar with a page icon, `Typography variant="h3"` title, "Live · {zone}" sub-line with an 8px green pulsing dot, and a pill `LocationAutocomplete` on the right (`pill accentColor="#C01227" paperComponent={SoftPaper}`). +2. **KPI tiles row** — `Grid` of 3–4 `Paper` cards, each with a 3px top stripe gradient, uppercase eyebrow label, large bold number, and a soft-tinted avatar holding an icon. The "primary" tile uses brand red `#C01227`; other tiles use semantic status colours. 3. **Filter bar ``** (optional) — pill-style `Autocomplete`s using `pillFieldSx(color)` + `SoftPaper`, each with an `AccentAvatar` start-adornment. -4. **Pill status tabs + pill search** — tabs as clickable `` pills (active = filled accent + glow ring, inactive = `tint` bg + `edge` border), each with an avatar icon and a count badge. Tab accents use the **semantic status colour** for each tab (pending → amber, delivered → emerald, etc.). Search via `DebounceSearchBar` styled with brand purple `#662582` (tint bg, edge border, focus ring). -5. **Table ``** with `` + sticky `` — uppercase muted headers on `DT.surfaceAlt`, rows with `borderBottom: 1px solid ${DT.divider}` and `:hover` row tint. Scrollbar thumb uses brand purple `edge('#662582')`. +4. **Pill status tabs + pill search** — tabs as clickable `` pills (active = filled accent + glow ring, inactive = `tint` bg + `edge` border), each with an avatar icon and a count badge. Tab accents use the **semantic status colour** for each tab (pending → amber, delivered → emerald, etc.). Search via `DebounceSearchBar` styled with brand red `#C01227` (tint bg, edge border, focus ring). +5. **Table ``** with `` + sticky `` — uppercase muted headers on `DT.surfaceAlt`, rows with `borderBottom: 1px solid ${DT.divider}` and `:hover` row tint. Scrollbar thumb uses brand red `edge('#C01227')`. 6. **Status badges in cells** — `Stack` with `AccentAvatar` + label inside a soft pill (tint bg, edge border). Status colours come from a per-page `STATUS_META` map keyed by lowercase status string — these are **semantic**, not brand. -7. **Edit / action icon buttons** — soft-pill `IconButton` using brand purple `#662582` (NOT `#8b5cf6` — that overlaps with the "Picked" status badge and confuses operators). +7. **Edit / action icon buttons** — soft-pill `IconButton` using brand red `#C01227` (NOT `#8b5cf6` — that overlaps with the "Picked" status badge and confuses operators). 8. **Empty state** — centered 64px avatar (soft grey), bold "No X to show" line, and a muted helper sentence. Do not use antd `` for new code. ### Universal brand colour -**`#662582` — NearlExpress brand purple.** This is the canonical primary colour for this app, defined in `src/themes/theme/default.js` as `primary.main`. It drives the sidebar, the logo, and is what every new surface (page headers, KPI primary tile, search bars, edit-action buttons, dialog/popup headers, scrollbars) must use as the brand accent. +**`#C01227` — Doormile Express brand red.** This is the canonical primary colour for this app, defined in `src/themes/theme/default.js` as `primary.main`. It drives the sidebar, the logo, and is what every new surface (page headers, KPI primary tile, search bars, edit-action buttons, dialog/popup headers, scrollbars) must use as the brand accent. Variants (also from `theme/default.js`): | Token | Hex | Use | |---|---|---| -| `primary.lighter` | `#E8D9EF` | Very subtle wash bg | -| `primary.light` / `primary.400` | `#9255AB` | Gradient pair with main | -| `primary.main` | `#662582` | Brand primary — default for all brand surfaces | -| `primary.dark` | `#4D1C61` | Hover / pressed states | -| `primary.darker` | `#260E30` | Deep contrast text on light bg | +| `primary.lighter` | `#F5DBDE` | Very subtle wash bg | +| `primary.light` / `primary.400` | `#D25463` | Gradient pair with main | +| `primary.main` | `#C01227` | Brand primary — default for all brand surfaces | +| `primary.dark` | `#910E1D` | Hover / pressed states | +| `primary.darker` | `#48070F` | Deep contrast text on light bg | -**Page header / dialog header gradient:** `linear-gradient(135deg, ${tint('#662582')} 0%, ${tint('#9255AB')} 100%)` (subtle wash) or `linear-gradient(135deg, #662582 0%, #9255AB 100%)` (solid, for dialog titles). +**Page header / dialog header gradient:** `linear-gradient(135deg, ${tint('#C01227')} 0%, ${tint('#D25463')} 100%)` (subtle wash) or `linear-gradient(135deg, #C01227 0%, #D25463 100%)` (solid, for dialog titles). -> **Migration note:** `deliveries.js`, `Tenants.js`, and `clientPricing.js` currently use `#6366f1` (indigo) as their brand accent — a holdover from the first design pass before brand purple was canonicalised. They are scheduled to migrate to `#662582`. `customers.js` and the createorder1 Saved-Address dialog have already been migrated. +> **Migration note:** `deliveries.js` currently uses `#6366f1` (indigo) as its brand accent — a holdover from the first design pass before brand red was canonicalised. It is scheduled to migrate to `#C01227`. The `createorder1` Saved-Address dialog has already been migrated. ### Status palette (semantic — distinct from brand) -These colour-code lifecycle states. Do **not** swap them for brand purple — operators rely on the colour to identify status at a glance. +These colour-code lifecycle states. Do **not** swap them for brand red — operators rely on the colour to identify status at a glance. | Meaning | Colour | |--------------------------|-----------| | Pending / waiting | `#f59e0b` (amber) | -| Accepted / assigned | `#6366f1` (indigo — semantically distinct from brand purple) | +| Accepted / assigned | `#6366f1` (indigo — semantically distinct from brand red) | | Arrived | `#06b6d4` (cyan) | | Picked up | `#8b5cf6` (light purple — distinct from brand) | | Active / in-transit | `#14b8a6` (teal) | @@ -300,3 +300,34 @@ These colour-code lifecycle states. Do **not** swap them for brand purple — op - **For the per-page API map and the architectural flow chart** (which endpoint each page calls, what the optimisation pipeline does, FCM flow), invoke the project skill **`nearlexpress-docs`** (`.claude/skills/nearlexpress-docs/SKILL.md`) rather than restating the content here. - **For shared design patterns** between pages, the source of truth is the `DT` token block and helpers near the top of `src/pages/nearle/deliveries/deliveries.js` (search for `const DT = {`). Copy from there, don't redesign. + + +Astryx v0.1.9 · 153 components +CLI: run every command as `yarn dlx @astryxdesign/cli ` (shown below as `astryx ...`). + +SETUP (once, in your app entry e.g. main.tsx) — without these, components render unstyled: + import "@astryxdesign/core/reset.css"; + import "@astryxdesign/core/astryx.css"; + +WORKFLOW — discover, don't guess. Before writing UI: +1. `astryx build ""` — START HERE: returns a kit (closest [page] + [block]s + [component]s). No args = full playbook. +2. `astryx template [--skeleton]` — scaffold the [page]/[block]s it named, or study their layout. Templates are reference code. +3. `astryx component ` — props + examples for every component you use. + +RULES: +- No
— components do all layout/spacing. Full page → AppShell; sidebar nav → SideNav. +- Frame first: pick the shell (AppShell / Layout+LayoutPanel) and budget regions in px BEFORE writing content (`astryx docs layout`). +- Dense data = rows (Table, List/Item) edge-to-edge — never Card-wrapped list items. Card = dashboard widgets, galleries, settings groups only. +- Status → StatusDot/Token; Badge only for counts and enumerated states, never decoration. +- Custom styling: component props first; else style/className with tokens — var(--color-*|--spacing-*|--radius-*). No raw hex/px. (No StyleX/Tailwind compiler here — don't use xstyle/utility classes.) +- Tokens for every value (`astryx docs tokens`). Brand/accent via `astryx theme` — never override --color-* in :root. +- SELF-CHECK before you finish: re-read the file and replace any raw
/ layout, imported .css/@apply, or hardcoded value (#hex, 16px) with the component or a token (var(--color-*|--spacing-*|…)). If unsure a component/prop exists, run `astryx component ` / `astryx search ""`; don't hand-roll CSS. + +MORE CLI: + search "" find any component / hook / doc / template / block + component --list 153 components by category + template --list page + block recipes + docs color, elevation, icons, illustrations, internationalization, layout, migration, motion, principles, shape, spacing, styling, theme, tokens, typography + swizzle eject component source for deep customization + upgrade --apply run after any @astryxdesign/core bump + diff --git a/FLOW.md b/FLOW.md index b52ba66..c62e55e 100644 --- a/FLOW.md +++ b/FLOW.md @@ -56,16 +56,16 @@ flowchart TD Sidebar --> ViewProfile %% ============================ Top-level pages ============================ - Dispatch[/"/nearle/dispatch
Live Map · Riders · Batches"/]:::core - Orders[/"/nearle/orders
Orders Dashboard"/]:::core - Deliveries[/"/nearle/deliveries
Dispatched Deliveries"/]:::core - Tenants[/"/nearle/tenants
Client/Tenant Management"/]:::core - Pricing[/"/nearle/pricing
Pricing Matrix (master-detail)"/]:::core - Customers[/"/nearle/customers
Customer Directory"/]:::core - Riders[/"/nearle/riders
Rider Pool"/]:::core - Invoice[/"/nearle/invoice
Billing"/]:::core - Requests[/"/nearle/requests
Expense Approvals"/]:::core - ReportsHub[/"/nearle/reports/*
BI Suite"/]:::core + Dispatch[/"/doormile/dispatch
Live Map · Riders · Batches"/]:::core + Orders[/"/doormile/orders
Orders Dashboard"/]:::core + Deliveries[/"/doormile/deliveries
Dispatched Deliveries"/]:::core + Tenants[/"/doormile/tenants
Client/Tenant Management"/]:::core + Pricing[/"/doormile/pricing
Pricing Matrix (master-detail)"/]:::core + Customers[/"/doormile/customers
Customer Directory"/]:::core + Riders[/"/doormile/riders
Rider Pool"/]:::core + Invoice[/"/doormile/invoice
Billing"/]:::core + Requests[/"/doormile/requests
Expense Approvals"/]:::core + ReportsHub[/"/doormile/reports/*
BI Suite"/]:::core ViewProfile[/"/viewprofile
Profile Manager"/]:::sub %% ============================ Sub-routes ============================ @@ -185,12 +185,12 @@ flowchart TD sequenceDiagram autonumber participant U as Operator - participant O as /nearle/orders + participant O as /doormile/orders participant S as Solver (routes/routemate.workolik.com) - participant P as /nearle/dispatch/preview + participant P as /doormile/dispatch/preview participant R as reconcile-steps participant J as jupiter.nearle.app - participant D as /nearle/deliveries + participant D as /doormile/deliveries participant F as FCM (rider device) U->>O: Select pending orders (checkbox) diff --git a/README.md b/README.md index 9d5a350..2518ead 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# NearleXpress - Operator Dispatch Console & Deliveries Portal +# Doormile Express - Operator Dispatch Console & Deliveries Portal A high-fidelity, real-time dispatcher command center and order-delivery console built using React, React-Leaflet, TanStack Query, and Material UI. diff --git a/config-overrides.js b/config-overrides.js index 3d669dd..0eb3c32 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -1,7 +1,29 @@ const webpack = require('webpack'); const WorkBoxPlugin = require('workbox-webpack-plugin'); +// NOTE on Astryx + StyleX custom styling (xstyle / stylex.create()): +// @stylexjs/babel-plugin alone only transforms the JS call sites — it does +// NOT inject the generated CSS. That requires a matching bundler plugin +// (@stylexjs/webpack-plugin), which is stuck at 0.11.1 and pins its own +// internal @stylexjs/babel-plugin@0.11.1 — two majors behind the 0.19.x +// core that this Astryx version requires, so the two would hash class +// names differently and produce the exact same "styled but invisible" +// bug either way. Until StyleX's webpack tooling catches up (or we write +// a custom metadata-collecting loader), custom Astryx styling is limited +// to component props; anything a prop can't express uses a plain native +// element with an inline `style` (StyleX-free, so it isn't affected). +// See themes/astryx.js and pages/nearle/login.js. + module.exports = function override(config) { + // @astryxdesign/core ships as ESM with relative imports missing extensions + // (e.g. '../Tooltip/Tooltip'), which webpack 5 rejects under strict ESM + // resolution. Relax it for this package only rather than globally. + config.module.rules.push({ + test: /\.m?js$/, + include: /node_modules[\\/]@astryxdesign/, + resolve: { fullySpecified: false } + }); + config.resolve.fallback = { process: require.resolve('process/browser'), // zlib: require.resolve('browserify-zlib'), diff --git a/package-lock.json b/package-lock.json index d74d137..390dd97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,18 +10,21 @@ "dependencies": { "@ant-design/colors": "^7.0.0", "@ant-design/icons": "^5.0.1", + "@astryxdesign/core": "^0.1.9", + "@astryxdesign/theme-neutral": "^0.1.9", "@emotion/cache": "^11.10.7", "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", - "@mui/base": "^5.0.0-alpha.126", + "@mui/base": "^5.0.0-beta.70", "@mui/icons-material": "^5.14.19", "@mui/lab": "^5.0.0-alpha.127", "@mui/material": "^5.12.1", - "@mui/x-date-pickers": "^6.18.2", + "@mui/x-date-pickers": "^7.29.4", "@react-google-maps/api": "^2.20.7", - "@reduxjs/toolkit": "^1.9.5", + "@reduxjs/toolkit": "^2.12.0", + "@stylexjs/stylex": "^0.19.0", "@svgr/webpack": "^7.0.0", - "@tanstack/react-query": "^5.17.9", + "@tanstack/react-query": "^5.101.4", "antd": "^5.11.5", "autosuggest-highlight": "^3.3.4", "axios": "^1.3.5", @@ -33,37 +36,35 @@ "env-cmd": "^10.1.0", "firebase": "^10.14.1", "formik": "^2.2.9", - "framer-motion": "^10.12.4", + "framer-motion": "^12.42.2", "geolib": "^3.3.4", "jsonwebtoken": "^9.0.0", "jwt-decode": "^3.1.2", "leaflet": "^1.9.4", "lodash": "^4.17.21", - "mui-daterange-picker": "^1.0.5", "notistack": "^3.0.1", "papaparse": "^5.4.1", "process": "^0.11.10", "prop-types": "^15.8.1", - "react": "^18.2.0", + "react": "^19.2.8", "react-csv": "^2.2.2", "react-device-detect": "^2.2.3", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", - "react-dom": "^18.2.0", + "react-dom": "^19.2.8", "react-geocode": "^0.2.3", "react-google-autocomplete": "^2.7.3", "react-icons": "^4.12.0", - "react-intl": "^6.4.1", - "react-leaflet": "^4.2.1", + "react-intl": "^7.1.14", + "react-leaflet": "^5.0.0", "react-loading-icons": "^1.1.0", - "react-redux": "^8.0.5", + "react-redux": "^9.2.0", "react-router": "^6.10.0", "react-router-dom": "^6.10.0", "react-scripts": "^5.0.1", "react-timer-hook": "^3.0.5", - "react-to-print": "^2.15.0", - "react18-input-otp": "^1.1.3", - "redux": "^4.2.1", + "react-to-print": "^3.3.0", + "redux": "^5.0.1", "simplebar": "^6.2.5", "simplebar-react": "^3.2.4", "stream-browserify": "^3.0.0", @@ -105,19 +106,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@ant-design/colors": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.0.tgz", @@ -227,13 +215,41 @@ "react": ">=16.9.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "node_modules/@astryxdesign/core": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@astryxdesign/core/-/core-0.1.9.tgz", + "integrity": "sha512-2ZNypZFfujMPxdU8M9Jbe77vwFPP1FYvA0Pk/3L3OLxEIzi98qLbRJjhDEHLbrflANSFVB4l5+qYpJjYtFFXMQ==", + "hasInstallScript": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "intl-messageformat": "^11.2.9" + }, + "peerDependencies": { + "@stylexjs/stylex": "^0.19.0", + "react": ">=19.0.0", + "react-dom": ">=19.0.0" + } + }, + "node_modules/@astryxdesign/theme-neutral": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@astryxdesign/theme-neutral/-/theme-neutral-0.1.9.tgz", + "integrity": "sha512-HZPJmzg0S+aKuxUBGJ1L9O+bBZzwzWlROey/7VilM8kUzHA4lYrFcIPzRixoiXs/qyR5sSfJE8YG7jsXuAuywA==", + "license": "MIT", + "dependencies": { + "lucide-react": "^1.18.0" + }, + "peerDependencies": { + "@astryxdesign/core": "0.1.9", + "react": ">=19" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "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" }, @@ -242,35 +258,35 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", - "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-compilation-targets": "^7.21.4", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.4", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.4", - "@babel/types": "^7.21.4", - "convert-source-map": "^1.7.0", + "@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.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -280,6 +296,12 @@ "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==", + "license": "MIT" + }, "node_modules/@babel/eslint-parser": { "version": "7.21.3", "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.21.3.tgz", @@ -299,13 +321,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "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.28.0", - "@babel/types": "^7.28.0", + "@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" @@ -314,16 +336,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", @@ -337,13 +349,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "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==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", + "@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" @@ -408,9 +420,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "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" @@ -430,27 +442,27 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "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.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -472,9 +484,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "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==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -540,27 +552,27 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "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.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "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.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "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==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -581,26 +593,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "license": "MIT", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "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.28.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -1066,12 +1077,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2252,31 +2263,31 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "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.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@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.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "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.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@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": { @@ -2284,13 +2295,13 @@ } }, "node_modules/@babel/types": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", - "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", + "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.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -3462,62 +3473,61 @@ "license": "MIT" }, "node_modules/@formatjs/ecma402-abstract": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz", - "integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", + "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==", "license": "MIT", "dependencies": { - "@formatjs/fast-memoize": "2.2.3", - "@formatjs/intl-localematcher": "0.5.8", - "tslib": "2" + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.2", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" } }, "node_modules/@formatjs/fast-memoize": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.3.tgz", - "integrity": "sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==", - "license": "MIT", - "dependencies": { - "tslib": "2" - } + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.7.tgz", + "integrity": "sha512-zXfhLpvA6T7+efdt9JLbBwZ00tT7NsBMDVnDu8rpHeNNv8KfRZAMo2gkG0k9lK/Nzc//3kJ9pImsfuJxk3KhUA==", + "license": "MIT" }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.4.tgz", - "integrity": "sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==", + "version": "3.5.15", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.15.tgz", + "integrity": "sha512-5o4grXKotAB3JqQuisLApHG43g17N+paoRTa92Jiz35Zvfemq0cVf4EDvuxyHAzmsJji7igaEowicLO/VmfJ8Q==", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "2.2.4", - "@formatjs/icu-skeleton-parser": "1.8.8", - "tslib": "2" + "@formatjs/icu-skeleton-parser": "2.1.11" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.8.tgz", - "integrity": "sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.2.4", - "tslib": "2" - } + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.11.tgz", + "integrity": "sha512-j8cUmOJzVgkHuS0QiQ6ga76UIoLOFSAMWhs7aZJztH3aAdCOAE6vpC8KVvFB4cU10ON0y2/5oOVmPJ43s2lTwA==", + "license": "MIT" }, "node_modules/@formatjs/intl": { - "version": "2.10.15", - "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.15.tgz", - "integrity": "sha512-i6+xVqT+6KCz7nBfk4ybMXmbKO36tKvbMKtgFz9KV+8idYFyFbfwKooYk8kGjyA5+T5f1kEPQM5IDLXucTAQ9g==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-3.1.8.tgz", + "integrity": "sha512-LWXgwI5zTMatvR8w8kCNh/priDTOF/ZssokMBHJ7ZWXFoYLVOYo0EJERD9Eajv+xsfQO1QkuAt77KWQ1OI4mOQ==", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "2.2.4", - "@formatjs/fast-memoize": "2.2.3", - "@formatjs/icu-messageformat-parser": "2.9.4", - "@formatjs/intl-displaynames": "6.8.5", - "@formatjs/intl-listformat": "7.7.5", - "intl-messageformat": "10.7.7", - "tslib": "2" + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.4", + "intl-messageformat": "10.7.18", + "tslib": "^2.8.0" }, "peerDependencies": { - "typescript": "^4.7 || 5" + "typescript": "^5.6.0" }, "peerDependenciesMeta": { "typescript": { @@ -3525,35 +3535,55 @@ } } }, - "node_modules/@formatjs/intl-displaynames": { - "version": "6.8.5", - "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.8.5.tgz", - "integrity": "sha512-85b+GdAKCsleS6cqVxf/Aw/uBd+20EM0wDpgaxzHo3RIR3bxF4xCJqH/Grbzx8CXurTgDDZHPdPdwJC+May41w==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.2.4", - "@formatjs/intl-localematcher": "0.5.8", - "tslib": "2" - } - }, - "node_modules/@formatjs/intl-listformat": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.7.5.tgz", - "integrity": "sha512-Wzes10SMNeYgnxYiKsda4rnHP3Q3II4XT2tZyOgnH5fWuHDtIkceuWlRQNsvrI3uiwP4hLqp2XdQTCsfkhXulg==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.2.4", - "@formatjs/intl-localematcher": "0.5.8", - "tslib": "2" - } - }, "node_modules/@formatjs/intl-localematcher": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz", - "integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz", + "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==", "license": "MIT", "dependencies": { - "tslib": "2" + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl/node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl/node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz", + "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/icu-skeleton-parser": "1.8.16", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl/node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz", + "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl/node_modules/intl-messageformat": { + "version": "10.7.18", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", + "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.4", + "tslib": "^2.8.0" } }, "node_modules/@googlemaps/js-api-loader": { @@ -4125,16 +4155,23 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "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/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" + "@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==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, "node_modules/@jridgewell/resolve-uri": { @@ -4146,15 +4183,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/source-map": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", @@ -4165,20 +4193,6 @@ "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", @@ -4202,31 +4216,31 @@ "license": "MIT" }, "node_modules/@mui/base": { - "version": "5.0.0-dev.20240529-082515-213b5e33ab", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-dev.20240529-082515-213b5e33ab.tgz", - "integrity": "sha512-3ic6fc6BHstgM+MGqJEVx3zt9g5THxVXm3VVFUfdeplPqAWWgW2QoKfZDLT10s+pi+MAkpgEBP0kgRidf81Rsw==", - "deprecated": "This package has been replaced by @base-ui-components/react", + "version": "5.0.0-beta.70", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.70.tgz", + "integrity": "sha512-Tb/BIhJzb0pa5zv/wu7OdokY9ZKEDqcu1BDFnohyvGCoHuSXbEr90rPq1qeNW3XvTBIbNWHEF7gqge+xpUo6tQ==", + "deprecated": "This package has been replaced by @base-ui/react", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.24.6", - "@floating-ui/react-dom": "^2.0.8", - "@mui/types": "^7.2.14-dev.20240529-082515-213b5e33ab", - "@mui/utils": "^6.0.0-dev.20240529-082515-213b5e33ab", + "@babel/runtime": "^7.26.0", + "@floating-ui/react-dom": "^2.1.1", + "@mui/types": "~7.2.24", + "@mui/utils": "^6.4.8", "@popperjs/core": "^2.11.8", "clsx": "^2.1.1", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.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": { "@types/react": { @@ -4609,9 +4623,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.21", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz", - "integrity": "sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==", + "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" @@ -4623,13 +4637,13 @@ } }, "node_modules/@mui/utils": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.3.tgz", - "integrity": "sha512-jxHRHh3BqVXE9ABxDm+Tc3wlBooYz/4XPa0+4AI+iF38rV1/+btJmSUgG4shDtSWVs/I97aDn5jBCt6SF2Uq2A==", + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.9.tgz", + "integrity": "sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/types": "^7.2.21", + "@mui/types": "~7.2.24", "@types/prop-types": "^15.7.14", "clsx": "^2.1.1", "prop-types": "^15.8.1", @@ -4653,16 +4667,16 @@ } }, "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==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.29.4.tgz", + "integrity": "sha512-wJ3tsqk/y6dp+mXGtT9czciAMEO5Zr3IIAHg9x6IL0Eqanqy0N3chbmQQZv3iq0m2qUpQDLvZ4utZBUTJdjNzw==", "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", + "@babel/runtime": "^7.25.7", + "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0", + "@mui/x-internals": "7.29.0", + "@types/react-transition-group": "^4.4.11", + "clsx": "^2.1.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, @@ -4671,22 +4685,22 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "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", + "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0", + "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", + "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0", "dayjs": "^1.10.7", "luxon": "^3.0.2", "moment": "^2.29.4", - "moment-hijri": "^2.1.2", + "moment-hijri": "^2.1.2 || ^3.0.0", "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" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -4718,34 +4732,24 @@ } } }, - "node_modules/@mui/x-date-pickers/node_modules/@mui/utils": { - "version": "5.16.14", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.14.tgz", - "integrity": "sha512-wn1QZkRzSmeXD1IguBVvJJHV3s6rxJrfb6YuC9Kk6Noh9f8Fb54nUs5JRkKm+BOerRhj5fLg05Dhx/H3Ofb8Mg==", + "node_modules/@mui/x-internals": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.29.0.tgz", + "integrity": "sha512-+Gk6VTZIFD70XreWvdXBwKd8GZ2FlSCuecQFzm6znwqXg1ZsndavrhG9tkxpxo2fM1Zf7Tk8+HcOO0hCbhTQFA==", "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" + "@babel/runtime": "^7.25.7", + "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.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/@nicolo-ribaudo/eslint-scope-5-internals": { @@ -5133,30 +5137,32 @@ "license": "MIT" }, "node_modules/@react-leaflet/core": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", - "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", + "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==", "license": "Hippocratic-2.1", "peerDependencies": { "leaflet": "^1.9.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" } }, "node_modules/@reduxjs/toolkit": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", - "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz", + "integrity": "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==", "license": "MIT", "dependencies": { - "immer": "^9.0.21", - "redux": "^4.2.1", - "redux-thunk": "^2.4.2", - "reselect": "^4.1.8" + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" }, "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.0.2" + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "peerDependenciesMeta": { "react": { @@ -5167,6 +5173,16 @@ } } }, + "node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.15", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.15.tgz", + "integrity": "sha512-VrNANlmnWQnh5COXIIOQXM9oOJw7naGKlBT74ZOOR6lpVXc3gFEu9FJLDFcpCJ2j+NWr8TIwtWD//T6ZX6TKiQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/@remix-run/router": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz", @@ -5291,6 +5307,29 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@stylexjs/stylex": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@stylexjs/stylex/-/stylex-0.19.0.tgz", + "integrity": "sha512-CnUFp7YMaDLDeemsWOfJgoC/gKM5P/yBNMcpJaE6ChJmXr7s0DJwSeGTTlHJcqqwN9OW1qGtmARWLFhGZN1pTA==", + "license": "MIT", + "dependencies": { + "css-mediaquery": "^0.1.2", + "invariant": "^2.2.4", + "styleq": "0.2.1" + } + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -5557,9 +5596,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.17.9", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.17.9.tgz", - "integrity": "sha512-8xcvpWIPaRMDNLMvG9ugcUJMgFK316ZsqkPPbsI+TMZsb10N9jk0B6XgPk4/kgWC2ziHyWR7n7wUhxmD0pChQw==", + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz", + "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", "license": "MIT", "funding": { "type": "github", @@ -5567,19 +5606,19 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.17.9", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.17.9.tgz", - "integrity": "sha512-M5E9gwUq1Stby/pdlYjBlL24euIVuGbWKIFCbtnQxSdXI4PgzjTSdXdV3QE6fc+itF+TUvX/JPTKIwq8yuBXcg==", + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.4.tgz", + "integrity": "sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.17.9" + "@tanstack/query-core": "5.101.4" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "react": "^18.0.0" + "react": "^18 || ^19" } }, "node_modules/@tootallnate/once": { @@ -5962,9 +6001,9 @@ "license": "MIT" }, "node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, "node_modules/@types/ws": { @@ -8607,6 +8646,12 @@ "node": ">=10" } }, + "node_modules/css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==", + "license": "BSD" + }, "node_modules/css-minimizer-webpack-plugin": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", @@ -9283,6 +9328,15 @@ "redux": "^4.2.0" } }, + "node_modules/dnd-core/node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -11238,21 +11292,24 @@ } }, "node_modules/framer-motion": { - "version": "10.18.0", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz", - "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==", + "version": "12.42.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.2.tgz", + "integrity": "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==", "license": "MIT", "dependencies": { + "motion-dom": "^12.42.2", + "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, - "optionalDependencies": { - "@emotion/is-prop-valid": "^0.8.2" - }, "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, "react": { "optional": true }, @@ -11261,23 +11318,6 @@ } } }, - "node_modules/framer-motion/node_modules/@emotion/is-prop-valid": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emotion/memoize": "0.7.4" - } - }, - "node_modules/framer-motion/node_modules/@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", - "license": "MIT", - "optional": true - }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -12198,15 +12238,13 @@ } }, "node_modules/intl-messageformat": { - "version": "10.7.7", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.7.tgz", - "integrity": "sha512-F134jIoeYMro/3I0h08D0Yt4N9o9pjddU/4IIxMMURqbAtI2wu70X8hvG1V48W49zXHXv3RKSF/po+0fDfsGjA==", + "version": "11.2.12", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.12.tgz", + "integrity": "sha512-KW70Xxfcvy7vV3qODfvShWkFDPMqKDAa4N+hSyVBWGNtVhTUFYaqlD/l88DaYPKiVcPP4rPQ3qnH7i5K82Mg7g==", "license": "BSD-3-Clause", "dependencies": { - "@formatjs/ecma402-abstract": "2.2.4", - "@formatjs/fast-memoize": "2.2.3", - "@formatjs/icu-messageformat-parser": "2.9.4", - "tslib": "2" + "@formatjs/fast-memoize": "3.1.7", + "@formatjs/icu-messageformat-parser": "3.5.15" } }, "node_modules/invariant": { @@ -14397,6 +14435,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.27.0.tgz", + "integrity": "sha512-rJicGl/3Fly/E0rOH1YmPZ6e49JCnKknh1ox1vpHnkfjujAkKA6sqUZvH3MTAaXXjgexyUwgNwTJzTtYuAFYJw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -14711,36 +14758,27 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/motion-dom": { + "version": "12.42.2", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.2.tgz", + "integrity": "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.39.0" + } + }, + "node_modules/motion-utils": { + "version": "12.39.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz", + "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==", + "license": "MIT" + }, "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/mui-daterange-picker": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/mui-daterange-picker/-/mui-daterange-picker-1.0.5.tgz", - "integrity": "sha512-+7Mp9DXQw6iLGotwp1rKfMV8sJxtaEto0IVId54orkvrCdqTgn167nzLBz6hd34tJLftg6DKAygkUom7tmg5Bw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.16.7" - }, - "engines": { - "node": ">=8", - "npm": ">=5" - }, - "peerDependencies": { - "@babel/runtime": "^7.16.7", - "@emotion/react": "^11.7.1", - "@emotion/styled": "^11.6.0", - "@mui/icons-material": "^5.0.4", - "@mui/material": "^5.2.6", - "@mui/system": "^5.0.6", - "date-fns": "^2.28.0", - "react": "^17.0.2", - "react-dom": "^17.0.2" - } - }, "node_modules/multicast-dns": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", @@ -17921,13 +17959,10 @@ } }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } @@ -18084,16 +18119,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.2.8" } }, "node_modules/react-error-overlay": { @@ -18146,25 +18180,23 @@ } }, "node_modules/react-intl": { - "version": "6.8.9", - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.8.9.tgz", - "integrity": "sha512-TUfj5E7lyUDvz/GtovC9OMh441kBr08rtIbgh3p0R8iF3hVY+V2W9Am7rb8BpJ/29BH1utJOqOOhmvEVh3GfZg==", + "version": "7.1.14", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-7.1.14.tgz", + "integrity": "sha512-VE/0Wi/lHJlBC7APQpCzLUdIt3GB5B0GZrRW8Q+ACbkHI4j+Wwgg9J1TniN6zmLHmPH5gxXcMy+fkSPfw5p1WQ==", "license": "BSD-3-Clause", "dependencies": { - "@formatjs/ecma402-abstract": "2.2.4", - "@formatjs/icu-messageformat-parser": "2.9.4", - "@formatjs/intl": "2.10.15", - "@formatjs/intl-displaynames": "6.8.5", - "@formatjs/intl-listformat": "7.7.5", - "@types/hoist-non-react-statics": "3", - "@types/react": "16 || 17 || 18", - "hoist-non-react-statics": "3", - "intl-messageformat": "10.7.7", - "tslib": "2" + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/icu-messageformat-parser": "2.11.4", + "@formatjs/intl": "3.1.8", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18 || 19", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "10.7.18", + "tslib": "^2.8.0" }, "peerDependencies": { - "react": "^16.6.0 || 17 || 18", - "typescript": "^4.7 || 5" + "react": "16 || 17 || 18 || 19", + "typescript": "^5.6.0" }, "peerDependenciesMeta": { "typescript": { @@ -18172,6 +18204,48 @@ } } }, + "node_modules/react-intl/node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/react-intl/node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz", + "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/icu-skeleton-parser": "1.8.16", + "tslib": "^2.8.0" + } + }, + "node_modules/react-intl/node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz", + "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "tslib": "^2.8.0" + } + }, + "node_modules/react-intl/node_modules/intl-messageformat": { + "version": "10.7.18", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", + "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.4", + "tslib": "^2.8.0" + } + }, "node_modules/react-is": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", @@ -18179,17 +18253,17 @@ "license": "MIT" }, "node_modules/react-leaflet": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz", - "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", + "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==", "license": "Hippocratic-2.1", "dependencies": { - "@react-leaflet/core": "^2.1.0" + "@react-leaflet/core": "^3.0.0" }, "peerDependencies": { "leaflet": "^1.9.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" } }, "node_modules/react-loading-icons": { @@ -18202,50 +18276,28 @@ } }, "node_modules/react-redux": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", - "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", - "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", - "use-sync-external-store": "^1.0.0" + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" }, "peerDependencies": { - "@types/react": "^16.8 || ^17.0 || ^18.0", - "@types/react-dom": "^16.8 || ^17.0 || ^18.0", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0", - "react-native": ">=0.59", - "redux": "^4 || ^5.0.0-beta.0" + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true }, - "@types/react-dom": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, "redux": { "optional": true } } }, - "node_modules/react-redux/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -18867,13 +18919,12 @@ } }, "node_modules/react-to-print": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/react-to-print/-/react-to-print-2.15.1.tgz", - "integrity": "sha512-1foogIFbCpzAVxydkhBiDfMiFYhIMphiagDOfcG4X/EcQ+fBPqJ0rby9Wv/emzY1YLkIQy/rEgOrWQT+rBKhjw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/react-to-print/-/react-to-print-3.3.0.tgz", + "integrity": "sha512-7j9GIeNZA9glZlbv9mIbIHDOOx+WYfRMbJzh04NiSKjdaeGkxJuKjJQrtRuNKtt5AvEVVjrLCPokZ9yJX51Fvg==", "license": "MIT", "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ~19" } }, "node_modules/react-transition-group": { @@ -18892,16 +18943,6 @@ "react-dom": ">=16.6.0" } }, - "node_modules/react18-input-otp": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/react18-input-otp/-/react18-input-otp-1.1.4.tgz", - "integrity": "sha512-35xvmTeuPWIxd0Z0Opx4z3OoMaTmKN4ubirQCx1YMZiNoe+2h1hsOSUco4aKPlGXWZCtXrfOFieAh46vqiK9mA==", - "license": "MIT", - "peerDependencies": { - "react": "16.2.0 - 18", - "react-dom": "16.2.0 - 18" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -18957,21 +18998,18 @@ } }, "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.9.2" - } + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" }, "node_modules/redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", "license": "MIT", "peerDependencies": { - "redux": "^4" + "redux": "^5.0.0" } }, "node_modules/reflect.getprototypeof": { @@ -19146,9 +19184,9 @@ "license": "MIT" }, "node_modules/reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz", + "integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==", "license": "MIT" }, "node_modules/resize-observer-polyfill": { @@ -19551,13 +19589,10 @@ } }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" }, "node_modules/schema-utils": { "version": "3.3.0", @@ -20594,6 +20629,12 @@ "postcss": "^8.2.15" } }, + "node_modules/styleq": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/styleq/-/styleq-0.2.1.tgz", + "integrity": "sha512-L0TR0NQb+X4/ktDEKmjWyp27gla+LUYi/by5k5SjKXf6/pvZP7wbwEB5J+tqxdFVPgzbsuz+d4RTScO/QZquBw==", + "license": "MIT" + }, "node_modules/stylis": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", @@ -20634,20 +20675,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/sucrase/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/sucrase/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -21418,9 +21445,9 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", "peer": true, "bin": { @@ -21428,7 +21455,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/ua-parser-js": { diff --git a/package.json b/package.json index f438d0e..d0da384 100644 --- a/package.json +++ b/package.json @@ -5,18 +5,21 @@ "dependencies": { "@ant-design/colors": "^7.0.0", "@ant-design/icons": "^5.0.1", + "@astryxdesign/core": "^0.1.9", + "@astryxdesign/theme-neutral": "^0.1.9", "@emotion/cache": "^11.10.7", "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", - "@mui/base": "^5.0.0-alpha.126", + "@mui/base": "^5.0.0-beta.70", "@mui/icons-material": "^5.14.19", "@mui/lab": "^5.0.0-alpha.127", "@mui/material": "^5.12.1", - "@mui/x-date-pickers": "^6.18.2", + "@mui/x-date-pickers": "^7.29.4", "@react-google-maps/api": "^2.20.7", - "@reduxjs/toolkit": "^1.9.5", + "@reduxjs/toolkit": "^2.12.0", + "@stylexjs/stylex": "^0.19.0", "@svgr/webpack": "^7.0.0", - "@tanstack/react-query": "^5.17.9", + "@tanstack/react-query": "^5.101.4", "antd": "^5.11.5", "autosuggest-highlight": "^3.3.4", "axios": "^1.3.5", @@ -28,37 +31,35 @@ "env-cmd": "^10.1.0", "firebase": "^10.14.1", "formik": "^2.2.9", - "framer-motion": "^10.12.4", + "framer-motion": "^12.42.2", "geolib": "^3.3.4", "jsonwebtoken": "^9.0.0", "jwt-decode": "^3.1.2", "leaflet": "^1.9.4", "lodash": "^4.17.21", - "mui-daterange-picker": "^1.0.5", "notistack": "^3.0.1", "papaparse": "^5.4.1", "process": "^0.11.10", "prop-types": "^15.8.1", - "react": "^18.2.0", + "react": "^19.2.8", "react-csv": "^2.2.2", "react-device-detect": "^2.2.3", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", - "react-dom": "^18.2.0", + "react-dom": "^19.2.8", "react-geocode": "^0.2.3", "react-google-autocomplete": "^2.7.3", "react-icons": "^4.12.0", - "react-intl": "^6.4.1", - "react-leaflet": "^4.2.1", + "react-intl": "^7.1.14", + "react-leaflet": "^5.0.0", "react-loading-icons": "^1.1.0", - "react-redux": "^8.0.5", + "react-redux": "^9.2.0", "react-router": "^6.10.0", "react-router-dom": "^6.10.0", "react-scripts": "^5.0.1", "react-timer-hook": "^3.0.5", - "react-to-print": "^2.15.0", - "react18-input-otp": "^1.1.3", - "redux": "^4.2.1", + "react-to-print": "^3.3.0", + "redux": "^5.0.1", "simplebar": "^6.2.5", "simplebar-react": "^3.2.4", "stream-browserify": "^3.0.0", diff --git a/public/favicon.png b/public/favicon.png index 73597f0..fc80e35 100644 Binary files a/public/favicon.png and b/public/favicon.png differ diff --git a/public/index.html b/public/index.html index d30ef4c..39da3c6 100644 --- a/public/index.html +++ b/public/index.html @@ -5,7 +5,7 @@ - + - Nearle Console + Doormile Express Console ({ @@ -39,7 +39,7 @@ const CircularLoader = () => ( {/* Logo Positioned at the Center */} - Logo + Doormile ); diff --git a/src/components/DateFilterDialog.js b/src/components/DateFilterDialog.js index 048bb0c..6a870d5 100644 --- a/src/components/DateFilterDialog.js +++ b/src/components/DateFilterDialog.js @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { Dialog, DialogTitle, DialogContent, Button, Stack, Typography } from '@mui/material'; -import { DateRangePicker } from 'mui-daterange-picker'; +import { DateRangePicker } from 'components/nearle_components/DateRangePicker'; import dayjs from 'dayjs'; import { addDays, addWeeks, startOfWeek, endOfWeek, startOfMonth, endOfMonth, addMonths } from 'date-fns'; diff --git a/src/components/nearle_components/CLAUDE.md b/src/components/nearle_components/CLAUDE.md index fc347f8..9ceafb8 100644 --- a/src/components/nearle_components/CLAUDE.md +++ b/src/components/nearle_components/CLAUDE.md @@ -1,6 +1,6 @@ # CLAUDE.md — `src/components/nearle_components/` -Rules for editing the shared NearlExpress UI primitives. +Rules for editing the shared Doormile Express UI primitives. These components are imported across every page. Changes here have **fan-out impact** — measure twice, cut once. @@ -26,13 +26,13 @@ The `DT` design tokens (palette, alpha helpers, `pillFieldSx`, `SoftPaper`, `Acc **Hard rules when editing components here:** -- **Universal brand colour is `#662582`** (NearlExpress purple — from `themes/theme/default.js` `primary.main`). Every brand surface (page header, dialog header, KPI primary tile, search bars, edit-action buttons, scrollbars) uses this. Gradient pair: `#662582 → #9255AB`. -- **Semantic status palette is distinct** from the brand. Use these for lifecycle indicators only: sky `#0ea5e9`, emerald `#10b981`, amber `#f59e0b`, red `#ef4444`, status-purple `#8b5cf6` (lighter than brand), cyan `#06b6d4`, teal `#14b8a6`, orange `#f97316`, muted `#94a3b8`, indigo `#6366f1` (Accepted status). Don't replace these with brand purple — operators colour-code on them. +- **Universal brand colour is `#C01227`** (Doormile Express red — from `themes/theme/default.js` `primary.main`). Every brand surface (page header, dialog header, KPI primary tile, search bars, edit-action buttons, scrollbars) uses this. Gradient pair: `#C01227 → #D25463`. +- **Semantic status palette is distinct** from the brand. Use these for lifecycle indicators only: sky `#0ea5e9`, emerald `#10b981`, amber `#f59e0b`, red `#ef4444`, status-purple `#8b5cf6`, cyan `#06b6d4`, teal `#14b8a6`, orange `#f97316`, muted `#94a3b8`, indigo `#6366f1` (Accepted status). Don't replace these with brand red — operators colour-code on them. - Don't introduce a colour from `theme.palette` for new surfaces — those are Mantis defaults and don't match the DT system. Use the hex values above directly. - Border radii: `12` (inner), `16` (card), `999` (pill). No other values. - Shadows: `DT.shadowSoft` / `DT.shadowMd` / `DT.shadowPop`. No raw `box-shadow` strings. -> Some existing redesigned pages (`deliveries.js`, `Tenants.js`, `clientPricing.js`) still use `#6366f1` as the brand accent — this is a legacy from the first design pass. `customers.js` and the `createorder1` Saved-Address dialog are already on `#662582`. When you next edit one of the legacy pages, migrate it to brand purple in the same PR. +> Some existing redesigned pages (`deliveries.js`) still use `#6366f1` as the brand accent — this is a legacy from the first design pass. `customers.js` and the `createorder1` Saved-Address dialog are already on `#C01227`. When you next edit one of the legacy pages, migrate it to brand red in the same PR. --- diff --git a/src/components/nearle_components/DateRangePicker.js b/src/components/nearle_components/DateRangePicker.js new file mode 100644 index 0000000..ed68d54 --- /dev/null +++ b/src/components/nearle_components/DateRangePicker.js @@ -0,0 +1,90 @@ +import { useState } from 'react'; +import { Box, Button, Divider, Stack, Typography } from '@mui/material'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { DT, soft, edge } from 'themes/dt/tokens'; + +// Drop-in replacement for the unmaintained `mui-daterange-picker` package +// (pinned to React 17, never updated — see the React 19 upgrade plan). +// Matches its public shape: `onChange({ startDate, endDate, label })` fires +// on every selection (preset or custom), `definedRanges` renders the quick +// picks. `open`/`toggle` are accepted for API compatibility but unused — +// visibility is already gated by the parent Dialog. +export const DateRangePicker = ({ onChange, definedRanges = [] }) => { + const [customStart, setCustomStart] = useState(null); + const [customEnd, setCustomEnd] = useState(null); + + const pickPreset = (range) => { + onChange({ startDate: range.startDate, endDate: range.endDate, label: range.label }); + }; + + const pickCustom = (nextStart, nextEnd) => { + if (nextStart && nextEnd) { + onChange({ startDate: nextStart.toDate(), endDate: nextEnd.toDate(), label: undefined }); + } + }; + + return ( + + + {definedRanges.map((range) => ( + + ))} + + + + + + + Custom range + + + + { + setCustomStart(v); + pickCustom(v, customEnd); + }} + format="DD-MM-YYYY" + slotProps={{ textField: { size: 'small', fullWidth: true } }} + /> + { + setCustomEnd(v); + pickCustom(customStart, v); + }} + format="DD-MM-YYYY" + slotProps={{ textField: { size: 'small', fullWidth: true } }} + /> + + + + + ); +}; + +export default DateRangePicker; diff --git a/src/components/nearle_components/LoaderWithImage.js b/src/components/nearle_components/LoaderWithImage.js index 10e1395..7e03bbc 100644 --- a/src/components/nearle_components/LoaderWithImage.js +++ b/src/components/nearle_components/LoaderWithImage.js @@ -1,7 +1,7 @@ // LoaderWithImage.jsx import React from 'react'; import { Box, CircularProgress } from '@mui/material'; -import nelogo from '../../assets/images/logo-sm.png'; +import nelogo from '../../assets/images/doormile-mark.png'; export default function LoaderWithImage({ size = 70, imgSize = 40, alt = 'loader' }) { return ( @@ -25,7 +25,8 @@ export default function LoaderWithImage({ size = 70, imgSize = 40, alt = 'loader width: '100%', height: '100%', borderRadius: '50%', - objectFit: 'contain' + objectFit: 'cover', + transform: 'scale(1.4)' }} /> diff --git a/src/components/nearle_components/MobileCard.js b/src/components/nearle_components/MobileCard.js index 31e2f69..20d564f 100644 --- a/src/components/nearle_components/MobileCard.js +++ b/src/components/nearle_components/MobileCard.js @@ -42,7 +42,7 @@ MobileCardList.propTypes = { // Card shell — coloured accent rail on the left, a header slot (status badge / // title / action buttons), then any field grid / collapse content as children. -export const MobileCard = ({ accent = '#662582', header, footer, selected = false, onClick, children, sx }) => ( +export const MobileCard = ({ accent = '#C01227', header, footer, selected = false, onClick, children, sx }) => ( diff --git a/src/firebase_notification/notification.js b/src/firebase_notification/notification.js index eb88714..b02582d 100644 --- a/src/firebase_notification/notification.js +++ b/src/firebase_notification/notification.js @@ -73,7 +73,7 @@ export const initFirebaseNotificationListener = () => { const registration = await navigator.serviceWorker.getRegistration(); if (registration) { - registration.showNotification(notification.title || 'Nearle', { + registration.showNotification(notification.title || 'Doormile', { body: notification.body, icon: notification.image || '/favicon.ico', data, diff --git a/src/index.js b/src/index.js index f2c90d2..6f0ec0b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ import logger from './utils/logger'; import { createRoot } from 'react-dom/client'; -import { BrowserRouter } from 'react-router-dom'; +import { BrowserRouter, Link } from 'react-router-dom'; // third-party import { Provider as ReduxProvider } from 'react-redux'; @@ -8,6 +8,13 @@ import { Provider as ReduxProvider } from 'react-redux'; // scroll bar import 'simplebar/dist/simplebar.css'; +// Astryx design system — zero-specificity :where() reset + component base +// styles, safe to load alongside MUI/Emotion (see themes/astryx.js for the +// Doormile brand theme applied per-page during migration). +import '@astryxdesign/core/reset.css'; +import '@astryxdesign/core/astryx.css'; +import { LinkProvider } from '@astryxdesign/core/Link'; + // apex-chart import 'assets/third-party/apex-chart.css'; import 'assets/third-party/react-table.css'; @@ -39,7 +46,7 @@ if (process.env.NODE_ENV !== 'development') { console.warn = () => {}; // Optionally disable console.warn } -logger.info('NearlExpress console application starting...'); +logger.info('Doormile Express console application starting...'); // const root = ReactDOM.createRoot(document.getElementById('root')); @@ -50,7 +57,13 @@ root.render( {/* */}{' '} - + {/* Makes every Astryx nav/link component (SideNavItem, TopNavHeading, + DropdownMenu, ...) route through React Router's Link instead of a + hard reload — see doormile_crm/src/main.jsx for the sibling + app's identical setup. */} + + + {/* */} diff --git a/src/layout/MainLayout/AppSideNav.js b/src/layout/MainLayout/AppSideNav.js new file mode 100644 index 0000000..d15850c --- /dev/null +++ b/src/layout/MainLayout/AppSideNav.js @@ -0,0 +1,176 @@ +import PropTypes from 'prop-types'; +import { useLocation } from 'react-router-dom'; +import { useIntl } from 'react-intl'; + +import { SideNav, SideNavSection, SideNavItem } from '@astryxdesign/core/SideNav'; +import { Icon } from '@astryxdesign/core/Icon'; + +import nearle from 'menu-items/nearle'; +import { DT } from 'themes/dt/tokens'; + +// Thin wrapper around Astryx's SideNav, driven by menu-items/nearle.js. +// Pattern (controlled collapse from the parent, section grouping, the CSS +// override block for row spacing/selected-state styling) mirrors +// doormile_crm/src/layout/MainLayout/Sidebar.jsx — that sibling app's +// Astryx sidebar is the validated reference; only the accent colour changes +// (brand red DT.brand instead of their navy) and items come from the +// existing nearle.js menu config instead of a flat navItems array. +// react-router routing works via the app-root LinkProvider (see +// src/index.js) — no per-item Link bridging needed. + +const AppSideNav = ({ isCollapsed, onCollapsedChange }) => { + const { pathname } = useLocation(); + const intl = useIntl(); + + const renderMenuItem = (item) => { + const label = intl.formatMessage({ id: item.id }); + // Wrap explicitly in Astryx's own rather than passing the bare + // component and letting SideNavItem's renderIconSlot auto-detect it: + // that detection only matches plain functions/forwardRef objects, not + // React.memo-wrapped ones (which several @ant-design/icons v5 icons + // are) — those fell through and got returned as a raw, uninstantiated + // element, crashing with "Objects are not valid as a React child". + // Wrapping here always renders through Icon's own component-mode path, + // which handles any component type and gives every icon the same + // normalised size/colour regardless of source library. + const icon = item.icon ? : undefined; + + if (item.type === 'collapse') { + const isChildSelected = item.children.some((child) => pathname.startsWith(child.url)); + return ( + + {item.children.map(renderMenuItem)} + + ); + } + + return ; + }; + + return ( + <> + + {nearle.children.map(renderMenuItem)} + + + + ); +}; + +AppSideNav.propTypes = { + isCollapsed: PropTypes.bool.isRequired, + onCollapsedChange: PropTypes.func.isRequired +}; + +export default AppSideNav; diff --git a/src/layout/MainLayout/AppTopNav.js b/src/layout/MainLayout/AppTopNav.js new file mode 100644 index 0000000..856ef24 --- /dev/null +++ b/src/layout/MainLayout/AppTopNav.js @@ -0,0 +1,199 @@ +import PropTypes from 'prop-types'; +import { useEffect, useRef, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useDispatch } from 'react-redux'; +import { useQueryClient } from '@tanstack/react-query'; + +import { TopNav } from '@astryxdesign/core/TopNav'; +import { TopNavHeading } from '@astryxdesign/core/TopNav'; +import { DropdownMenu } from '@astryxdesign/core/DropdownMenu'; +import { Popover } from '@astryxdesign/core/Popover'; +import { IconButton } from '@astryxdesign/core/IconButton'; +import { Avatar } from '@astryxdesign/core/Avatar'; +import { HStack } from '@astryxdesign/core/HStack'; +import { VStack } from '@astryxdesign/core/VStack'; +import { Text } from '@astryxdesign/core/Text'; +import { Heading } from '@astryxdesign/core/Heading'; +import { Divider } from '@astryxdesign/core/Divider'; + +import { TbBoxMultiple1 } from 'react-icons/tb'; +import { GrMultiple } from 'react-icons/gr'; +import { BellOutlined, WindowsOutlined, EditOutlined, CommentOutlined, LogoutOutlined, GiftOutlined, MessageOutlined, SettingOutlined } from '@ant-design/icons'; + +import avatar1 from 'assets/images/users/avatar-1.png'; +import logo from 'assets/images/doormile-logo.png'; +import navbarMark from 'assets/images/doormile-mark.png'; +import { clearFcmToken } from 'store/reducers/fcmSlice'; +import { logoutUser } from 'store/reducers/loginUserSlice'; +import { performSessionLogout } from 'utils/session'; +import { DT } from 'themes/dt/tokens'; + +// doormile-logo.png is a white asset; recolour to brand red for this +// light TopNav surface (same trick login.js uses for its white-card logo). +const logoStyle = { + height: 24, + width: 'auto', + filter: 'brightness(0) saturate(100%) invert(15%) sepia(93%) saturate(5437%) hue-rotate(346deg) brightness(81%) contrast(92%)' +}; + +// Collapsed rail: doormile-mark.png (red-on-white disc) needs a white +// backing + scale(1.4) crop to fill its circle cleanly — same composition +// the old DrawerHeader used, just sized for the nav-bar row instead of the +// full-height sidebar rail. +const markWrapperStyle = { + width: 32, + height: 32, + borderRadius: '50%', + background: '#fff', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + overflow: 'hidden', + flexShrink: 0 +}; + +const markStyle = { + width: '100%', + height: '100%', + objectFit: 'cover', + transform: 'scale(1.4)', + display: 'block' +}; + +const NotificationPanel = () => ( + + + Notifications + + + + {[ + { icon: , text: "It's Cristina danny's birthday today.", time: '2 min ago' }, + { icon: , text: 'Aida Burg commented your post.', time: '5 August' }, + { icon: , text: 'Your profile is 60% complete.', time: '7 hours ago' } + ].map((n) => ( + + + + {n.text} + {n.time} + + + ))} + + + + + View all + + + +); + +// ==============================|| MAIN LAYOUT - TOP NAV ||============================== // + +const AppTopNav = ({ isSidebarCollapsed }) => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const queryClient = useQueryClient(); + const topNavRef = useRef(null); + const [headerHeight, setHeaderHeight] = useState(null); + + const firstname = localStorage.getItem('firstname') || ''; + + const handleLogout = () => { + performSessionLogout({ queryClient, dispatch, clearFcmToken, logoutUser }); + }; + + // AppShell (height="auto") pins the sticky side nav below the header using + // a --appshell-header-height CSS var it measures itself via ResizeObserver + // (AppShell.tsx). On some routes that measurement lags/settles wrong, so + // the side nav's sticky offset is off and its bottom-anchored collapse + // button visibly jumps on scroll. Re-measure the header independently here + // and force the same variable with !important on the shell root — an + // author-stylesheet !important rule beats AppShell's own non-important + // inline style.setProperty() call on that same element, so this always + // wins regardless of what the internal measurement produced. + useEffect(() => { + const el = topNavRef.current; + if (!el) return undefined; + + const updateHeight = () => setHeaderHeight(el.getBoundingClientRect().height); + updateHeight(); + + const observer = new ResizeObserver(updateHeight); + observer.observe(el); + return () => observer.disconnect(); + }, []); + + return ( + <> + + + + + Doormile Express + + ) : ( + Doormile Express + ) + } + headingHref="/doormile/dispatch" + /> + } + endContent={ + + , isIconOnly: true, variant: 'ghost' }} + items={[ + { label: 'Create Order', icon: , onClick: () => navigate('/doormile/orders/create') }, + { label: 'Create Multiple Order', icon: , onClick: () => navigate('/doormile/orders/createorders') } + ]} + /> + + } label="Notifications"> + } /> + + + , variant: 'ghost' }} + items={[ + { label: 'View Profile', icon: , onClick: () => navigate('/viewprofile') }, + { label: 'Support Ticket', icon: }, + { type: 'divider' }, + { label: 'Logout', icon: , onClick: handleLogout } + ]} + /> + + } + /> + {headerHeight != null && ( + + )} + + ); +}; + +AppTopNav.propTypes = { + isSidebarCollapsed: PropTypes.bool +}; + +export default AppTopNav; diff --git a/src/layout/MainLayout/Drawer/DrawerContent/NavCard.js b/src/layout/MainLayout/Drawer/DrawerContent/NavCard.js deleted file mode 100644 index 82905e1..0000000 --- a/src/layout/MainLayout/Drawer/DrawerContent/NavCard.js +++ /dev/null @@ -1,32 +0,0 @@ -// material-ui -import { Button, Link, CardMedia, Stack, Typography } from '@mui/material'; - -// project import -import MainCard from 'components/MainCard'; - -// assets -import avatar from 'assets/images/users/avatar-group.png'; -import AnimateButton from 'components/@extended/AnimateButton'; - -// ==============================|| DRAWER CONTENT - NAVIGATION CARD ||============================== // - -const NavCard = () => ( - - - - - Help? - - Get to resolve query - - - - - - - -); - -export default NavCard; diff --git a/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavCollapse.js b/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavCollapse.js deleted file mode 100644 index 3504851..0000000 --- a/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavCollapse.js +++ /dev/null @@ -1,503 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { useEffect, useState, useMemo } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; - -// material-ui -import { styled, useTheme } from '@mui/material/styles'; -import { - Box, - Collapse, - ClickAwayListener, - List, - ListItemButton, - ListItemIcon, - ListItemText, - Paper, - Popper, - Typography, - useMediaQuery -} from '@mui/material'; - -// project import -import NavItem from './NavItem'; -import Dot from 'components/@extended/Dot'; -import SimpleBar from 'components/third-party/SimpleBar'; -import Transitions from 'components/@extended/Transitions'; - -import useConfig from 'hooks/useConfig'; -import { dispatch, useSelector } from 'store'; -import { activeItem } from 'store/reducers/menu'; -import { MenuOrientation, ThemeMode } from 'config'; - -// assets -import { BorderOutlined, DownOutlined, UpOutlined, RightOutlined } from '@ant-design/icons'; - -// mini-menu - wrapper -const PopperStyled = styled(Popper)(({ theme }) => ({ - overflow: 'visible', - zIndex: 1202, - minWidth: 180, - '&:before': { - content: '""', - display: 'block', - position: 'absolute', - top: 38, - left: -5, - width: 10, - height: 10, - backgroundColor: theme.palette.background.paper, - transform: 'translateY(-50%) rotate(45deg)', - zIndex: 120, - borderLeft: `1px solid ${theme.palette.grey.A800}`, - borderBottom: `1px solid ${theme.palette.grey.A800}` - } -})); - -// ==============================|| NAVIGATION - LIST COLLAPSE ||============================== // - -const NavCollapse = ({ menu, level, parentId, setSelectedItems, selectedItems, setSelectedLevel, selectedLevel }) => { - const theme = useTheme(); - - const downLG = useMediaQuery(theme.breakpoints.down('lg')); - const menuState = useSelector((state) => state.menu); - - const { drawerOpen } = menuState; - const { menuOrientation } = useConfig(); - const navigation = useNavigate(); - - const [open, setOpen] = useState(false); - const [selected, setSelected] = useState(null); - - const [anchorEl, setAnchorEl] = useState(null); - - const handleClick = (event) => { - setAnchorEl(null); - setSelectedLevel(level); - if (drawerOpen) { - setOpen(!open); - setSelected(!selected ? menu.id : null); - setSelectedItems(!selected ? menu.id : ''); - if (menu.url) navigation(`${menu.url}`); - } else { - setAnchorEl(event?.currentTarget); - } - }; - - const handlerIconLink = () => { - if (!drawerOpen) { - if (menu.url) navigation(`${menu.url}`); - setSelected(menu.id); - } - }; - - const handleHover = (event) => { - setAnchorEl(event?.currentTarget); - if (!drawerOpen) { - setSelected(menu.id); - } - }; - - const miniMenuOpened = Boolean(anchorEl); - - const handleClose = () => { - setOpen(false); - if (!miniMenuOpened) { - if (!menu.url) { - setSelected(null); - } - } - setAnchorEl(null); - }; - - useMemo(() => { - if (selected === selectedItems) { - if (level === 1) { - setOpen(true); - } - } else { - if (level === selectedLevel) { - setOpen(false); - if (!miniMenuOpened && !drawerOpen && !selected) { - setSelected(null); - } - if (drawerOpen) { - setSelected(null); - } - } - } - }, [selectedItems, level, selected, miniMenuOpened, drawerOpen, selectedLevel]); - - const { pathname } = useLocation(); - - useEffect(() => { - if (pathname === menu.url) { - setSelected(menu.id); - } - // eslint-disable-next-line - }, [pathname]); - - const checkOpenForParent = (child, id) => { - child.forEach((item) => { - if (item.url === pathname) { - setOpen(true); - setSelected(id); - } - }); - }; - - useEffect(() => { - setOpen(false); - if (!miniMenuOpened) { - setSelected(null); - } - if (miniMenuOpened) setAnchorEl(null); - if (menu.children) { - menu.children.forEach((item) => { - if (item.children?.length) { - checkOpenForParent(item.children, menu.id); - } - if (pathname && pathname.includes('product-details')) { - if (item.url && item.url.includes('product-details')) { - setSelected(menu.id); - setOpen(true); - } - } - if (item.url === pathname) { - setSelected(menu.id); - setOpen(true); - } - }); - } - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pathname, menu.children]); - - useEffect(() => { - if (menu.url === pathname) { - dispatch(activeItem({ openItem: [menu.id] })); - setSelected(menu.id); - setAnchorEl(null); - setOpen(true); - } - }, [pathname, menu]); - - const navCollapse = menu.children?.map((item) => { - switch (item.type) { - case 'collapse': - return ( - - ); - case 'item': - return ; - default: - return ( - - Fix - Collapse or Item - - ); - } - }); - - const isSelected = selected === menu.id; - const borderIcon = level === 1 ? : false; - const Icon = menu.icon; - const menuIcon = menu.icon ? : borderIcon; - // const textColor = theme.palette.mode === ThemeMode.DARK ? 'grey.400' : 'text.primary'; - // const iconSelectedColor = theme.palette.mode === ThemeMode.DARK && drawerOpen ? theme.palette.text.primary : theme.palette.primary.main; - const popperId = miniMenuOpened ? `collapse-pop-${menu.id}` : undefined; - const FlexBox = { display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }; - const textColor = 'white'; - const iconSelectedColor = 'white'; - // const isSelected = true; - - return ( - <> - {menuOrientation === MenuOrientation.VERTICAL || downLG ? ( - <> - - {menuIcon && ( - - {menuIcon} - - )} - {(drawerOpen || (!drawerOpen && level !== 1)) && ( - - {menu.title} - - } - secondary={ - menu.caption && ( - - {menu.caption} - - ) - } - /> - )} - {(drawerOpen || (!drawerOpen && level !== 1)) && - (miniMenuOpened || open ? ( - - ) : ( - - ))} - - {!drawerOpen && ( - - {({ TransitionProps }) => ( - - - - - {navCollapse} - - - - - )} - - )} - - {drawerOpen && ( - - {navCollapse} - - )} - - ) : ( - <> - - - {menuIcon && ( - - {menuIcon} - - )} - {!menuIcon && level !== 1 && ( - - - - )} - - {menu.title} - - } - /> - {miniMenuOpened ? : } - - - {anchorEl && ( - - {({ TransitionProps }) => ( - - - - - {navCollapse} - - - - - )} - - )} - - - )} - - ); -}; - -NavCollapse.propTypes = { - menu: PropTypes.object, - level: PropTypes.number, - parentId: PropTypes.string, - setSelectedItems: PropTypes.func, - selectedItems: PropTypes.string, - setSelectedLevel: PropTypes.func, - selectedLevel: PropTypes.number -}; - -export default NavCollapse; diff --git a/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.js b/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.js deleted file mode 100644 index 7017fe3..0000000 --- a/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.js +++ /dev/null @@ -1,343 +0,0 @@ -import PropTypes from 'prop-types'; -import { Fragment, useEffect, useState } from 'react'; -import { useLocation } from 'react-router'; - -// material-ui -import { styled, useTheme } from '@mui/material/styles'; -import { - Box, - ClickAwayListener, - List, - ListItemButton, - ListItemIcon, - ListItemText, - Paper, - Popper, - Typography, - useMediaQuery -} from '@mui/material'; - -// third-party -import { FormattedMessage } from 'react-intl'; - -// project import -import NavItem from './NavItem'; -import NavCollapse from './NavCollapse'; -import SimpleBar from 'components/third-party/SimpleBar'; -import Transitions from 'components/@extended/Transitions'; - -import { MenuOrientation } from 'config'; -import useConfig from 'hooks/useConfig'; -import { dispatch, useSelector } from 'store'; -import { activeID } from 'store/reducers/menu'; - -// assets -import { DownOutlined, RightOutlined } from '@ant-design/icons'; - -// ==============================|| NAVIGATION - LIST GROUP ||============================== // - -const PopperStyled = styled(Popper)(({ theme }) => ({ - overflow: 'visible', - zIndex: 1202, - minWidth: 180, - '&:before': { - content: '""', - display: 'block', - position: 'absolute', - top: 5, - left: 32, - width: 12, - height: 12, - transform: 'translateY(-50%) rotate(45deg)', - zIndex: 120, - borderWidth: '6px', - borderStyle: 'solid', - borderColor: `${theme.palette.background.paper} transparent transparent ${theme.palette.background.paper}` - } -})); - -const NavGroup = ({ item, lastItem, remItems, lastItemId, setSelectedItems, selectedItems, setSelectedLevel, selectedLevel }) => { - const theme = useTheme(); - const { pathname } = useLocation(); - - const { menuOrientation } = useConfig(); - const menu = useSelector((state) => state.menu); - const { drawerOpen, selectedID } = menu; - - const downLG = useMediaQuery(theme.breakpoints.down('lg')); - - const [anchorEl, setAnchorEl] = useState(null); - const [currentItem, setCurrentItem] = useState(item); - - const openMini = Boolean(anchorEl); - - useEffect(() => { - if (lastItem) { - if (item.id === lastItemId) { - const localItem = { ...item }; - const elements = remItems.map((ele) => ele.elements); - localItem.children = elements.flat(1); - setCurrentItem(localItem); - } else { - setCurrentItem(item); - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [item, lastItem, downLG]); - - const checkOpenForParent = (child, id) => { - child.forEach((ele) => { - if (ele.children?.length) { - checkOpenForParent(ele.children, currentItem.id); - } - if (ele.url === pathname) { - dispatch(activeID(id)); - } - }); - }; - const checkSelectedOnload = (data) => { - const childrens = data.children ? data.children : []; - childrens.forEach((itemCheck) => { - if (itemCheck.children?.length) { - checkOpenForParent(itemCheck.children, currentItem.id); - } - if (itemCheck.url === pathname) { - dispatch(activeID(currentItem.id)); - } - }); - }; - - useEffect(() => { - checkSelectedOnload(currentItem); - if (openMini) setAnchorEl(null); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pathname, currentItem]); - - const handleClick = (event) => { - if (!openMini) { - setAnchorEl(event?.currentTarget); - } - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - const Icon = currentItem?.icon; - const itemIcon = currentItem?.icon ? ( - - ) : null; - - const navCollapse = item.children?.map((menuItem) => { - switch (menuItem.type) { - case 'collapse': - return ( - - ); - case 'item': - return ; - default: - } - }); - - const moreItems = remItems.map((itemRem, i) => ( - - {itemRem.title && ( - - {itemRem.title} - - )} - {itemRem?.elements?.map((menu) => { - switch (menu.type) { - case 'collapse': - return ( - - ); - case 'item': - return ; - default: - return ( - - Menu Items Error - - ); - } - })} - - )); - - // menu list collapse & items - const items = currentItem.children?.map((menu) => { - switch (menu.type) { - case 'collapse': - return ( - - ); - case 'item': - return ; - default: - return ( - - Menu Items Error - - ); - } - }); - - const popperId = openMini ? `group-pop-${item.id}` : undefined; - - return ( - <> - {menuOrientation === MenuOrientation.VERTICAL || downLG ? ( - - - {item.title} - - {item.caption && ( - - {item.caption} - - )} - - ) - } - sx={{ mt: drawerOpen && item.title ? 1.5 : 0, py: 0, zIndex: 0 }} - > - {navCollapse} - - ) : ( - - - {itemIcon && ( - - {currentItem.id === lastItemId ? : itemIcon} - - )} - - {currentItem.id === lastItemId ? : currentItem.title} - - } - /> - {openMini ? ( - - ) : ( - - )} - {anchorEl && ( - - {({ TransitionProps }) => ( - - - - - {currentItem.id !== lastItemId ? items : moreItems} - - - - - )} - - )} - - - )} - - ); -}; - -NavGroup.propTypes = { - item: PropTypes.object, - lastItem: PropTypes.number, - remItems: PropTypes.array, - lastItemId: PropTypes.string, - setSelectedItems: PropTypes.func, - selectedItems: PropTypes.string, - setSelectedLevel: PropTypes.func, - selectedLevel: PropTypes.number -}; - -export default NavGroup; diff --git a/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.js b/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.js deleted file mode 100644 index 3cd1a24..0000000 --- a/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.js +++ /dev/null @@ -1,294 +0,0 @@ -import PropTypes from 'prop-types'; -import { forwardRef, useEffect, useState } from 'react'; -import { Link } from 'react-router-dom'; -import { useDispatch, useSelector } from 'react-redux'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { Avatar, Chip, ListItemButton, ListItemIcon, ListItemText, Tooltip, Typography, useMediaQuery } from '@mui/material'; - -// project import -import Dot from 'components/@extended/Dot'; - -import { MenuOrientation, ThemeMode } from 'config'; -import useConfig from 'hooks/useConfig'; -// import { dispatch, useSelector } from 'store'; -import { activeItem, openDrawer, setSelectedMenu } from 'store/reducers/menu'; - -// ==============================|| NAVIGATION - LIST ITEM ||============================== // - -const NavItem = ({ item, level }) => { - const theme = useTheme(); - const dispatch = useDispatch(); - - const { menuOrientation } = useConfig(); - const { drawerOpen, openItem } = useSelector((state) => state.menu); - - const downLG = useMediaQuery(theme.breakpoints.down('lg')); - - let itemTarget = '_self'; - if (item.target) { - itemTarget = '_blank'; - } - - let listItemProps = { component: forwardRef((props, ref) => ) }; - if (item?.external) { - listItemProps = { component: 'a', href: item.url, target: itemTarget }; - } - - const Icon = item.icon; - - const isSelected = openItem.findIndex((id) => id === item.id) > -1; - - const itemIcon = item.icon ? ( - - ) : ( - false - ); - - // const { pathname } = useLocation(); - const pathname = document.location.pathname; - - // active menu item on page load - useEffect(() => { - if (pathname && pathname.includes('product-details')) { - if (item.url && item.url.includes('product-details')) { - dispatch(activeItem({ openItem: [item.id] })); - } - } - - if (pathname && pathname.includes('kanban')) { - if (item.url && item.url.includes('kanban')) { - dispatch(activeItem({ openItem: [item.id] })); - } - } - - if (pathname.includes(item.url)) { - dispatch(activeItem({ openItem: [item.id] })); - } - - // eslint-disable-next-line - }, [pathname]); - - useEffect(() => { - dispatch(setSelectedMenu(pathname)); - }, [pathname]); - - const textColor = theme.palette.mode === ThemeMode.DARK ? 'grey.400' : '#fff'; - const iconSelectedColor = theme.palette.mode === ThemeMode.DARK && drawerOpen ? 'text.primary' : 'primary.main'; - - return ( - <> - {menuOrientation === MenuOrientation.VERTICAL || downLG ? ( - - { - // dispatch(setSelectedMenu(item)); - }} - sx={{ - zIndex: 1201, - pl: drawerOpen ? `${level * 28}px` : 1.5, - py: !drawerOpen && level === 1 ? 1.25 : 1, - ...(drawerOpen && { - '&:hover': { - bgcolor: '#7b1fa2' - }, - '&.Mui-selected': { - bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter', - borderRight: `2px solid ${theme.palette.primary.main}`, - color: iconSelectedColor, - '&:hover': { - color: iconSelectedColor, - bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter' - } - } - }), - ...(!drawerOpen && { - bgcolor: '#662582', - '&:hover': { - bgcolor: '#662582' - }, - '&.Mui-selected': { - '&:hover': { - bgcolor: 'transparent' - }, - bgcolor: 'transparent' - } - }) - }} - {...(downLG && { - onClick: () => { - dispatch(openDrawer(false)); - } - })} - > - {itemIcon && ( - - {itemIcon} - - )} - {(drawerOpen || (!drawerOpen && level !== 1)) && ( - - {item.title} - - } - /> - )} - {(drawerOpen || (!drawerOpen && level !== 1)) && item.chip && ( - {item.chip.avatar}} - /> - )} - - - ) : ( - - {itemIcon && ( - - {itemIcon} - - )} - - {!itemIcon && ( - - - - )} - - {item.title} - - } - /> - {(drawerOpen || (!drawerOpen && level !== 1)) && item.chip && ( - {item.chip.avatar}} - /> - )} - - )} - - ); -}; - -NavItem.propTypes = { - item: PropTypes.object, - level: PropTypes.number -}; - -export default NavItem; diff --git a/src/layout/MainLayout/Drawer/DrawerContent/Navigation/index.js b/src/layout/MainLayout/Drawer/DrawerContent/Navigation/index.js deleted file mode 100644 index c00e5ad..0000000 --- a/src/layout/MainLayout/Drawer/DrawerContent/Navigation/index.js +++ /dev/null @@ -1,111 +0,0 @@ -import { useEffect, useLayoutEffect, useState } from 'react'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { Box, Typography, useMediaQuery } from '@mui/material'; -import { Menu } from 'menu-items/dashboard'; - -import { useSelector } from 'store'; -import useConfig from 'hooks/useConfig'; -import { HORIZONTAL_MAX_ITEM, MenuOrientation } from 'config'; - -// project import -import NavGroup from './NavGroup'; -import menuItem from 'menu-items'; - -// ==============================|| DRAWER CONTENT - NAVIGATION ||============================== // - -const Navigation = () => { - const theme = useTheme(); - - const downLG = useMediaQuery(theme.breakpoints.down('lg')); - - const { menuOrientation } = useConfig(); - const { drawerOpen } = useSelector((state) => state.menu); - const [selectedItems, setSelectedItems] = useState(''); - const [selectedLevel, setSelectedLevel] = useState(0); - const [menuItems, setMenuItems] = useState({ items: [] }); - - useEffect(() => { - handlerMenuItem(); - // eslint-disable-next-line - }, []); - - let getMenu = Menu(); - const handlerMenuItem = () => { - const isFound = menuItem.items.some((element) => { - if (element.id === 'group-dashboard') { - return true; - } - return false; - }); - - if (getMenu?.id !== undefined && !isFound) { - menuItem.items.splice(0, 0, getMenu); - setMenuItems(menuItem); - } - }; - - useLayoutEffect(() => { - setMenuItems(menuItem); - // eslint-disable-next-line - }, [menuItem]); - - const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG; - - const lastItem = isHorizontal ? HORIZONTAL_MAX_ITEM : null; - let lastItemIndex = menuItems.items.length - 1; - let remItems = []; - let lastItemId; - - // first it checks menu item is more than giving HORIZONTAL_MAX_ITEM after that get lastItemid by giving horizontal max - // item and it sets horizontal menu by giving horizontal max item lastly slice menuItem from array and set into remItems - - if (lastItem && lastItem < menuItems.items.length) { - lastItemId = menuItems.items[lastItem - 1].id; - lastItemIndex = lastItem - 1; - remItems = menuItems.items.slice(lastItem - 1, menuItems.items.length).map((item) => ({ - title: item.title, - elements: item.children, - icon: item.icon - })); - } - - const navGroups = menuItems.items.slice(0, lastItemIndex + 1).map((item) => { - switch (item.type) { - case 'group': - return ( - - ); - default: - return ( - - Fix - Navigation Group - - ); - } - }); - return ( - ul:first-of-type': { mt: 0 }, - display: isHorizontal ? { xs: 'block', lg: 'flex' } : 'block' - }} - > - {navGroups} - - ); -}; - -export default Navigation; diff --git a/src/layout/MainLayout/Drawer/DrawerContent/Navigation/navitemdemo.js b/src/layout/MainLayout/Drawer/DrawerContent/Navigation/navitemdemo.js deleted file mode 100644 index f92350a..0000000 --- a/src/layout/MainLayout/Drawer/DrawerContent/Navigation/navitemdemo.js +++ /dev/null @@ -1,99 +0,0 @@ -import PropTypes from 'prop-types'; -import { forwardRef, useEffect } from 'react'; -import { Link } from 'react-router-dom'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { useMediaQuery, Avatar, Chip, ListItemButton, ListItemText, Typography } from '@mui/material'; - -// project imports -import { ThemeMode } from 'config'; -import { dispatch, useSelector } from 'store'; -import { activeComponent, openComponentDrawer } from 'store/reducers/menu'; - -// ==============================|| NAVIGATION - LIST ITEM ||============================== // - -const NavItem = ({ item }) => { - const theme = useTheme(); - const matchesMD = useMediaQuery(theme.breakpoints.down('md')); - - const menu = useSelector((state) => state.menu); - const { openComponent } = menu; - - let itemTarget = '_self'; - if (item.target) { - itemTarget = '_blank'; - } - - let listItemProps = { component: forwardRef((props, ref) => ) }; - if (item?.external) { - listItemProps = { component: 'a', href: item.url, target: itemTarget }; - } - - const itemHandler = (id) => { - dispatch(activeComponent({ openComponent: id })); - if (matchesMD) dispatch(openComponentDrawer({ componentDrawerOpen: false })); - }; - - // active menu item on page load - useEffect(() => { - const currentIndex = document.location.pathname - .toString() - .split('/') - .findIndex((id) => id === item.id); - if (currentIndex > -1) { - dispatch(activeComponent({ openComponent: item.id })); - } - // eslint-disable-next-line - }, []); - - const textColor = theme.palette.mode === ThemeMode.DARK ? 'grey.400' : 'text.primary'; - const iconSelectedColor = theme.palette.mode === ThemeMode.DARK ? 'text.primary' : 'primary.main'; - - return ( - itemHandler(item.id)} - selected={openComponent === item.id} - sx={{ - pl: 4, - py: 1, - mb: 0.5, - '&:hover': { - bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter' - }, - '&.Mui-selected': { - bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter', - borderRight: `2px solid ${theme.palette.primary.main}`, - '&:hover': { - bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter' - } - } - }} - > - - {item.title} - - } - /> - {item.chip && ( - {item.chip.avatar}} - /> - )} - - ); -}; - -NavItem.propTypes = { - item: PropTypes.object -}; - -export default NavItem; diff --git a/src/layout/MainLayout/Drawer/DrawerContent/index.js b/src/layout/MainLayout/Drawer/DrawerContent/index.js deleted file mode 100644 index 12f2256..0000000 --- a/src/layout/MainLayout/Drawer/DrawerContent/index.js +++ /dev/null @@ -1,37 +0,0 @@ -// material-ui -import // useMediaQuery, -// useTheme -'@mui/material'; - -// project import -// import NavCard from './NavCard'; -import Navigation from './Navigation'; -// import { useSelector } from 'store'; -import SimpleBar from 'components/third-party/SimpleBar'; - -// ==============================|| DRAWER CONTENT ||============================== // - -const DrawerContent = () => { - // const theme = useTheme(); - // const matchDownMD = useMediaQuery(theme.breakpoints.down('lg')); - - // const menu = useSelector((state) => state.menu); - // const { drawerOpen } = menu; - - return ( - - - {/* no need navCrd just hided */} - {/* {drawerOpen && !matchDownMD && } */} - - ); -}; - -export default DrawerContent; diff --git a/src/layout/MainLayout/Drawer/DrawerHeader/DrawerHeaderStyled.js b/src/layout/MainLayout/Drawer/DrawerHeader/DrawerHeaderStyled.js deleted file mode 100644 index 2f8a1ff..0000000 --- a/src/layout/MainLayout/Drawer/DrawerHeader/DrawerHeaderStyled.js +++ /dev/null @@ -1,22 +0,0 @@ -import PropTypes from 'prop-types'; - -// material-ui -import { styled } from '@mui/material/styles'; -import { Box } from '@mui/material'; - -// ==============================|| DRAWER HEADER - STYLED ||============================== // - -const DrawerHeaderStyled = styled(Box, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({ - ...theme.mixins.toolbar, - display: 'flex', - alignItems: 'center', - justifyContent: open ? 'flex-start' : 'center', - paddingLeft: theme.spacing(open ? 3 : 0) -})); - -DrawerHeaderStyled.propTypes = { - theme: PropTypes.object, - open: PropTypes.bool -}; - -export default DrawerHeaderStyled; diff --git a/src/layout/MainLayout/Drawer/DrawerHeader/index.js b/src/layout/MainLayout/Drawer/DrawerHeader/index.js deleted file mode 100644 index 57b10dd..0000000 --- a/src/layout/MainLayout/Drawer/DrawerHeader/index.js +++ /dev/null @@ -1,58 +0,0 @@ -import PropTypes from 'prop-types'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { useMediaQuery } from '@mui/material'; - -// project import -import DrawerHeaderStyled from './DrawerHeaderStyled'; - -import { MenuOrientation } from 'config'; -import useConfig from 'hooks/useConfig'; - -import logo from 'assets/images/logo-nearle9.png' -import logo1 from 'assets/images/logo-sm1.png' -// ==============================|| DRAWER HEADER ||============================== // - -const DrawerHeader = ({ open }) => { - const theme = useTheme(); - const downLG = useMediaQuery(theme.breakpoints.down('lg')); - - const { menuOrientation } = useConfig(); - const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG; - - return ( - - {/* */} - - {(open) && - logo - } - {(!open) && - logo - } - - ); -}; - -DrawerHeader.propTypes = { - open: PropTypes.bool -}; - -export default DrawerHeader; diff --git a/src/layout/MainLayout/Drawer/HorizontalBar.js b/src/layout/MainLayout/Drawer/HorizontalBar.js deleted file mode 100644 index 5096d22..0000000 --- a/src/layout/MainLayout/Drawer/HorizontalBar.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { AppBar, Box, Container, useScrollTrigger } from '@mui/material'; - -// project imports -import Navigation from './DrawerContent/Navigation'; -import useConfig from 'hooks/useConfig'; - -// ==============================|| HORIZONTAL MENU LIST ||============================== // - -function ElevationScroll({ children, window }) { - const theme = useTheme(); - // Note that you normally won't need to set the window ref as useScrollTrigger - // will default to window. - // This is only being set here because the demo is in an iframe. - const trigger = useScrollTrigger({ - disableHysteresis: true, - threshold: 0, - target: window - }); - - theme.shadows[4] = theme.customShadows.z1; - - return React.cloneElement(children, { - elevation: trigger ? 4 : 0 - }); -} - -// ==============================|| HORIZONTAL MENU LIST ||============================== // - -const CustomAppBar = () => { - const theme = useTheme(); - const { container } = useConfig(); - - return ( - - - - - - - - - - ); -}; - -export default CustomAppBar; diff --git a/src/layout/MainLayout/Drawer/MiniDrawerStyled.js b/src/layout/MainLayout/Drawer/MiniDrawerStyled.js deleted file mode 100644 index ec2de91..0000000 --- a/src/layout/MainLayout/Drawer/MiniDrawerStyled.js +++ /dev/null @@ -1,51 +0,0 @@ -// material-ui -import { styled } from '@mui/material/styles'; -import Drawer from '@mui/material/Drawer'; - -// project import -import { DRAWER_WIDTH, ThemeMode } from 'config'; - -const openedMixin = (theme) => ({ - width: DRAWER_WIDTH, - // borderRight: `1px solid ${theme.palette.divider}`, - borderRight: 'none', - transition: theme.transitions.create('width', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.enteringScreen - }), - overflowX: 'hidden', - boxShadow: theme.palette.mode === ThemeMode.DARK ? theme.customShadows.z1 : 'none', - backgroundColor:'#662582', -}); - -const closedMixin = (theme) => ({ - transition: theme.transitions.create('width', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen - }), - // overflowX: 'hidden', - width: theme.spacing(7.5), - borderRight: 'none', - boxShadow: theme.customShadows.z1, - backgroundColor:'#662582', -}); - -// ==============================|| DRAWER - MINI STYLED ||============================== // - -const MiniDrawerStyled = styled(Drawer, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({ - width: DRAWER_WIDTH, - flexShrink: 0, - whiteSpace: 'nowrap', - boxSizing: 'border-box', - - ...(open && { - ...openedMixin(theme), - '& .MuiDrawer-paper': openedMixin(theme) - }), - ...(!open && { - ...closedMixin(theme), - '& .MuiDrawer-paper': closedMixin(theme) - }) -})); - -export default MiniDrawerStyled; diff --git a/src/layout/MainLayout/Drawer/index.js b/src/layout/MainLayout/Drawer/index.js deleted file mode 100644 index 18e1d55..0000000 --- a/src/layout/MainLayout/Drawer/index.js +++ /dev/null @@ -1,74 +0,0 @@ -import PropTypes from 'prop-types'; -import { useMemo } from 'react'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { Box, Drawer, useMediaQuery } from '@mui/material'; - -// project import -import DrawerHeader from './DrawerHeader'; -import DrawerContent from './DrawerContent'; -import MiniDrawerStyled from './MiniDrawerStyled'; - -import { DRAWER_WIDTH } from 'config'; -import { dispatch, useSelector } from 'store'; -import { openDrawer } from 'store/reducers/menu'; - -// ==============================|| MAIN LAYOUT - DRAWER ||============================== // - -const MainDrawer = ({ window }) => { - const theme = useTheme(); - const matchDownMD = useMediaQuery(theme.breakpoints.down('lg')); - - const menu = useSelector((state) => state.menu); - const { drawerOpen } = menu; - - // responsive drawer container - const container = window !== undefined ? () => window().document.body : undefined; - - // header content - const drawerContent = useMemo(() => , []); - const drawerHeader = useMemo(() => , [drawerOpen]); - - return ( - - {!matchDownMD ? ( - - {drawerHeader} - {drawerContent} - - ) : ( - dispatch(openDrawer(!drawerOpen))} - ModalProps={{ keepMounted: true }} - sx={{ - display: { xs: 'block', lg: 'none' }, - '& .MuiDrawer-paper': { - boxSizing: 'border-box', - width: DRAWER_WIDTH, - borderRight: `1px solid ${theme.palette.divider}`, - backgroundImage: 'none', - boxShadow: 'inherit', - bgcolor:'#662582' - } - }} - > - {drawerHeader} - {drawerContent} - - )} - - ); -}; - -MainDrawer.propTypes = { - window: PropTypes.object -}; - -export default MainDrawer; diff --git a/src/layout/MainLayout/Header/AppBarStyled.js b/src/layout/MainLayout/Header/AppBarStyled.js deleted file mode 100644 index f10b097..0000000 --- a/src/layout/MainLayout/Header/AppBarStyled.js +++ /dev/null @@ -1,35 +0,0 @@ -import PropTypes from 'prop-types'; - -// material-ui -import { styled } from '@mui/material/styles'; -import AppBar from '@mui/material/AppBar'; - -// project import -import { DRAWER_WIDTH } from 'config'; - -// ==============================|| HEADER - APP BAR STYLED ||============================== // - -const AppBarStyled = styled(AppBar, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({ - zIndex: theme.zIndex.drawer + 1, - transition: theme.transitions.create(['width', 'margin'], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen - }), - ...(!open && { - width: `calc(100% - ${theme.spacing(7.5)})` - }), - ...(open && { - marginLeft: DRAWER_WIDTH, - width: `calc(100% - ${DRAWER_WIDTH}px)`, - transition: theme.transitions.create(['width', 'margin'], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.enteringScreen - }) - }) -})); - -AppBarStyled.propTypes = { - open: PropTypes.bool -}; - -export default AppBarStyled; diff --git a/src/layout/MainLayout/Header/HeaderContent/MegaMenuSection.js b/src/layout/MainLayout/Header/HeaderContent/MegaMenuSection.js deleted file mode 100644 index 3a12aab..0000000 --- a/src/layout/MainLayout/Header/HeaderContent/MegaMenuSection.js +++ /dev/null @@ -1,298 +0,0 @@ -import { useRef, useState } from 'react'; -import { Link } from 'react-router-dom'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { - Button, - Box, - CardMedia, - ClickAwayListener, - Grid, - List, - ListItemButton, - ListItemIcon, - ListItemText, - ListSubheader, - Paper, - Popper, - Stack, - Typography -} from '@mui/material'; - -// project import -import MainCard from 'components/MainCard'; -import Dot from 'components/@extended/Dot'; -import IconButton from 'components/@extended/IconButton'; -import Transitions from 'components/@extended/Transitions'; -import { DRAWER_WIDTH, ThemeMode } from 'config'; - -// assets -import { ArrowRightOutlined, WindowsOutlined } from '@ant-design/icons'; -import backgroundVector from 'assets/images/mega-menu/back.svg'; -import imageChart from 'assets/images/mega-menu/chart.svg'; -import AnimateButton from 'components/@extended/AnimateButton'; - -// ==============================|| HEADER CONTENT - MEGA MENU SECTION ||============================== // - -const MegaMenuSection = () => { - const theme = useTheme(); - - const anchorRef = useRef(null); - const [open, setOpen] = useState(false); - - const handleClose = (event) => { - if (anchorRef.current && anchorRef.current.contains(event.target)) { - return; - } - setOpen(false); - }; - - const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300'; - const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100'; - - return ( - - - - - - {({ TransitionProps }) => ( - - - - - - - - - - Explore Components - - - Try our pre made component pages to check how it feels and suits as per your need. - - - - - - - - - - - - - - - - - Authentication - - - } - > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Other Pages - - - } - > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SAAS Pages - - - } - > - - - - - - - - - - - - - - - - - - - - - - - )} - - - ); -}; - -export default MegaMenuSection; diff --git a/src/layout/MainLayout/Header/HeaderContent/Message.js b/src/layout/MainLayout/Header/HeaderContent/Message.js deleted file mode 100644 index 289db22..0000000 --- a/src/layout/MainLayout/Header/HeaderContent/Message.js +++ /dev/null @@ -1,252 +0,0 @@ -import { useRef, useState } from 'react'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { - Avatar, - Box, - ClickAwayListener, - Divider, - List, - ListItemButton, - ListItemAvatar, - ListItemText, - ListItemSecondaryAction, - Paper, - Popper, - Typography, - useMediaQuery -} from '@mui/material'; - -// project import -import MainCard from 'components/MainCard'; -import IconButton from 'components/@extended/IconButton'; -import Transitions from 'components/@extended/Transitions'; -import { ThemeMode } from 'config'; - -// assets -import avatar2 from 'assets/images/users/avatar-2.png'; -import avatar3 from 'assets/images/users/avatar-3.png'; -import avatar4 from 'assets/images/users/avatar-4.png'; -import avatar5 from 'assets/images/users/avatar-5.png'; -import { MailOutlined, CloseOutlined } from '@ant-design/icons'; - -// sx styles -const avatarSX = { - width: 48, - height: 48 -}; - -const actionSX = { - mt: '6px', - ml: 1, - top: 'auto', - right: 'auto', - alignSelf: 'flex-start', - transform: 'none' -}; - -// ==============================|| HEADER CONTENT - MESSAGES ||============================== // - -const Message = () => { - const theme = useTheme(); - const matchesXs = useMediaQuery(theme.breakpoints.down('md')); - - const anchorRef = useRef(null); - const [open, setOpen] = useState(false); - const handleToggle = () => { - setOpen((prevOpen) => !prevOpen); - }; - - const handleClose = (event) => { - if (anchorRef.current && anchorRef.current.contains(event.target)) { - return; - } - setOpen(false); - }; - - const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300'; - const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100'; - - return ( - - - - - - {({ TransitionProps }) => ( - - - - - - - } - > - - - - - - - It's{' '} - - Cristina danny's - {' '} - birthday today. - - } - secondary="2 min ago" - /> - - - 3:00 AM - - - - - - - - - - - Aida Burg - {' '} - commented your post. - - } - secondary="5 August" - /> - - - 6:00 PM - - - - - - - - - - There was a failure to your setup. - - } - secondary="7 hours ago" - /> - - - 2:45 PM - - - - - - - - - - - Cristina Danny - {' '} - invited to join{' '} - - Meeting. - - - } - secondary="Daily scrum meeting time" - /> - - - 9:10 PM - - - - - - - View All - - } - /> - - - - - - - )} - - - ); -}; - -export default Message; diff --git a/src/layout/MainLayout/Header/HeaderContent/MobileSection.js b/src/layout/MainLayout/Header/HeaderContent/MobileSection.js deleted file mode 100644 index 82eab82..0000000 --- a/src/layout/MainLayout/Header/HeaderContent/MobileSection.js +++ /dev/null @@ -1,104 +0,0 @@ -import { useEffect, useRef, useState } from 'react'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { AppBar, Box, ClickAwayListener, Paper, Popper, Toolbar } from '@mui/material'; - -// project import -import Profile from './Profile'; -import IconButton from 'components/@extended/IconButton'; -import Transitions from 'components/@extended/Transitions'; -import { ThemeMode } from 'config'; - -// assets -import { MoreOutlined } from '@ant-design/icons'; - -// ==============================|| HEADER CONTENT - MOBILE ||============================== // - -const MobileSection = () => { - const theme = useTheme(); - - const [open, setOpen] = useState(false); - const anchorRef = useRef(null); - - const handleToggle = () => { - setOpen((prevOpen) => !prevOpen); - }; - - const handleClose = (event) => { - if (anchorRef.current && anchorRef.current.contains(event.target)) { - return; - } - - setOpen(false); - }; - - const prevOpen = useRef(open); - useEffect(() => { - if (prevOpen.current === true && open === false) { - anchorRef.current.focus(); - } - - prevOpen.current = open; - }, [open]); - - return ( - <> - - - - - - - {({ TransitionProps }) => ( - - - - - - {/* */} - - - - - - - )} - - - ); -}; - -export default MobileSection; diff --git a/src/layout/MainLayout/Header/HeaderContent/Notification.js b/src/layout/MainLayout/Header/HeaderContent/Notification.js deleted file mode 100644 index ddc7f8e..0000000 --- a/src/layout/MainLayout/Header/HeaderContent/Notification.js +++ /dev/null @@ -1,303 +0,0 @@ -import { useRef, useState } from 'react'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { - Avatar, - Badge, - Box, - ClickAwayListener, - Divider, - List, - ListItemButton, - ListItemAvatar, - ListItemText, - ListItemSecondaryAction, - Paper, - Popper, - Tooltip, - Typography, - useMediaQuery -} from '@mui/material'; - -// project import -import MainCard from 'components/MainCard'; -import IconButton from 'components/@extended/IconButton'; -import Transitions from 'components/@extended/Transitions'; -import { ThemeMode } from 'config'; - -// assets -import { BellOutlined, CheckCircleOutlined, GiftOutlined, MessageOutlined, SettingOutlined } from '@ant-design/icons'; - -// sx styles -const avatarSX = { - width: 36, - height: 36, - fontSize: '1rem' -}; - -const actionSX = { - mt: '6px', - ml: 1, - top: 'auto', - right: 'auto', - alignSelf: 'flex-start', - - transform: 'none' -}; - -// ==============================|| HEADER CONTENT - NOTIFICATION ||============================== // - -const Notification = () => { - const theme = useTheme(); - const matchesXs = useMediaQuery(theme.breakpoints.down('md')); - - const anchorRef = useRef(null); - const [read, setRead] = useState(0); - const [open, setOpen] = useState(false); - - const handleClose = (event) => { - if (anchorRef.current && anchorRef.current.contains(event.target)) { - return; - } - setOpen(false); - }; - - return ( - - - - - - - - - - {({ TransitionProps }) => ( - - - - - {read > 0 && ( - - setRead(0)}> - - - - )} - - } - > - - 0}> - - - - - - - It's{' '} - - Cristina danny's - {' '} - birthday today. - - } - secondary="2 min ago" - /> - - - 3:00 AM - - - - - 0}> - - - - - - - - Aida Burg - {' '} - commented your post. - - } - secondary="5 August" - /> - - - 6:00 PM - - - - - - - - - - - - Your Profile is Complete   - - 60% - {' '} - - } - secondary="7 hours ago" - /> - - - 2:45 PM - - - - - - - - C - - - - - Cristina Danny - {' '} - invited to join{' '} - - Meeting. - - - } - secondary="Daily scrum meeting time" - /> - - - 9:10 PM - - - - - - - View All - - } - /> - - - - - - - )} - - - ); -}; - -export default Notification; diff --git a/src/layout/MainLayout/Header/HeaderContent/Profile/ProfileTab.js b/src/layout/MainLayout/Header/HeaderContent/Profile/ProfileTab.js deleted file mode 100644 index 92d8b1d..0000000 --- a/src/layout/MainLayout/Header/HeaderContent/Profile/ProfileTab.js +++ /dev/null @@ -1,61 +0,0 @@ -import PropTypes from 'prop-types'; -import { useState } from 'react'; - -// material-ui -import { List, ListItemButton, ListItemIcon, ListItemText } from '@mui/material'; - -// assets -import { EditOutlined, LogoutOutlined, CommentOutlined } from '@ant-design/icons'; -import { useNavigate } from 'react-router'; - -// ==============================|| HEADER PROFILE - PROFILE TAB ||============================== // - -const ProfileTab = ({ handleLogout }) => { - const [selectedIndex, setSelectedIndex] = useState(0); - const navigate = useNavigate(); - const handleListItemClick = (event, index) => { - setSelectedIndex(index); - }; - - return ( - - { - handleListItemClick(event, 0); - navigate('/viewprofile'); - }} - > - - - - - - - handleListItemClick(event, 1)}> - - - - - - {/* handleListItemClick(event, 4)}> - - - - - */} - - - - - - - - ); -}; - -ProfileTab.propTypes = { - handleLogout: PropTypes.func -}; - -export default ProfileTab; diff --git a/src/layout/MainLayout/Header/HeaderContent/Profile/SettingTab.js b/src/layout/MainLayout/Header/HeaderContent/Profile/SettingTab.js deleted file mode 100644 index a656563..0000000 --- a/src/layout/MainLayout/Header/HeaderContent/Profile/SettingTab.js +++ /dev/null @@ -1,53 +0,0 @@ -import { useState } from 'react'; - -// material-ui -import { List, ListItemButton, ListItemIcon, ListItemText } from '@mui/material'; - -// assets -import { CommentOutlined, LockOutlined, QuestionCircleOutlined, UserOutlined, UnorderedListOutlined } from '@ant-design/icons'; - -// ==============================|| HEADER PROFILE - SETTING TAB ||============================== // - -const SettingTab = () => { - const [selectedIndex, setSelectedIndex] = useState(0); - const handleListItemClick = (event, index) => { - setSelectedIndex(index); - }; - - return ( - - handleListItemClick(event, 0)}> - - - - - - handleListItemClick(event, 1)}> - - - - - - handleListItemClick(event, 2)}> - - - - - - handleListItemClick(event, 3)}> - - - - - - handleListItemClick(event, 4)}> - - - - - - - ); -}; - -export default SettingTab; diff --git a/src/layout/MainLayout/Header/HeaderContent/Profile/index.js b/src/layout/MainLayout/Header/HeaderContent/Profile/index.js deleted file mode 100644 index f02bec4..0000000 --- a/src/layout/MainLayout/Header/HeaderContent/Profile/index.js +++ /dev/null @@ -1,211 +0,0 @@ -import PropTypes from 'prop-types'; -import { useRef, useState } from 'react'; -import { useQueryClient } from '@tanstack/react-query'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { Box, ButtonBase, CardContent, ClickAwayListener, Grid, Paper, Popper, Stack, Tab, Tabs, Tooltip, Typography } from '@mui/material'; - -// project import -import ProfileTab from './ProfileTab'; -import SettingTab from './SettingTab'; -import Avatar from 'components/@extended/Avatar'; -import MainCard from 'components/MainCard'; -import Transitions from 'components/@extended/Transitions'; -import IconButton from 'components/@extended/IconButton'; - -import { ThemeMode } from 'config'; -// import useAuth from 'hooks/useAuth'; - -// assets -import avatar1 from 'assets/images/users/avatar-1.png'; -import { LogoutOutlined, UserOutlined } from '@ant-design/icons'; -import { clearFcmToken } from 'store/reducers/fcmSlice'; -import { useDispatch } from 'react-redux'; -import { logoutUser } from 'store/reducers/loginUserSlice'; -import { performSessionLogout } from 'utils/session'; - -// tab panel wrapper -function TabPanel({ children, value, index, ...other }) { - return ( - - ); -} - -TabPanel.propTypes = { - children: PropTypes.node, - index: PropTypes.any.isRequired, - value: PropTypes.any.isRequired -}; - -function a11yProps(index) { - return { - id: `profile-tab-${index}`, - 'aria-controls': `profile-tabpanel-${index}` - }; -} - -// ==============================|| HEADER CONTENT - PROFILE ||============================== // - -const Profile = () => { - const theme = useTheme(); - const dispatch = useDispatch(); - const queryClient = useQueryClient(); - - const handleLogout = () => { - performSessionLogout({ queryClient, dispatch, clearFcmToken, logoutUser }); - }; - - const anchorRef = useRef(null); - const [open, setOpen] = useState(false); - const handleToggle = () => { - setOpen((prevOpen) => !prevOpen); - }; - - const handleClose = (event) => { - if (anchorRef.current && anchorRef.current.contains(event.target)) { - return; - } - setOpen(false); - }; - - const [value, setValue] = useState(0); - - const handleChange = (event, newValue) => { - setValue(newValue); - }; - - return ( - - - - - - {/* {user?.name} */} - - - - - {({ TransitionProps }) => ( - - - - - - - - - - - - {/* {user?.name} */} - {localStorage.getItem('firstname') || ''} - - - {/* UI/UX Designer */} - Partner - - - - - - - - - - - - - - - - - } - label="Profile" - {...a11yProps(0)} - /> - {/* } - label="Setting" - {...a11yProps(1)} - /> */} - - - - - - - - - - - - - )} - - - ); -}; - -export default Profile; diff --git a/src/layout/MainLayout/Header/HeaderContent/Search.js b/src/layout/MainLayout/Header/HeaderContent/Search.js deleted file mode 100644 index 4124c7e..0000000 --- a/src/layout/MainLayout/Header/HeaderContent/Search.js +++ /dev/null @@ -1,30 +0,0 @@ -// material-ui -import { Box, FormControl, InputAdornment, OutlinedInput } from '@mui/material'; - -// assets -import { SearchOutlined } from '@ant-design/icons'; - -// ==============================|| HEADER CONTENT - SEARCH ||============================== // - -const Search = () => ( - - - - - - } - aria-describedby="header-search-text" - inputProps={{ - 'aria-label': 'weight' - }} - placeholder="Ctrl + K" - /> - - -); - -export default Search; diff --git a/src/layout/MainLayout/Header/HeaderContent/index.js b/src/layout/MainLayout/Header/HeaderContent/index.js deleted file mode 100644 index a4e0a17..0000000 --- a/src/layout/MainLayout/Header/HeaderContent/index.js +++ /dev/null @@ -1,292 +0,0 @@ -import { useMemo, useState } from 'react'; - -// material-ui -import { - Box, - useMediaQuery, - Stack, - Tooltip, - IconButton, - Popper, - ClickAwayListener, - List, - ListItemButton, - ListItemText, - Grid, - ListItemIcon, - Typography -} from '@mui/material'; -import { TbBoxMultiple1 } from 'react-icons/tb'; -import { GrMultiple } from 'react-icons/gr'; -import { TbUserEdit } from 'react-icons/tb'; - -import Transitions from 'components/@extended/Transitions'; - -// project import -import Profile from './Profile'; -import Notification from './Notification'; -import { useNavigate } from 'react-router'; -import { - WindowsOutlined -} from '@ant-design/icons'; -import { useTheme } from '@mui/material/styles'; - -// ==============================|| HEADER - CONTENT ||============================== // - -const HeaderContent = () => { - const matchesXs = useMediaQuery((theme) => theme.breakpoints.down('md')); - - const navigate = useNavigate(); - const [open, setOpen] = useState(false); - const [anchorEl, setAnchorEl] = useState(null); - const theme = useTheme(); - - const handleToggle = (e) => { - setOpen(!open); - setAnchorEl(e.currentTarget); - }; - const handleClickAway = () => { - setOpen(false); - }; - - return ( - <> - {/* {!matchesXs && } */} - - {/* {!matchesXs && megaMenu} */} - - {localStorage.getItem('firstname') || ''} - - {matchesXs && } - - - - - - - - - - {/* */} - - - {/* */} - - { - // console.log(const location = useLocation();) - navigate('/nearle/orders/create'); - handleClickAway(); - }} - > - - - - - Create Order - - } - /> - - { - // console.log(const location = useLocation();) - navigate('/nearle/orders/createorders'); - handleClickAway(); - }} - > - - - - - Create Multiple Order - - } - /> - - { - navigate('/nearle/customer/create'); - handleClickAway(); - }} - > - - - - - Create Customer - - } - /> - - {/* { - navigate('/clients/create') - handleClickAway() - }} > - - - - - Create Client - - - } - /> - */} - {/* { - navigate('/riders/create') - handleClickAway() - }} > - - - - - Create Rider - - - } - /> - */} - - {/* - { - navigate('/create_order') - handleClickAway() - }}> - - - - - - - - - { - navigate('/create_client') - handleClickAway() - }}> - - - - - - - - - { - navigate('/create_staff') - handleClickAway() - }}> - - - - - - */} - - - {/* */} - - - - - - - - {/* */} - {/* {!matchesXs && } - {matchesXs && } */} - - - - - - - ); -}; - -export default HeaderContent; diff --git a/src/layout/MainLayout/Header/index.js b/src/layout/MainLayout/Header/index.js deleted file mode 100644 index 1fed5ba..0000000 --- a/src/layout/MainLayout/Header/index.js +++ /dev/null @@ -1,86 +0,0 @@ -import { useMemo } from 'react'; - -// material-ui -import { useTheme } from '@mui/material/styles'; -import { AppBar, Toolbar, useMediaQuery } from '@mui/material'; - -// project import -import AppBarStyled from './AppBarStyled'; -import HeaderContent from './HeaderContent'; -import IconButton from 'components/@extended/IconButton'; - -import { MenuOrientation, ThemeMode } from 'config'; -import useConfig from 'hooks/useConfig'; -import { dispatch, useSelector } from 'store'; -import { openDrawer } from 'store/reducers/menu'; - -// assets -import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'; - -// ==============================|| MAIN LAYOUT - HEADER ||============================== // - -const Header = () => { - const theme = useTheme(); - const downLG = useMediaQuery(theme.breakpoints.down('lg')); - const { menuOrientation } = useConfig(); - - const menu = useSelector((state) => state.menu); - const { drawerOpen } = menu; - - const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG; - - // header content - const headerContent = useMemo(() => , []); - - // common header - const mainHeader = ( - - {!isHorizontal ? ( - dispatch(openDrawer(!drawerOpen))} - edge="start" - // color="secondary" - // variant="light" - // sx={{ color: 'text.primary', bgcolor: drawerOpen ? iconBackColorOpen : iconBackColor, ml: { xs: 0, lg: -2 } }} - sx={{ color: '#fff', bgcolor: 'transparent', ml: { xs: 0, lg: -2 }, - fontSize:'25px', - ':hover':{ - color: '#fff', bgcolor: 'transparent' - } }} - > - {!drawerOpen ? : } - - ) : null} - {headerContent} - - ); - - // app-bar params - const appBar = { - position: 'fixed', - color: 'inherit', - elevation: 0, - sx: { - borderBottom: `1px solid ${theme.palette.divider}`, - zIndex: 1200, - width: isHorizontal ? '100%' : drawerOpen ? 'calc(100% - 260px)' : { xs: '100%', lg: 'calc(100% - 60px)' }, - // boxShadow: theme.customShadows.z1 - bgcolor:'#662582' - } - }; - - return ( - <> - {!downLG ? ( - - {mainHeader} - - ) : ( - {mainHeader} - )} - - ); -}; - -export default Header; diff --git a/src/layout/MainLayout/index.js b/src/layout/MainLayout/index.js index 23040ce..c9f88b7 100644 --- a/src/layout/MainLayout/index.js +++ b/src/layout/MainLayout/index.js @@ -1,72 +1,58 @@ -import { useEffect } from 'react'; -import { useSelector } from 'react-redux'; +import { useState } from 'react'; import { Outlet } from 'react-router-dom'; -// material-ui -import { useTheme } from '@mui/material/styles'; -import { useMediaQuery, Box, Container, Toolbar } from '@mui/material'; +// Astryx design system — see themes/astryx.js and CLAUDE.md's Astryx CLI +// section. This replaces the old MUI Drawer/Header/HorizontalBar shell; +// horizontal menu orientation (config.menuOrientation) was dropped since it +// was never exposed as a user-facing toggle in this app — see AppSideNav.js. +// AppShell config (variant/height/contentPadding/mobileNav breakpoint) and +// the lifted collapse state mirror doormile_crm/src/layout/MainLayout/index.jsx +// — that sibling Doormile app's validated Astryx shell. +import { AppShell } from '@astryxdesign/core/AppShell'; +import { Theme } from '@astryxdesign/core/theme'; +import { doormileTheme } from 'themes/astryx'; -// project import -import Drawer from './Drawer'; -import Header from './Header'; -import HorizontalBar from './Drawer/HorizontalBar'; - -import { MenuOrientation } from 'config'; -import useConfig from 'hooks/useConfig'; -import { dispatch } from 'store'; -import { openDrawer } from 'store/reducers/menu'; +import AppTopNav from './AppTopNav'; +import AppSideNav from './AppSideNav'; // ==============================|| MAIN LAYOUT ||============================== // const MainLayout = () => { - const theme = useTheme(); - const matchDownXL = useMediaQuery(theme.breakpoints.down('xl')); - const downLG = useMediaQuery(theme.breakpoints.down('lg')); - - const { drawerOpen } = useSelector((state) => state.menu); - const { container, miniDrawer, menuOrientation } = useConfig(); - - const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG; - - // set media wise responsive drawer - useEffect(() => { - if (!miniDrawer) { - // dispatch(openDrawer(!matchDownXL)); - dispatch(openDrawer(false)); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [matchDownXL]); + // Lifted here (rather than left as SideNav's own uncontrolled state) so the + // TopNav logo can track the sidebar's collapse state. Defaults to collapsed + // on every load/reload (not persisted). + const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(true); return ( - -
- {!isHorizontal ? : } - + {/* height="auto" (not doormile_crm's "fill"): "fill" scrolls the + content area in its own container, and CLAUDE.md's canonical + useInfiniteQuery pattern observes a sentinel against the window's + viewport (IntersectionObserver root=null), which only fires + correctly with normal document-level scroll. */} + } + sideNav={} + mobileNav={{ breakpoint: 'lg' }} > - - - {/* */} +
- {/*
*/} - - - +
+ +
+ ); }; diff --git a/src/menu-items/nearle.js b/src/menu-items/nearle.js index d1fdc9a..de29ca6 100644 --- a/src/menu-items/nearle.js +++ b/src/menu-items/nearle.js @@ -1,10 +1,5 @@ // third-party import { FormattedMessage } from 'react-intl'; -import DirectionsBikeOutlinedIcon from '@mui/icons-material/DirectionsBikeOutlined'; -import ReceiptOutlinedIcon from '@mui/icons-material/ReceiptOutlined'; -import MopedOutlinedIcon from '@mui/icons-material/MopedOutlined'; -import NearMeOutlinedIcon from '@mui/icons-material/NearMeOutlined'; -import { TbListDetails } from 'react-icons/tb'; // assets import { @@ -19,14 +14,15 @@ import { StopOutlined, DashboardOutlined, ClockCircleOutlined, - UserOutlined, SettingOutlined, - TeamOutlined, MailOutlined, ImportOutlined, BarChartOutlined, - MoneyCollectOutlined, - FileDoneOutlined + FileDoneOutlined, + PartitionOutlined, + CarOutlined, + UserOutlined, + ProfileOutlined } from '@ant-design/icons'; // icons @@ -42,24 +38,22 @@ const icons = { DeploymentUnitOutlined, DashboardOutlined, ClockCircleOutlined, - UserOutlined, SettingOutlined, - TeamOutlined, MailOutlined, ImportOutlined, BarChartOutlined, - ReceiptOutlinedIcon, - NearMeOutlinedIcon, - DirectionsBikeOutlinedIcon, - MopedOutlinedIcon, - FileDoneOutlined + FileDoneOutlined, + PartitionOutlined, + CarOutlined, + UserOutlined, + ProfileOutlined }; // ==============================|| MENU ITEMS - SUPPORT ||============================== // const nearle = { id: 'nearle_Pages', - title: , + title: , icon: icons.FileDoneOutlined, type: 'group', children: [ @@ -67,51 +61,29 @@ const nearle = { id: 'dispatch', title: , type: 'item', - url: '/nearle/dispatch', - icon: icons.DirectionsBikeOutlinedIcon + url: '/doormile/dispatch', + icon: icons.PartitionOutlined }, { id: 'orders', title: , type: 'item', - url: '/nearle/orders', + url: '/doormile/orders', icon: icons.DashboardOutlined }, - { id: 'deliveries', title: , type: 'item', - url: '/nearle/deliveries', - icon: MopedOutlinedIcon - }, - { - id: 'tenants', - title: , - type: 'item', - url: '/nearle/tenants', - icon: icons.UserOutlined - }, - { - id: 'pricing', - title: , - type: 'item', - url: '/nearle/pricing', - icon: MoneyCollectOutlined - }, - { - id: 'customers', - title: , - type: 'item', - url: '/nearle/customers', - icon: icons.TeamOutlined + url: '/doormile/deliveries', + icon: icons.CarOutlined }, { id: 'riders', title: , type: 'item', - url: '/nearle/riders', - icon: DirectionsBikeOutlinedIcon + url: '/doormile/riders', + icon: icons.UserOutlined }, { id: 'reports', @@ -123,48 +95,24 @@ const nearle = { id: 'reports', title: , type: 'item', - url: '/nearle/reports/orderssummary', - icon: TbListDetails + url: '/doormile/reports/orderssummary', + icon: icons.ProfileOutlined }, { id: 'ordersdetails', title: , type: 'item', - url: '/nearle/reports/ordersdetails', + url: '/doormile/reports/ordersdetails', icon: icons.DashboardOutlined - // target: true }, { id: 'riderssummary', title: , type: 'item', - url: '/nearle/reports/riderssummary', - icon: DirectionsBikeOutlinedIcon - // target: true - }, - { - id: 'riderslogs', - title: , - type: 'item', - url: '/nearle/reports/riderslogs', - icon: DirectionsBikeOutlinedIcon - // target: true - }, - { - id: 'profitability', - title: , - type: 'item', - url: '/nearle/reports/profitability', - icon: icons.BarChartOutlined + url: '/doormile/reports/riderssummary', + icon: icons.UserOutlined } ] - }, - { - id: 'invoice', - title: , - type: 'item', - url: '/nearle/invoice', - icon: icons.ReceiptOutlinedIcon } ] }; diff --git a/src/pages/api/api.js b/src/pages/api/api.js index 16726f3..e4b4961 100644 --- a/src/pages/api/api.js +++ b/src/pages/api/api.js @@ -233,7 +233,7 @@ export const notifyRider = async (riderToken) => { const response = await axios.post(`${process.env.REACT_APP_URL}/utils/notifyuser`, { token: riderToken, notification: { - title: 'NearleXpress', + title: 'DoormileXpress', body: 'Orders have been placed for delivery. Kindly accept and process deliveries', sound: 'ring', image: '' @@ -271,12 +271,9 @@ export const cancelMultipleOrder = async (orderlist) => { // ==============================|| fetchDeliveries (deliveries) ||============================== // export const fetchDeliveries = async ({ pageParam = 1, queryKey }) => { - let [, appId, userid, currentStatus, startdate, enddate, rowsPerPage, searchword, tenantid, locationid, riderid] = queryKey; + let [, appId, , currentStatus, startdate, enddate, rowsPerPage, searchword, tenantid, locationid, riderid] = queryKey; currentStatus = currentStatus == 'All' ? 'all' : currentStatus; - const url = - appId === 0 - ? `${process.env.REACT_APP_URL}/deliveries/getdeliveries/?appuserid=${userid}&status=${currentStatus}&fromdate=${startdate}&todate=${enddate}&pageno=${pageParam}&pagesize=${rowsPerPage}&keyword=${searchword}&tenantid=${tenantid}&locationid=${locationid}&userid=${riderid}` - : `${process.env.REACT_APP_URL}/deliveries/getdeliveries/?applocationid=${appId}&status=${currentStatus}&fromdate=${startdate}&todate=${enddate}&pageno=${pageParam}&pagesize=${rowsPerPage}&keyword=${searchword}&tenantid=${tenantid}&locationid=${locationid}&userid=${riderid}`; + const url = `${process.env.REACT_APP_URL}/deliveries/getdeliveries/?applocationid=${appId}&status=${currentStatus}&fromdate=${startdate}&todate=${enddate}&pageno=${pageParam}&pagesize=${rowsPerPage}&keyword=${searchword}&tenantid=${tenantid}&locationid=${locationid}&userid=${riderid}`; const response = await axios.get(url); return { @@ -311,10 +308,7 @@ export const fetchPercentageAPI = async (appId) => { // ==============================|| fetchCountAPI (deliveries) ||============================== // export const fetchCountAPI = async (appId, userid, startdate, enddate, rowsPerPage, debouncedSearch, tenantid, locationid, riderid) => { - const url = - appId == 0 - ? `${process.env.REACT_APP_URL}/deliveries/deliverysummary/?appuserid=${userid}&fromdate=${startdate}&todate=${enddate}` - : `${process.env.REACT_APP_URL}/deliveries/deliverysummary/?applocationid=${appId}&fromdate=${startdate}&todate=${enddate}&tenantid=${tenantid}&locationid=${locationid}&userid=${riderid}`; + const url = `${process.env.REACT_APP_URL}/deliveries/deliverysummary/?applocationid=${appId}&fromdate=${startdate}&todate=${enddate}&tenantid=${tenantid}&locationid=${locationid}&userid=${riderid}`; const response = await axios.get(url); const data = response.data.details; return { @@ -369,103 +363,6 @@ export const updateDeliveryAPI = async (orderData) => { return axios.put(`${process.env.REACT_APP_URL}/deliveries/updatedelivery`, orderData); }; -// ==============================|| getalltenants (tenants) ||============================== // - -export const getalltenants = async ({ queryKey }) => { - const [, appId, debouncedSearch, status, page, rowsPerPage] = queryKey; - try { - let url = `${process.env.REACT_APP_URL - }/tenants/getalltenants/?status=${status}&applocationid=${appId}&keyword=${debouncedSearch}&pageno=${page + 1 - }&pagesize=${rowsPerPage}&moduleid=6`; - const response = await axios.get(url); - return response.data.details; // return only data, keep it clean - } catch (err) { - const message = err.response?.data?.message || err.message || 'Something went wrong'; - OpenToast(message); - return null; // return null for failure - } -}; -// ==============================|| gettenantsummary (tenants) ||============================== // - -export const gettenantsummary = async ({ queryKey }) => { - const [, appId] = queryKey; - try { - const response = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantsummary/?moduleid=6&applocationid=${appId}`); - return response.data.summary; // return only data, keep it clean - } catch (err) { - const message = err.response?.data?.message || err.message || 'Something went wrong'; - OpenToast(message); - return null; // return null for failure - } -}; -// ==============================|| getpricinglist (tenants) ||============================== // - -export const getpricinglist = async ({ queryKey }) => { - const [, appId] = queryKey; - try { - const response = await axios.get(`${process.env.REACT_APP_URL}/tenants/getpricinglist/?moduleid=6&applocationid=${appId}`); - return response.data.summary; // return only data, keep it clean - } catch (err) { - const message = err.response?.data?.message || err.message || 'Something went wrong'; - OpenToast(message); - return null; // return null for failure - } -}; -// ==============================|| getallpricing (clientPricing) ||============================== // - -export const getallpricing = async ({ queryKey }) => { - const [, appId] = queryKey; - try { - const response = await axios.get(`${process.env.REACT_APP_URL}/utils/getallpricing/?applocationid=${appId}`); - return response.data.details || []; - } catch (err) { - const message = err.response?.data?.message || err.message || 'Something went wrong'; - OpenToast(message); - return []; - } -}; - -// ==============================|| getcustomersummary (customers) ||============================== // - -export const getcustomersummary = async ({ queryKey }) => { - const [, appId] = queryKey; - try { - const response = await axios.get(`${process.env.REACT_APP_URL}/customers/getcustomersummary?applocationid=${appId}`); - return response.data.summary; - } catch (err) { - const message = err.response?.data?.message || err.message || 'Something went wrong'; - OpenToast(message); - return null; // return null for failure - } -}; - -// ==============================|| getallcustomers (customers) ||============================== // - -export const getallcustomers = async ({ pageParam = 1, queryKey }) => { - const [, appId, debouncedSearch, rowsPerPage] = queryKey; - - try { - const response = await axios.get(`${process.env.REACT_APP_URL}/customers/getallcustomers/`, { - params: { - applocationid: appId, - keyword: debouncedSearch, - pageno: pageParam, - pagesize: rowsPerPage - } - }); - - return { - data: response.data.details || [], - nextPage: response.data.details?.length === rowsPerPage ? pageParam + 1 : undefined - }; - } catch (err) { - const message = err.response?.data?.message || err.message || 'Something went wrong'; - - OpenToast(message); - throw err; // IMPORTANT for React Query - } -}; - // ==============================|| fetchAllRiders (riders) ||============================== // export const fetchAllRiders = async ({ pageParam = 1, queryKey }) => { try { @@ -554,10 +451,7 @@ export const fetchorderdetails = async ({ queryKey }) => { const [appId, startdate, enddate, page, rowsPerPage] = queryKey; const response = await axios.get( - appId == 0 - ? `${process.env.REACT_APP_URL2}/orders/getorders/?appuserid=${userid}&fromdate=${startdate}&todate=${enddate}&pageno=${page + 1 - }&pagesize=${rowsPerPage}` - : `${process.env.REACT_APP_URL2}/orders/getorders/?fromdate=${startdate}&todate=${enddate}&applocationid=${appId}&pageno=${page}&pagesize=${rowsPerPage}` + `${process.env.REACT_APP_URL2}/orders/getorders/?fromdate=${startdate}&todate=${enddate}&applocationid=${appId}&pageno=${page}&pagesize=${rowsPerPage}` ); const detailsWithSNo = response.data.details.map((item, index) => ({ ...item, @@ -621,21 +515,6 @@ export const fetchLocations = async () => { return updatedLocations; }; -// ==============================|| fetchinvoiceinsight (Invoice)||============================== // - -export const fetchinvoiceinsight = async () => { - const insightResponse = await axios.get(`${process.env.REACT_APP_URL}/invoice/getinvoiceinsight`); - return insightResponse.data.details; -}; - -// ==============================|| fetchdeliverylist (Invoice)||============================== // - -export const fetchdeliverylist = async ({ queryKey }) => { - const [billStatus] = queryKey; - const deliveyResponse = await axios.get(`${process.env.REACT_APP_URL}/invoice/getallinvoice/?billstatus=${billStatus}`); - console.log('fetchdeliverylist', deliveyResponse.data.details); - return deliveyResponse.data.details; -}; // ==============================|| fetchRidersLogs (RiderLogs)||============================== // export const fetchRidersLogs = async ({ queryKey }) => { diff --git a/src/pages/auth/code-verification.js b/src/pages/auth/code-verification.js deleted file mode 100644 index 8a8098d..0000000 --- a/src/pages/auth/code-verification.js +++ /dev/null @@ -1,29 +0,0 @@ -// material-ui -import { Grid, Stack, Typography } from '@mui/material'; - -// project import -import AuthWrapper from 'sections/auth/AuthWrapper'; -import AuthCodeVerification from 'sections/auth/auth-forms/AuthCodeVerification'; - -// ================================|| CODE VERIFICATION ||================================ // - -const CodeVerification = () => ( - - - - - Enter Verification Code - We send you on mail. - - - - We`ve send you code on jone. ****@company.com - - - - - - -); - -export default CodeVerification; diff --git a/src/pages/nearle/clientPricing/clientPricing.js b/src/pages/nearle/clientPricing/clientPricing.js deleted file mode 100644 index 46b75f3..0000000 --- a/src/pages/nearle/clientPricing/clientPricing.js +++ /dev/null @@ -1,602 +0,0 @@ -import React, { useMemo, useRef, useState } from 'react'; -import { - Avatar, - Box, - Grid, - Paper, - Stack, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Typography, - useMediaQuery, - useTheme -} from '@mui/material'; -import { - MdLocalOffer, - MdMyLocation, - MdAttachMoney, - MdGroups, - MdPlace, - MdSpeed, - MdPriceCheck, - MdStraighten, - MdReceiptLong, - MdOutlineLocalOffer, - MdOutlineGroups, - MdOutlineAttachMoney, - MdOutlinePlace -} from 'react-icons/md'; -import { useQuery } from '@tanstack/react-query'; - -import Loader from 'components/Loader'; -import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar'; -import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete'; -import PageHeader from 'components/nearle_components/PageHeader'; -import StatCard from 'components/nearle_components/StatCard'; -import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; -import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton'; -import { getallpricing } from 'pages/api/api'; - -// ============================================================================ -// Design tokens — shared with the deliveries / tenants / customers pages so every -// surface (header, KPI tiles, table, badges) speaks the same visual language. -// Keep this block in sync with customers.js / deliveries.js. -// ============================================================================ -const DT = { - radiusPill: 999, - radiusCard: 14, - radiusField: 10, - shadowSoft: '0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.05)', - shadowMd: '0 1px 3px rgba(15, 23, 42, 0.06)', - shadowPop: '0 12px 32px rgba(15, 23, 42, 0.12)', - textPrimary: '#0f172a', - textSecondary: '#64748b', - textMuted: '#94a3b8', - borderSubtle: '#e2e8f0', - borderHover: '#cbd5e1', - divider: '#f1f5f9', - surface: '#ffffff', - surfaceAlt: '#f8fafc', - brand: '#662582' -}; -const a = (c, suffix) => `${c}${suffix}`; -const tint = (c) => a(c, '08'); -const soft = (c) => a(c, '18'); -const ring = (c) => a(c, '26'); -const edge = (c) => a(c, '55'); - -const BRAND = '#662582'; - -const SoftPaper = (props) => ( - -); - -const AccentAvatar = ({ color, selected, size = 24, children }) => ( - - {children} - -); - -const formatRupees = (value) => - new Intl.NumberFormat('en-IN', { - style: 'currency', - currency: 'INR', - minimumFractionDigits: 2 - }).format(Number(value) || 0); - -const formatDecimal = (value) => - new Intl.NumberFormat('en-IN', { minimumFractionDigits: 2 }).format(Number(value) || 0); - -// Numeric table value — plain, strong, right-readable text. The old version -// wrapped every cell in a coloured bordered pill, which made the table read -// like a rainbow; corporate data tables keep figures as quiet typography and -// let the column header carry the meaning. -const MetricPill = ({ label }) => ( - - {label} - -); - -// Subtle neutral category chip (zone / slab) — one quiet style, muted icon. -const CategoryChip = ({ icon, label }) => ( - - - {icon} - - {label} - -); - -// ==============================|| Pricing page ||============================== // - -const ClientsPricing = () => { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('md')); - const containerRef = useRef(); - const [appId, setAppId] = useState(0); - const [locaName, setLocoName] = useState('All'); - const [searchword, setSearchword] = useState(''); - const [debouncedSearch, setDebouncedSearch] = useState(''); - - const { - data: pricing = [], - isLoading - } = useQuery({ - queryKey: ['getallpricing', appId], - queryFn: getallpricing, - keepPreviousData: true - }); - - const rows = useMemo(() => { - if (!debouncedSearch) return pricing; - const q = debouncedSearch.toLowerCase().trim(); - return pricing.filter((row) => - [row.applocation, row.appname, row.slab, String(row.pricingid)] - .filter(Boolean) - .some((field) => String(field).toLowerCase().includes(q)) - ); - }, [pricing, debouncedSearch]); - - const stats = useMemo(() => { - const total = pricing.length; - const tenants = new Set(pricing.map((r) => r.appname).filter(Boolean)).size; - const avgBase = total - ? pricing.reduce((sum, r) => sum + (Number(r.baseprice) || 0), 0) / total - : 0; - return { total, tenants, avgBase }; - }, [pricing]); - - const KPI_META = [ - { key: 'total', label: 'Total Pricing Slabs', color: BRAND, icon: MdOutlineLocalOffer, value: stats.total }, - { key: 'tenants', label: 'Tenants Priced', color: '#0ea5e9', icon: MdOutlineGroups, value: stats.tenants }, - { key: 'avg', label: 'Avg Base Price', color: '#f59e0b', icon: MdOutlineAttachMoney, value: formatRupees(stats.avgBase) }, - { key: 'zone', label: 'Active Zone', color: '#10b981', icon: MdOutlinePlace, value: locaName || 'All Zones' } - ]; - - return ( - <> - {isLoading && } - - {/* ============================================= || Header || ============================================= */} - } - placeholder="Select Zone" - paperComponent={SoftPaper} - sx={{ width: { xs: '100%', sm: 280 }, zIndex: 100 }} - /> - } - /> - - {/* ============================================= || KPI Cards || ============================================= */} - - {KPI_META.map((item) => { - const Icon = item.icon; - return ( - - } color={item.color} /> - - ); - })} - - - {/* ============================================= || Search Header || ============================================= */} - - - - - - - - - Pricing Catalog - - - {pricing.length} total · {rows.length} shown - - - - - - - - - - {/* ============================================= || Table || ============================================= */} - - {isMobile ? ( - rows.length === 0 && !isLoading ? ( - - - - - - No pricing to show - - - {searchword ? 'Try a different keyword.' : 'Pick a zone above to load the catalog.'} - - - ) : ( - - {rows.map((row, index) => ( - - - - - - - - {row.appname || '—'} - - - ID #{row.pricingid} - - - - - {String(index + 1).padStart(2, '0')} - - - } - > - - {row.applocation ? ( - - {row.applocation} - - ) : null} - - {row.slab || '—'} - - - - - - } - label={formatRupees(row.baseprice)} - /> - - - } - label={formatRupees(row.priceperkm)} - /> - - - } - label={`${formatDecimal(row.minkm)} km`} - /> - - - } - label={`${formatDecimal(row.maxkm)} km`} - /> - - - - - - {row.minorder ?? '—'} - - - - - - ))} - - ) - ) : ( - - - - - # - Tenant - Zone - Slab - Base Price - Min KM - Price / KM - Max KM - Min Orders - - - - - {isLoading && } - {rows.length === 0 && !isLoading ? ( - - - - - - - - No pricing to show - - - {searchword ? 'Try a different keyword.' : 'Pick a zone above to load the catalog.'} - - - - - ) : ( - rows.map((row, index) => ( - - - - {String(index + 1).padStart(2, '0')} - - - - - - - - - - - {row.appname || '—'} - - - ID #{row.pricingid} - - - - - - - {row.applocation ? ( - } label={row.applocation} /> - ) : ( - - )} - - - - } label={row.slab || '—'} /> - - - - } - label={formatRupees(row.baseprice)} - width={110} - /> - - - - } - label={`${formatDecimal(row.minkm)} km`} - width={90} - /> - - - - } - label={formatRupees(row.priceperkm)} - width={110} - /> - - - - } - label={`${formatDecimal(row.maxkm)} km`} - width={90} - /> - - - - - - - {row.minorder ?? '—'} - - - - - )) - )} - -
-
- )} -
- - ); -}; - -export default ClientsPricing; diff --git a/src/pages/nearle/clients/Tenants.js b/src/pages/nearle/clients/Tenants.js deleted file mode 100644 index b9bd26d..0000000 --- a/src/pages/nearle/clients/Tenants.js +++ /dev/null @@ -1,2265 +0,0 @@ -import React, { useEffect, useState, useRef, useMemo, Fragment } from 'react'; -import MainCard from 'components/MainCard'; -import axios from 'axios'; -import { useTheme } from '@mui/material/styles'; -import Loader from 'components/Loader'; -import { Empty } from 'antd'; -import dayjs from 'dayjs'; -import { enqueueSnackbar } from 'notistack'; -import Geocode from 'react-geocode'; -import LocationOnIcon from '@mui/icons-material/LocationOn'; -import parse from 'autosuggest-highlight/parse'; -import { debounce } from '@mui/material/utils'; -import { - Stack, - Table, - TableCell, - TableContainer, - TableHead, - TableRow, - TableBody, - Typography, - IconButton, - Box, - Grid, - Autocomplete, - TextField, - Tooltip, - Collapse, - Tab, - Tabs, - Divider, - Card, - CardContent, - CardActions, - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - FormLabel, - TablePagination, - Skeleton, - Avatar, - Paper, - useMediaQuery -} from '@mui/material'; -import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; -import { - EyeOutlined, - EyeInvisibleOutlined, - EditOutlined, - IssuesCloseOutlined, - StopOutlined, - CloseOutlined -} from '@ant-design/icons'; -import { PopupTransition } from 'components/@extended/Transitions'; -import { FaRegCheckCircle, FaRegAddressCard } from 'react-icons/fa'; -import { TbBrandDatabricks, TbWorldLongitude, TbWorldLatitude } from 'react-icons/tb'; -import { GiMoneyStack, GiModernCity } from 'react-icons/gi'; -import { FiUser, FiPhoneCall } from 'react-icons/fi'; -import { - MdNumbers, - MdGroups, - MdCheckCircle, - MdHourglassEmpty, - MdCancel, - MdOutlineCheckCircle, - MdOutlinePendingActions, - MdOutlineCancel, - MdMyLocation, - MdPersonPin -} from 'react-icons/md'; -import { LuMail } from 'react-icons/lu'; -import { BiUser } from 'react-icons/bi'; -import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete'; -import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar'; -import PageHeader from 'components/nearle_components/PageHeader'; -import StatCard from 'components/nearle_components/StatCard'; -import { useQuery } from '@tanstack/react-query'; -import { getalltenants, gettenantsummary } from 'pages/api/api'; -import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton'; - -// ============================================================================ -// Design tokens — shared with the deliveries page so every surface (header, -// filters, KPI tiles, tabs, badges, dialogs) speaks the same visual language. -// ============================================================================ -const DT = { - radiusPill: 999, - radiusCard: 14, - radiusInner: 12, - shadowSoft: '0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.05)', - shadowMd: '0 1px 3px rgba(15, 23, 42, 0.06)', - shadowPop: '0 12px 32px rgba(15, 23, 42, 0.12)', - textPrimary: '#0f172a', - textSecondary: '#64748b', - textMuted: '#94a3b8', - borderSubtle: '#e2e8f0', - divider: '#f1f5f9', - surface: '#ffffff', - surfaceAlt: '#f8fafc' -}; - -const a = (c, suffix) => `${c}${suffix}`; -const tint = (c) => a(c, '08'); -const soft = (c) => a(c, '18'); -const ring = (c) => a(c, '26'); -const edge = (c) => a(c, '55'); - -const pillFieldSx = (color) => ({ - cursor: 'pointer', - '& .MuiOutlinedInput-root': { - borderRadius: '10px', - bgcolor: '#ffffff', - fontWeight: 600, - color: DT.textPrimary, - paddingRight: '8px', - cursor: 'pointer', - transition: 'border-color 0.15s, box-shadow 0.15s, background-color 0.2s', - '& fieldset': { borderColor: '#e2e8f0', borderWidth: 1 }, - '&:hover fieldset': { borderColor: '#cbd5e1' }, - '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring(color)}` }, - '&.Mui-focused fieldset': { borderColor: '#662582', borderWidth: 1.5 } - }, - '& .MuiAutocomplete-endAdornment .MuiSvgIcon-root': { color: color } -}); - -// Status palette — drives the pill tabs and per-row badges. -const STATUS_META = { - active: { label: 'Active', color: '#10b981', icon: MdCheckCircle, statusKey: 'active' }, - pending: { label: 'Pending', color: '#f59e0b', icon: MdHourglassEmpty, statusKey: 'pending' }, - inactive: { label: 'Inactive', color: '#ef4444', icon: MdCancel, statusKey: 'inactive' } -}; - -const STATUS_TABS = [ - { status: 'active', countKey: 'active', tabStatus: 'Active' }, - { status: 'pending', countKey: 'pending', tabStatus: 'Pending' }, - { status: 'inactive', countKey: 'inactive', tabStatus: 'InActive' } -]; - -const KPI_META = [ - { key: 'active', label: 'Active Tenants', color: '#10b981', icon: MdOutlineCheckCircle, countKey: 'active' }, - { key: 'pending', label: 'Pending Approval', color: '#f59e0b', icon: MdOutlinePendingActions, countKey: 'pending' }, - { key: 'inactive', label: 'Inactive Tenants', color: '#ef4444', icon: MdOutlineCancel, countKey: 'inactive' } -]; - -const SoftPaper = (props) => ( - -); - -const AccentAvatar = ({ color, selected, size = 24, children }) => ( - - {children} - -); - -// ==============================|| Starts here||============================== // -const Clients1 = () => { - const textFieldRef = useRef(null); - const [searchword, setSearchword] = useState(''); - const [debouncedSearch, setDebouncedSearch] = useState(''); - const [rowsPerPage, setRowsPerPage] = React.useState(10); - // const [tenantList, settenantList] = useState([]); - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('md')); - const [isloader, setisloader] = useState(false); - const [appId, setAppId] = useState(0); - const [locaName, setLocoName] = useState(''); - const [locations] = useState('All'); - const [value, setValue] = React.useState(null); - const [value0, setValue0] = useState(0); - const [value1, setValue1] = useState(0); - const [value2, setValue2] = useState(0); - const [selectedTenid, setSelectedtenid] = useState(null); - const [clientpricelist, setClientpricelist] = useState([]); - const [status, setstatus] = useState('active'); - const [selectedCustomer, setSelectedCustomer] = useState({}); // to edit - const [dialogopen, setDialogopen] = useState(false); - const [appPricing, setAppPricing] = useState([]); - const [selectedPricing, setSelectedPricing] = useState({}); - const [isPrice, setIsprice] = useState(true); - const [address, setAddress] = useState(''); - const [city, setCity] = useState(''); - const [zipcode, setZipcode] = useState(''); - const [latlong, setLatlong] = useState({}); - const [editClient, setEditClient] = useState({}); - const [page, setPage] = useState(0); - const [tabStatus, setTabStatus] = useState('Active'); - - const handleChangePage = (event, newPage) => { - setPage(newPage); - }; - - const handleChangeRowsPerPage = (event) => { - setRowsPerPage(parseInt(event.target.value, 10)); - setPage(0); - }; - const dialogclose = () => { - setDialogopen(false); - }; - - const handleChange = (event, newValue) => { - setTabStatus(newValue == 0 ? 'Active' : newValue == 1 ? 'Pending' : 'InActive'); - setValue0(newValue); - setSearchword(''); - setPage(0); - }; - const handleChange1 = (event, newValue1) => { - console.log('newValue1', newValue1); - setValue1(newValue1); - }; - const handleChange2 = (event, newValue2) => { - console.log('newValue2', newValue2); - setValue2(newValue2); - }; - - const [openRowIndex1, setOpenRowIndex1] = useState(null); // Initially no row is open for collapsible section 1 - const [openRowIndex2, setOpenRowIndex2] = useState(null); // Initially no row is open for collapsible section 2 - // Handle toggling for collapsible section 1 - const handleCollapseToggle1 = (rowIndex) => { - setOpenRowIndex1((prevIndex) => (prevIndex === rowIndex ? null : rowIndex)); - }; - // Handle toggling for collapsible section 2 - const handleCollapseToggle2 = (rowIndex) => { - setOpenRowIndex2((prevIndex) => (prevIndex === rowIndex ? null : rowIndex)); - }; - useEffect(() => { - console.log('selectedCustomer', selectedCustomer); - }, [selectedCustomer]); - useEffect(() => { - setOpenRowIndex1(-1); - setOpenRowIndex2(-1); - }, [value0]); - - /* ============================================= || opentoast || ============================================= */ - - const opentoast = (message, variant, duration) => { - enqueueSnackbar(message, { - variant: variant, - anchorOrigin: { vertical: 'top', horizontal: 'right' }, - autoHideDuration: duration - }); - }; - // ==============================|| google address ||============================== // - const GOOGLE_MAPS_API_KEY = process.env.REACT_APP_GOOGLE_MAPS_API_KEY; - - function loadScript(src, position, id) { - if (!position) { - return; - } - - const script = document.createElement('script'); - script.setAttribute('async', ''); - script.setAttribute('id', id); - script.src = src; - position.appendChild(script); - } - - const autocompleteService = { current: null }; - // ==============================|| for google address ||============================== // - const [inputValue, setInputValue] = useState(''); - const [options, setOptions] = useState([]); - const loaded = useRef(false); - if (typeof window !== 'undefined' && !loaded.current) { - if (!document.querySelector('#google-maps')) { - loadScript( - `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API_KEY}&libraries=places`, - document.querySelector('head'), - 'google-maps' - ); - } - - loaded.current = true; - } - - const fetch = useMemo( - () => - debounce((request, callback) => { - autocompleteService.current.getPlacePredictions(request, callback); - }, 400), - [] - ); - useEffect(() => { - let active = true; - if (!autocompleteService.current && window.google) { - autocompleteService.current = new window.google.maps.places.AutocompleteService(); - } - if (!autocompleteService.current) { - return undefined; - } - - if (inputValue === '') { - setOptions(value ? [value] : []); - return undefined; - } - fetch({ input: inputValue }, (results) => { - if (active) { - let newOptions = []; - - if (value) { - newOptions = [value]; - } - - if (results) { - newOptions = [...newOptions, ...results]; - } - setOptions(newOptions); - } - }); - - return () => { - active = false; - }; - }, [value, inputValue, fetch]); - - Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY); - useEffect(() => { - try { - console.log('selected address =>', address); - Geocode.fromAddress(address).then( - (response) => { - console.log('lat long response =>', response.results[0]); - if (response.status == 'OK') { - const { lat, lng } = response.results[0].geometry.location; - setLatlong({ - lat, - lng - }); - setEditClient({ - ...editClient, - latitude: lat.toString(), - longitude: lng.toString() - }); - // setSelectedCustomer({ - // ...selectedCustomer, - // latitude: lat, - // longitude: lng, - // }); - if (response.results[0].address_components) { - let place = response.results[0]; - let city1, zipcode1, state1, suburb1; - for (let i = 0; i < place.address_components.length; i++) { - for (let j = 0; j < place.address_components[i].types.length; j++) { - switch (place.address_components[i].types[j]) { - case 'locality': - city1 = place.address_components[i].long_name; - break; - case 'administrative_area_level_1': - state1 = place.address_components[i].long_name; - break; - case 'postal_code': - zipcode1 = place.address_components[i].long_name; - break; - case 'sublocality': - suburb1 = place.address_components[i].long_name; - break; - } - } - } - setCity(city1 || ''); - setState(state1 || ''); - setZipcode(zipcode1 || ''); - setSuburb(suburb1 || ''); - setEditClient({ - ...editClient, - city: city1 || '', - state: state1 || '', - postcode: zipcode || '', - suburb: suburb1 || '' - }); - } - } - }, - (error) => { - console.log(error); - } - ); - } catch (err) { - console.log(err); - } - }, [address]); - - useEffect(() => { - selectedCustomer && - setLatlong({ - lat: selectedCustomer.latitude, - lng: selectedCustomer.longitude - }); - }, [selectedCustomer]); - /* ============================================= || handleKeyPress (ctrl+k)| ============================================= */ - useEffect(() => { - const handleKeyPress = (event) => { - if (event.key === 'k' && (event.metaKey || event.ctrlKey)) { - event.preventDefault(); - - textFieldRef.current.focus(); - } - if (event.key === 'Escape' && document.activeElement === textFieldRef.current) { - // Remove focus from the TextField - textFieldRef.current.blur(); - } - }; - document.addEventListener('keydown', handleKeyPress); - - return () => { - document.removeEventListener('keydown', handleKeyPress); - }; - }, []); - - /* ============================================= || getalltenants| ============================================= */ - - const { - data: tenantList, - isLoading: getalltenantsIsLoading, - refetch: getalltenantsRefetch - } = useQuery({ - queryKey: ['tenantList', appId, debouncedSearch, status, page, rowsPerPage], - queryFn: getalltenants - }); - - // ==============================|| gettenantsummary ||============================== // - const { - data: summaryData = [], - isLoading: summaryDataIsLoading, - refetch: summaryDataRefetch - } = useQuery({ - queryKey: ['gettenantsummary', appId, status], - queryFn: gettenantsummary - }); - - /* ============================================= || fetchclientpricelist || ============================================= */ - - const fetchclientpricelist = async () => { - await axios - .get(`${process.env.REACT_APP_URL}/tenants/getpricinglist/?tenantid=${selectedTenid}`) - .then((res) => { - console.log('getpricinglist', res); - if (res.data.status) { - setClientpricelist(res.data.details); - } - }) - .catch((err) => { - console.log(err); - }); - }; - useEffect(() => { - selectedTenid && fetchclientpricelist(); - }, [selectedTenid]); - - /* ============================================= || fetchTenanatPricing || ============================================= */ - const fetchTenanatPricing = async (id) => { - try { - let tenantPricing = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantpricing/?tenantid=${id}`); - console.log('tenantPricing', tenantPricing.data.details); - setTenanatPricing(tenantPricing.data.details); - } catch (error) { - console.log('fetchTenanatPricing', error); - } - }; - useEffect(() => { - selectedTenid && fetchTenanatPricing(selectedTenid); - }, [selectedTenid]); - /* ============================================= || tenantupdate || ============================================= */ - - const tenantupdate = async (tenid) => { - setisloader(true); - let updateData; - if (tenid == -1) { - updateData = { - tenantid: selectedTenid, - approved: 1 - }; - } else { - updateData = { tenantid: tenid, status: status === 'active' ? 'InActive' : 'Active' }; - } - - await axios - .put(`${process.env.REACT_APP_URL}/tenants/update`, updateData) - .then((res) => { - if (res.data.status) { - opentoast( - value0 == 0 ? 'Inactivated Successfully' : value0 == 1 ? 'Approved Successfully' : 'Activate Successfully', - 'success', - 2000 - ); - getalltenantsRefetch(); - summaryDataRefetch(); - setisloader(false); - } - }) - .catch((err) => { - console.log(err); - opentoast(err.message, 'error', 1500); - setisloader(false); - }); - // } - }; - /* ============================================= || getAppPricing || ============================================= */ - const getAppPricing = async (id) => { - console.log('id', id); - try { - let appPricingRes = await axios.get(`${process.env.REACT_APP_URL}/utils/getapppricing/?applocationid=${id}`); - console.log('appPricingRes', appPricingRes.data.details); - setAppPricing(appPricingRes.data.details); - } catch (error) { - console.log('appPricingRes', error); - } - }; - /* ============================================= || createpricing || ============================================= */ - const createpricing = async () => { - setisloader(true); - await axios - .post(`${process.env.REACT_APP_URL}/tenants/createpricing`, { - tenantpricingid: 0, - applocationid: appId, - pricingid: selectedPricing.pricingid, - tenantid: selectedTenid, - pricingdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), - configid: selectedPricing.configid, - pricingtypeid: selectedPricing.pricingtypeid, - slab: selectedPricing.slab, - baseprice: +selectedPricing.baseprice, - priceperkm: +selectedPricing.priceperkm, - minkm: +selectedPricing.minkm, - maxkm: +selectedPricing.maxkm, - orders: +selectedPricing.minorder, - othercharges: 0 - }) - .then((res) => { - if (res.data.status) { - enqueueSnackbar('Price Created Successfully', { - variant: 'success', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, - autoHideDuration: 2000 - }); - dialogclose(); - setSelectedPricing({}); - tenantupdate(-1); - fetchclientpricelist(); - } - }) - .catch((err) => { - console.log(err); - }); - }; - /* ============================================= || updateClient || ============================================= */ - const updateClient = async () => { - try { - const arr = { ...editClient, tenantid: selectedTenid }; - - console.log('updateClient', arr); - const updateRes = await axios.put(`${process.env.REACT_APP_URL}/tenants/update`, arr); - console.log('updateClient', updateRes.data.message); - if (updateRes.data.status) { - opentoast(updateRes.data.message, 'success', 1500); - } - getalltenantsRefetch(); - summaryDataRefetch(); - } catch (err) { - console.log('updateClient', err); - } - }; - - const activeTabMeta = STATUS_META[STATUS_TABS[value0]?.status] || STATUS_META.active; - - return ( - <> - {(getalltenantsIsLoading || summaryDataIsLoading || isloader) && } - {/* ============================================= || Header | ============================================= */} - } - placeholder="Select Zone" - paperComponent={SoftPaper} - sx={{ width: { xs: '100%', sm: 280 }, zIndex: 100 }} - /> - } - /> - - {/* ============================================= || KPI Cards | ============================================= */} - - {KPI_META.map((item, idx) => { - const Icon = item.icon; - const value = summaryData?.[item.countKey] ?? 0; - return ( - - { - if (idx >= 0) handleChange(null, idx); - }} - sx={{ cursor: 'pointer', height: '100%' }} - > - } - color={idx === 0 ? '#662582' : item.color} - loading={summaryDataIsLoading} - /> - - - ); - })} - - - {/* ============================================= || Status Tabs + Search || ============================================= */} - - - - {STATUS_TABS.map((t, idx) => { - const meta = STATUS_META[t.status]; - const Icon = meta.icon; - const active = value0 === idx; - const count = summaryData?.[t.countKey] ?? 0; - return ( - { - setstatus(t.status); - handleChange(null, idx); - }} - sx={{ - display: 'inline-flex', - alignItems: 'center', - gap: { xs: 0.625, md: 0.875 }, - pl: 0.5, - pr: { xs: 1, md: 1.25 }, - py: 0.5, - flexShrink: 0, - cursor: 'pointer', - borderRadius: '10px', - border: `1px solid ${active ? meta.color : '#e2e8f0'}`, - bgcolor: active ? meta.color : '#fff', - color: active ? '#fff' : '#64748b', - fontWeight: 600, - boxShadow: 'none', - transition: 'background-color 0.15s, border-color 0.15s, color 0.15s', - '&:hover': { - borderColor: active ? meta.color : '#cbd5e1', - bgcolor: active ? meta.color : '#f8fafc' - } - }} - > - - - - - {meta.label} - - - {summaryDataIsLoading ? : count} - - - ); - })} - - - {/* Search */} - - - - - - - {/* ============================================= || Table || ============================================= */} - - - - - - # - Status - Client - Contact - Address - Actions - - - - {getalltenantsIsLoading && } - - {tenantList?.length == 0 && !isloader ? ( - - - - - - - - No tenants to show - - - {`No ${activeTabMeta.label.toLowerCase()} tenants for this filter.`} - - - - - ) : ( - tenantList?.map((row, index) => { - const rowStatusKey = (value0 === 0 ? 'active' : value0 === 1 ? 'pending' : 'inactive'); - const rowStatusMeta = STATUS_META[rowStatusKey]; - const RowStatusIcon = rowStatusMeta.icon; - return ( - - {/* ============================================ || tablerow 1 || ============================================ */} - - - - {String(index + 1 + page * rowsPerPage).padStart(2, '0')} - - - - - - - - - {rowStatusMeta.label} - - - - - - - - - - - {row.tenantname} - - - ID #{row.tenantid} - - - - - - - - {row.primarycontact || '—'} - - - {row.primaryemail || '—'} - - - - - - - {row.address || '—'} - - - - - - {value0 == 0 && ( - - { - setSelectedCustomer(row); - setSelectedtenid(row.tenantid); - setAppId(row.applocationid); - setTimeout(() => { - tenantupdate(row.tenantid); - }, 100); - }} - > - - - - )} - {value0 == 1 && ( - - { - setSelectedCustomer(row); - setDialogopen(true); - setSelectedtenid(row.tenantid); - setAppId(row.applocationid); - getAppPricing(row.applolcationid); - }} - > - - - - )} - {value0 == 2 && ( - - { - setSelectedCustomer(row); - setSelectedtenid(row.tenantid); - setAppId(row.applocationid); - setTimeout(() => { - tenantupdate(row.tenantid); - }, 100); - }} - > - - - - )} - - { - setSelectedCustomer(row); - handleCollapseToggle1(index); - setOpenRowIndex2(-1); - setSelectedtenid(row.tenantid); - }} - > - {openRowIndex1 === index ? : } - - - {value0 !== 1 && ( - - { - setSelectedCustomer(row); - handleCollapseToggle2(index); - setOpenRowIndex1(-1); - setSelectedtenid(row.tenantid); - setAppId(row.applocationid); - }} - > - {openRowIndex2 === index ? : } - - - )} - - - - {/* ============================================ || collapsive row (1) (view) || ============================================ */} - {openRowIndex1 === index && ( - - - - - - } - sx={{ - alignItems: 'center', - flexDirection: 'row' - }} - /> - - } - sx={{ - alignItems: 'center', - flexDirection: 'row' - }} - /> - - } - > - {/* ============================================= || Details || ============================================= */} - {value1 == 0 && ( - - - - - - - - {row.tenantname} - - Tenant - - - - - - - - - - - {row.firstname} - - Contact Person - - - - - - - - - - - {row.primarycontact} - - Phone - - - - - - - - - - - {row.primaryemail} - - E-Mail - - - - - - - - - - - {row.address} - - Address - - - - - - {/* - - - - - - {row.subcategoryname} - - Category - - - - - - - - {row.suburb} - - - Location - - - - */} - - - - - - - - {row.city} - - City - - - - - - - - {row.postcode} - - - PostCode - - - - - - - - - - - {row.latitude} - - Latitude - - - - - - - - {row.longitude} - - - Longitude - - - - - - )} - {/* ============================================= || Pricing || ============================================= */} - {value1 == 1 && ( - - -
- - - # - Date - Slab - Base Price - Min Kms - Price/Km - Other Charges - - - - {clientpricelist?.length === 0 ? ( - - - - - - ) : ( - clientpricelist.map((val, i) => ( - - {i + 1} - {dayjs(val.pricingdate).format('DD-MM-YYYY')} - {val.slab} - {val.baseprice} - {val.minkm} - {val.priceperkm} - {val.othercharges} - - )) - )} - -
-
- - )} - - - - - )} - {/* ============================================ || collapsive row (2) (edit)|| ============================================ */} - {openRowIndex2 === index && ( - - - {/* */} - - - - } - sx={{ - alignItems: 'center', - flexDirection: 'row' - }} - /> - {value0 !== 1 && ( - - } - sx={{ - alignItems: 'center', - flexDirection: 'row' - }} - onClick={() => { - getAppPricing(selectedCustomer.applolcationid); - }} - /> - )} - - - {value2 == 1 && ( - - )} -
- } - > - {/* ============================================= || Edit Details || ============================================= */} - {value2 == 0 && ( - - - {/* =========================|| Edit Details (right) || =========================*/} - - - - - {/* =========================|| Tenant || =========================*/} - - - - - - - { - setEditClient({ - ...editClient, - tenantname: e.target.value - }); - }} - sx={{ mt: 2 }} - /> - - - {' '} - {/* =========================|| Contact Person || =========================*/} - - - - - - - { - setEditClient({ - ...editClient, - firstname: e.target.value - }); - }} - sx={{ mt: 2 }} - /> - - - - {/* =========================|| Contact Number || =========================*/} - - - - - - - { - setEditClient({ - ...editClient, - primarycontact: e.target.value - }); - }} - sx={{ mt: 2 }} - /> - - - - {/* =========================|| E-Mail || =========================*/} - - - - - - - { - setEditClient({ - ...editClient, - primaryemail: e.target.value - }); - }} - sx={{ mt: 2 }} - /> - - - - {/* =========================|| Address || =========================*/} - - - - - - - (typeof option === 'string' ? option : option.description)} - filterOptions={(x) => x} - options={options} - autoComplete - includeInputInList - filterSelectedOptions - defaultValue={selectedCustomer.address} - noOptionsText="No locations" - onChange={(event, newValue) => { - setOptions(newValue ? [newValue, ...options] : options); - setValue(newValue || ''); - console.log('newValue', newValue); - setAddress(newValue?.description || ''); - setEditClient({ - ...editClient, - address: newValue?.description || '' - }); - }} - onInputChange={(event, newInputValue) => { - setInputValue(newInputValue); - }} - renderInput={(params) => ( - - )} - renderOption={(props, option) => { - const matches = option.structured_formatting.main_text_matched_substrings || []; - - const parts = parse( - option.structured_formatting.main_text, - matches.map((match) => [match.offset, match.offset + match.length]) - ); - - return ( -
  • - - - - - - {parts.map((part, index) => ( - - {part.text} - - ))} - - {option.structured_formatting.secondary_text} - - - -
  • - ); - }} - /> -
    -
    -
    - {/* =========================|| category name || =========================*/} - {/* - - - - - - { - setEditClient({ - ...editClient, - subcategoryname: e.target.value - }); - }} - /> - - - - - - { - setEditClient({ - ...editClient, - suburb: e.target.value - }); - }} - value={selectedCustomer.suburb} - /> - - - */} - {/* =========================|| city || =========================*/} - - - - - - - { - setEditClient({ - ...editClient, - city: e.target.value - }); - }} - value={city == '' ? selectedCustomer.city : city} - /> - - - - - - { - { - setEditClient({ - ...editClient, - postcode: e.target.value - }); - } - }} - value={zipcode == '' ? selectedCustomer.postcode : zipcode} - /> - - - - {/* =========================|| latitude || =========================*/} - - - - - - - - - - - - - { - // setLongi(e.target.value.toString()) - // setEditClient({ - // ...editClient, - // longitude: - // e.target.value.toString(), - // }); - // }} - /> - - - -
    -
    - {/* =========================|| update || =========================*/} - - - - -
    -
    -
    -
    - )} - {/* ============================================= || Edit Pricing || ============================================= */} - {value2 == 1 && ( - - - - - - # - Date - Slab - Base Price - Min Kms - Price/Km - Other Charges - - - - {clientpricelist?.length === 0 ? ( - - - - - - ) : ( - clientpricelist.map((val, i) => ( - - {i + 1} - {dayjs(val.pricingdate).format('DD-MM-YYYY')} - {val.slab} - {val.baseprice} - {val.minkm} - {val.priceperkm} - {val.othercharges} - - )) - )} - -
    -
    -
    - )} - - {/* */} - - - )} - - ); - }) - )} - - - - - {/* ============================================= || Mobile cards (xs only) || ============================================= */} - {isMobile && ( - - {getalltenantsIsLoading && ( - - - - - )} - - {tenantList?.length == 0 && !isloader ? ( - - - - - - No tenants to show - - - {`No ${activeTabMeta.label.toLowerCase()} tenants for this filter.`} - - - ) : ( - tenantList?.map((row, index) => { - const rowStatusKey = value0 === 0 ? 'active' : value0 === 1 ? 'pending' : 'inactive'; - const rowStatusMeta = STATUS_META[rowStatusKey]; - const RowStatusIcon = rowStatusMeta.icon; - const expanded = openRowIndex1 === index; - return ( - - - - - - - - {row.tenantname} - - - - - - - {rowStatusMeta.label} - - - - - - {value0 == 0 && ( - { - setSelectedCustomer(row); - setSelectedtenid(row.tenantid); - setAppId(row.applocationid); - setTimeout(() => { - tenantupdate(row.tenantid); - }, 100); - }} - > - - - )} - {value0 == 1 && ( - { - setSelectedCustomer(row); - setDialogopen(true); - setSelectedtenid(row.tenantid); - setAppId(row.applocationid); - getAppPricing(row.applolcationid); - }} - > - - - )} - {value0 == 2 && ( - { - setSelectedCustomer(row); - setSelectedtenid(row.tenantid); - setAppId(row.applocationid); - setTimeout(() => { - tenantupdate(row.tenantid); - }, 100); - }} - > - - - )} - { - setSelectedCustomer(row); - handleCollapseToggle1(index); - setOpenRowIndex2(-1); - setSelectedtenid(row.tenantid); - }} - > - {expanded ? : } - - {value0 !== 1 && ( - { - setSelectedCustomer(row); - setSelectedtenid(row.tenantid); - setAppId(row.applocationid); - getAppPricing(row.applolcationid); - setDialogopen(true); - }} - > - - - )} - - - } - > - - - - - - - {row.address || '—'} - - - - - {expanded && ( - - - - - - - - - - )} - - ); - }) - )} - - )} - - {/* ============================================= || Pagination| ============================================= */} - {!searchword && tenantList?.length > 0 && ( - <> - - - - )} - - {/* // ==============================||( Client Pricing ) dialog (dialogopen) ||============================== // */} - - - {`Client Pricing - (${selectedCustomer.tenantname})`} - - - - - - Select Slab - `${option.slab}`} - fullWidth - selectOnFocus - renderInput={(params) => } - onChange={(event, value, reason) => { - setSelectedPricing(value); - console.log('pricing', value); - setIsprice(false); - - if (reason === 'clear') { - setIsprice(true); - } - }} - /> - - - - Base Price - { - setSelectedPricing({ - ...selectedPricing, - baseprice: e.target.value - }); - }} - /> - - - Price/Km - { - setSelectedPricing({ - ...selectedPricing, - priceperkm: e.target.value - }); - }} - /> - - - - Min Kms - { - setSelectedPricing({ - ...selectedPricing, - minkm: e.target.value - }); - }} - /> - - - Max Kms - { - setSelectedPricing({ - ...selectedPricing, - maxkm: e.target.value - }); - }} - /> - - - Min Orders - { - setSelectedPricing({ - ...selectedPricing, - minorder: e.target.value - }); - }} - /> - - - - - - - - - {value2 == 1 && ( - - )} - {value0 == 1 && ( - - )} - - - - - - - - ); -}; - -export default Clients1; diff --git a/src/pages/nearle/clients/createCustomer.js b/src/pages/nearle/clients/createCustomer.js deleted file mode 100644 index a997b54..0000000 --- a/src/pages/nearle/clients/createCustomer.js +++ /dev/null @@ -1,545 +0,0 @@ -import { React, useEffect, useState, useRef } from 'react'; -import { useTheme } from '@mui/material/styles'; -import { Button, Grid, InputLabel, MenuItem, Select, Stack, TextField, Typography, IconButton, Autocomplete, useMediaQuery } from '@mui/material'; -import MainCard from 'components/MainCard'; -import axios from 'axios'; -import Loader from 'components/Loader'; -import Geocode from 'react-geocode'; -import { enqueueSnackbar } from 'notistack'; -import { useNavigate } from 'react-router'; -import CloseIcon from '@mui/icons-material/Close'; -import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete'; -import { OpenToast } from 'components/third-party/OpenToast'; - -const CreateCustomer = () => { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('md')); - const [appId, setAppId] = useState(0); - const locationRef = useRef(null); - const [mobilenumber, setMobilenumber] = useState(''); - const [emailaddress, setEmailaddress] = useState(''); - const [address, setAddress] = useState(''); - const [firstname, setFirstname] = useState(''); - const [doorno, setDoorno] = useState(''); - const [landmark, setLandmark] = useState(''); - const [inputValue2, setInputValue2] = useState(''); - const [appLocaLat, setAppLocaLat] = useState(); - const [appLocaLng, setAppLocaLng] = useState(); - const [appLocaRadius, setAppLocaRadius] = useState(); - const [locaName, setLocoName] = useState('Select Location'); - const [tenantlist, setTenantlist] = useState([]); - const [tid, setTid] = useState(0); - const [pickCust, setPickCust] = useState({}); - const [startPoint, setStartPoint] = useState({ latitude: 0, longitude: 0 }); - const [loading, setLoading] = useState(false); - const navigate = useNavigate(); - - Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY); - - useEffect(() => { - // Initialize Google Maps Autocomplete - if (inputValue2) { - const autocompleteInput = document.getElementById('addressAuto1'); - const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, { - // types: ['(cities)'], // You can adjust the types parameter based on your requirements - strictBounds: true, - bounds: new window.google.maps.Circle({ - // center: new window.google.maps.LatLng(11.0050707, 76.9509083), - // radius: 100000 - center: new window.google.maps.LatLng(appLocaLat, appLocaLng), - - radius: appLocaRadius * 1000 - }).getBounds() - }); - // Event listener for autocomplete place changed - autocomplete.addListener('place_changed', () => { - const place = autocomplete.getPlace(); - setInputValue2(`${place.name}, ${place.formatted_address}`); - console.log('new place', place); // Do something with the selected place - console.log(' pick (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place - // to trigger getDistance - setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() }); - setAddress(`${place.name} ${place.formatted_address}`); - setPickCust({ ...pickCust, address: `${place.name} ${place.formatted_address}` }); - const address = { - address: `${place.name} ${place.formatted_address}`, - street_number: '', - route: '', - locality: '', - sublocality_level_1: '', - administrative_area_level_3: '', - administrative_area_level_1: '', - country: '', - postal_code: '' - }; - place.address_components.forEach((component) => { - component.types.forEach((type) => { - switch (type) { - case 'street_number': - address.street_number = component.long_name; - break; - case 'route': - address.route = component.long_name; - break; - case 'locality': - address.locality = component.long_name; - break; - case 'sublocality_level_1': - address.sublocality_level_1 = component.long_name; - break; - case 'administrative_area_level_3': - address.administrative_area_level_3 = component.long_name; - break; - case 'administrative_area_level_1': - address.administrative_area_level_1 = component.long_name; - break; - case 'country': - address.country = component.long_name; - break; - case 'postal_code': - address.postal_code = component.long_name; - break; - // Add more cases as needed for other types - } - }); - }); - - // Use address object as per your requirements - setPickCust({ - ...pickCust, - address: address.address, - doorno: `${address.street_number} ${address.route}`, - suburb: address.administrative_area_level_3, - city: address.locality, - state: address.administrative_area_level_1, - postcode: address.postal_code, - latitude: place.geometry.location.lat(), - longitude: place.geometry.location.lng() - }); - console.log('Pick Address:', address); - }); - } - }, [inputValue2]); - // ==================================================== || getapplocations || ==================================================== - const getapplocations = async () => { - setLoading(true); - await axios - .get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`) - .then((res) => { - console.log('getapplocations', res); - const { latitude, longitude, radius } = res.data.details[0]; - if (res.data.status) { - setAppLocaLat(latitude); - setAppLocaLng(longitude); - setAppLocaRadius(radius); - console.log('radius', radius); - } - setLoading(false); - }) - .catch((err) => { - console.log(err); - setLoading(false); - }); - }; - useEffect(() => { - if (appId) { - getapplocations(); - } - }, [appId]); - - // ===================================================== || fetchtenantinfolist || ===================================================== - - const fetchtenantinfolist = async (id) => { - setLoading(true); - await axios - .get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${id}&status=active`) - - .then((res) => { - console.log(res); - if (res.data.status) { - let arr = []; - res.data.details.map((val) => { - arr.push({ - ...val, - label: `${val.tenantname}` - }); - }); - setTenantlist(arr); - } - setLoading(false); - }) - .catch((err) => { - console.log(err); - setLoading(false); - }); - }; - useEffect(() => { - appId && fetchtenantinfolist(appId); - }, [appId]); - // ============================================= || gettenantlocations (branches) || ============================================= - const gettenantlocations = async (id) => { - try { - const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${id}`); - console.log('gettenantlocations', res.data.details); - if (res.data.details.length == 1) { - } else { - } - } catch (err) { - console.log('gettenantlocations', err); - } - }; - - const opentoast = (message) => { - enqueueSnackbar(message, { - variant: 'error', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, - autoHideDuration: 2000 - }); - }; - - const createprofile = async () => { - let obj = { - applocationid: +appId, - tenantid: +tid, - customerid: 0, - configid: 1, - firstname: firstname, - dialcode: '+91', - contactno: mobilenumber, - email: emailaddress, - doorno: doorno, - address: pickCust.address, - suburb: pickCust.suburb, - city: pickCust.city, - state: pickCust.state, - postcode: pickCust.postcode, - landmark: landmark, - latitude: startPoint.latitude.toString(), - longitude: startPoint.longitude.toString(), - profileimage: '', - devicetype: '', - deviceid: '', - customertoken: '', - primaryaddress: 1 - }; - console.log(obj); - - setLoading(true); - try { - await axios - .post(`${process.env.REACT_APP_URL}/customers/create`, obj) - .then((res) => { - console.log(res); - if (res.data.status) { - enqueueSnackbar(' Created Successfully ', { - variant: 'success', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, - autoHideDuration: 2000 - }); - navigate('/nearle/customers'); - } else if (res.data.message == 'Customer Already available') { - enqueueSnackbar('Customer Already available', { - variant: 'error', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, - autoHideDuration: 2000 - }); - } - setLoading(false); - }) - .catch((err) => { - console.log(err); - - setLoading(false); - enqueueSnackbar(err.message, { - variant: 'error', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, - autoHideDuration: 2000 - }); - }); - } catch (err) { - console.log(err); - setLoading(false); - } - }; - - return ( - <> - {loading && } - - - Create Customer - - - - - - - {/* ===================================================== || Choose location || ===================================================== */} - - - - {/* ===================================================== || Choose client || ===================================================== */} - - } - onChange={(e, val, reason) => { - if (val) { - console.log('Client', val); - gettenantlocations(val.tenantid); - setTid(val.tenantid); - } else { - setClientinfo({}); - setTenantid(''); - } - if (reason == 'clear') { - } - }} - />{' '} - - - {/* ===================================================== || Name|| ===================================================== */} - - - Name - setFirstname(e.target.value)} - value={firstname} - autoComplete="off" - /> - - - {/* ===================================================== || Phone Number || ===================================================== */} - - - Phone Number - - - { - if (e.target.value.toString().length <= 10) { - setMobilenumber(e.target.value); - } - }} - value={mobilenumber} - autoComplete="off" - // disabled - sx={{ cursor: 'not-allowed' }} - /> - - - - {/* ===================================================== || Email|| ===================================================== */} - - - - Email - setEmailaddress(e.target.value)} - value={emailaddress} - autoComplete="off" - /> - - - {/* ===================================================== || door no || ===================================================== */} - - - - Door No - setDoorno(e.target.value)} - value={doorno} - autoComplete="off" - /> - - - - {/* ===================================================== || Address || ===================================================== */} - - - - Address - { - if (appId) { - appId && setInputValue2(e.target.value); - } else { - OpenToast('Select Location First', 'warning', 3000); - } - }} - InputProps={{ - endAdornment: ( - { - setInputValue2(''); - setPickCust({ - ...pickCust, - doorno: '', - suburb: '', - city: '', - postcode: '', - landmark: '' - }); - setStartPoint({ latitude: 0, longitude: 0 }); - }} - size="small" - > - - - ) - }} - /> - - - - - - Location - setPickCust({ ...pickCust, suburb: e.target.value })} - value={pickCust.suburb} - autoComplete="off" - /> - - - - - City - setPickCust({ ...pickCust, city: e.target.value })} - value={pickCust.city} - autoComplete="off" - /> - - - - - - State - setPickCust({ ...pickCust, state: e.target.value })} - value={pickCust.state} - autoComplete="off" - /> - - - - - Post Code - setPickCust({ ...pickCust, postcode: e.target.value })} - value={pickCust.postcode} - autoComplete="off" - /> - - - - - - Landmark - setLandmark(e.target.value)} - value={landmark} - autoComplete="off" - /> - - - - - - - - - - - - - ); -}; - -export default CreateCustomer; diff --git a/src/pages/nearle/clients/createclient.js b/src/pages/nearle/clients/createclient.js index e44eb07..de64479 100644 --- a/src/pages/nearle/clients/createclient.js +++ b/src/pages/nearle/clients/createclient.js @@ -2,7 +2,8 @@ import { useEffect, useState } from 'react'; // material-ui import { useTheme } from '@mui/material/styles'; -import { Box, Button, FormLabel, Grid, InputLabel, MenuItem, Select, Stack, TextField, Typography, useMediaQuery } from '@mui/material'; +import { Avatar, Box, Button, FormLabel, Grid, InputLabel, MenuItem, Paper, Select, Stack, TextField, Typography, useMediaQuery } from '@mui/material'; +import { MdPersonAddAlt1 } from 'react-icons/md'; // third-party // import { PatternFormat } from 'react-number-format'; @@ -15,6 +16,7 @@ import Loader from 'components/Loader'; import Geocode from 'react-geocode'; import { enqueueSnackbar } from 'notistack'; import { useNavigate } from 'react-router'; +import { DT, tint } from 'themes/dt/tokens'; // import { setLocationType } from 'react-geocode'; // const avatarImage = require.context('assets/images/users', true); @@ -145,12 +147,6 @@ const Createclient = () => { }); }; - useEffect(() => { - if (selectedImage) { - setAvatar(URL.createObjectURL(selectedImage)); - } - }, [selectedImage]); - const { ref: materialRef } = usePlacesWidget({ apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY, onPlaceSelected: (place) => { @@ -296,11 +292,28 @@ const Createclient = () => { {loading && } - - Create Client - + + + + + + Create Client + + - + {/* diff --git a/src/pages/nearle/customers/customers.js b/src/pages/nearle/customers/customers.js deleted file mode 100644 index fa57360..0000000 --- a/src/pages/nearle/customers/customers.js +++ /dev/null @@ -1,1067 +0,0 @@ -import { React, useState, useEffect, useRef, useMemo } from 'react'; -import axios from 'axios'; -import { FaRegEdit } from 'react-icons/fa'; -import LoaderWithImage from 'components/nearle_components/LoaderWithImage'; - -// material-ui -import { - Box, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Grid, - Typography, - IconButton, - Stack, - Tooltip, - Dialog, - DialogActions, - DialogTitle, - DialogContent, - Button, - TextField, - Autocomplete, - Avatar, - Paper, - useMediaQuery, - useTheme -} from '@mui/material'; -import { - MdMyLocation, - MdPersonPin, - MdPhone, - MdLocationOn, - MdEdit, - MdGroups, - MdHowToReg, - MdPlace, - MdOutlineGroups, - MdOutlineHowToReg, - MdOutlinePlace -} from 'react-icons/md'; -import Geocode from 'react-geocode'; -import LocationOnIcon from '@mui/icons-material/LocationOn'; -import parse from 'autosuggest-highlight/parse'; -import { debounce } from '@mui/material/utils'; -// project imports -import Loader from 'components/Loader'; -import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar'; -import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete'; -import PageHeader from 'components/nearle_components/PageHeader'; -import StatCard from 'components/nearle_components/StatCard'; -import { useInfiniteQuery, useQuery } from '@tanstack/react-query'; -import { getallcustomers, getcustomersummary } from 'pages/api/api'; -import { OpenToast } from 'components/third-party/OpenToast'; -import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton'; -import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; - -// ============================================================================ -// Design tokens — shared with the deliveries / tenants / pricing pages so every -// surface (header, KPI tiles, table, badges, dialog) speaks the same visual -// language. Keep this block in sync with deliveries.js:109–162. -// ============================================================================ -const DT = { - radiusPill: 999, - radiusCard: 14, - shadowSoft: '0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.05)', - shadowMd: '0 1px 3px rgba(15, 23, 42, 0.06)', - shadowPop: '0 12px 32px rgba(15, 23, 42, 0.12)', - textPrimary: '#0f172a', - textSecondary: '#64748b', - textMuted: '#94a3b8', - borderSubtle: '#e2e8f0', - divider: '#f1f5f9', - surface: '#ffffff', - surfaceAlt: '#f8fafc' -}; -const a = (c, suffix) => `${c}${suffix}`; -const tint = (c) => a(c, '08'); -const soft = (c) => a(c, '18'); -const ring = (c) => a(c, '26'); -const edge = (c) => a(c, '55'); - -const SoftPaper = (props) => ( - -); - -const AccentAvatar = ({ color, selected, size = 24, children }) => ( - - {children} - -); - -// ==============================|| google address ||============================== // -const GOOGLE_MAPS_API_KEY = process.env.REACT_APP_GOOGLE_MAPS_API_KEY; - -function loadScript(src, position, id) { - if (!position) { - return; - } - - const script = document.createElement('script'); - script.setAttribute('async', ''); - script.setAttribute('id', id); - script.src = src; - position.appendChild(script); -} - -const autocompleteService = { current: null }; - -// ==============================|| MUI TABLE - ENHANCED ||============================== // - -export default function Customers() { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('md')); - const containerRef = useRef(); - const loadMoreRef = useRef(); - const [rowsPerPage] = useState(50); - const [page] = useState(0); - const [appId, setAppId] = useState(0); - const [locaName, setLocoName] = useState('All'); - const [selectedCustomer, setSelectedCustomer] = useState({}); // to edit - const [open, setOpen] = useState(false); - const [address, setAddress] = useState(''); - const [latlong, setLatlong] = useState({}); - const [city, setCity] = useState(''); - const [postcode, setPostcode] = useState(''); - const [state, setState] = useState(''); - const [suburb, setSuburb] = useState(''); - const [searchword, setSearchword] = useState(''); - const [debouncedSearch, setDebouncedSearch] = useState(''); - - // ==============================|| for google address ||============================== // - const [value, setValue] = useState(null); - const [inputValue, setInputValue] = useState(''); - const [options, setOptions] = useState([]); - const loaded = useRef(false); - if (typeof window !== 'undefined' && !loaded.current) { - if (!document.querySelector('#google-maps')) { - loadScript( - `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API_KEY}&libraries=places`, - document.querySelector('head'), - 'google-maps' - ); - } - - loaded.current = true; - } - const fetch = useMemo( - () => - debounce((request, callback) => { - autocompleteService.current.getPlacePredictions(request, callback); - }, 400), - [] - ); - useEffect(() => { - let active = true; - - if (!autocompleteService.current && window.google) { - autocompleteService.current = new window.google.maps.places.AutocompleteService(); - } - if (!autocompleteService.current) { - return undefined; - } - - if (inputValue === '') { - setOptions(value ? [value] : []); - return undefined; - } - - fetch({ input: inputValue }, (results) => { - if (active) { - let newOptions = []; - - if (value) { - newOptions = [value]; - } - - if (results) { - newOptions = [...newOptions, ...results]; - } - - setOptions(newOptions); - } - }); - - return () => { - active = false; - }; - }, [value, inputValue, fetch]); - - Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY); - useEffect(() => { - try { - console.log('selected address =>', address); - Geocode.fromAddress(address).then( - (response) => { - console.log('lat long response =>', response.results[0]); - if (response.status == 'OK') { - const { lat, lng } = response.results[0].geometry.location; - setLatlong({ - lat, - lng - }); - // setSelectedCustomer({ - // ...selectedCustomer, - // latitude: lat, - // longitude: lng - // }); - if (response.results[0].address_components) { - let place = response.results[0]; - let city1, zipcode1, state1, suburb1; - for (let i = 0; i < place.address_components.length; i++) { - for (let j = 0; j < place.address_components[i].types.length; j++) { - switch (place.address_components[i].types[j]) { - case 'locality': - city1 = place.address_components[i].long_name; - break; - case 'administrative_area_level_1': - state1 = place.address_components[i].long_name; - break; - case 'postal_code': - zipcode1 = place.address_components[i].long_name; - break; - case 'sublocality': - suburb1 = place.address_components[i].long_name; - break; - } - } - } - setCity(city1 || ''); - setState(state1 || ''); - setPostcode(zipcode1 || ''); - setSuburb(suburb1 || ''); - setSelectedCustomer((prev) => ({ - ...prev, - city: city1 || '', - state: state1 || '', - postcode: zipcode1 || '', - suburb: suburb1 || '', - latitude: lat || '', - longitude: lng || '' - })); - } - } - }, - (error) => { - console.log(error); - } - ); - } catch (err) { - console.log(err); - } - }, [address]); - - // useEffect(() => { - // selectedCustomer && - // setLatlong({ - // lat: selectedCustomer.latitude, - // lng: selectedCustomer.longitude - // }); - // }, [selectedCustomer]); - - // ==============================|| getallcustomers (customers) ||============================== // - - const { - data, - isLoading: customersIsLoading, - isFetchingNextPage, - fetchNextPage, - hasNextPage, - refetch: getallcustomersRefetch - } = useInfiniteQuery({ - queryKey: ['getAllCustomers', appId, debouncedSearch, rowsPerPage], - queryFn: getallcustomers, - getNextPageParam: (lastPage) => lastPage.nextPage, - keepPreviousData: true - }); - const rows = data?.pages.flatMap((page) => page.data) || []; - - useEffect(() => { - if (!hasNextPage) return; - const observer = new IntersectionObserver( - (entries) => { - if (entries[0].isIntersecting) { - fetchNextPage(); - } - }, - { - root: document.querySelector('.MuiTableContainer-root'), // 👈 or explicitly TableContainer - rootMargin: '0px', - threshold: 1.0 - } - ); - if (loadMoreRef.current) observer.observe(loadMoreRef.current); - return () => { - if (loadMoreRef.current) observer.unobserve(loadMoreRef.current); - }; - }, [hasNextPage, fetchNextPage]); - - const handleScroll = (event) => { - const { scrollTop, scrollHeight, clientHeight } = event.currentTarget; - if (scrollTop + clientHeight >= scrollHeight - 50) { - if (hasNextPage && !isFetchingNextPage) { - fetchNextPage(); - } - } - }; - - // ==============================|| getcustomersummary (customers) ||============================== // - - const { data: pageCount, isLoading: customerSummaryIsLoading } = useQuery({ - queryKey: ['customersummary', appId], - queryFn: getcustomersummary - }); - useEffect(() => { - console.log('pageCount', pageCount); - }, [pageCount]); - // ==============================|| updateCustomer (post)||============================== // - const updateCustomer = async () => { - console.log('selectedCustomer', selectedCustomer); - - if (!selectedCustomer.firstname) { - OpenToast('Enter Door NO', 'warning', 1500); - } else if (!selectedCustomer.contactno) { - OpenToast('Enter Contact Number ', 'warning', 1500); - } else if (!selectedCustomer.address) { - OpenToast('Enter Valid Address', 'warning', 1500); - } else if (!selectedCustomer.suburb) { - OpenToast('Enter Suburb', 'warning', 1500); - } else if (!selectedCustomer.city) { - OpenToast('Enter City ', 'warning', 1500); - } else if (!selectedCustomer.state) { - OpenToast('Enter State', 'warning', 1500); - } else if (!selectedCustomer.postcode) { - OpenToast('Enter PostCode', 'warning', 1500); - } else if (!selectedCustomer.landmark) { - OpenToast('Enter Landmark', 'warning', 1500); - } else if (!selectedCustomer.latitude) { - OpenToast('Enter Latitude', 'warning', 1500); - } else if (!selectedCustomer.longitude) { - OpenToast('Enter Longitude', 'warning', 1500); - } else { - try { - const postUpdateResponse = await axios.put(`${process.env.REACT_APP_URL}/customers/update`, { - customerid: selectedCustomer.customerid, - configid: 1, - firstname: selectedCustomer.firstname, - applocationid: selectedCustomer.applocationid, - profileimage: '', - dialcode: '+91', - contactno: selectedCustomer.contactno, - devicetype: '', - deviceid: '', - customertoken: '123', - address: selectedCustomer.address, - suburb: suburb, - city: city, - state: state, - postcode: postcode, - landmark: selectedCustomer.landmark, - doorno: selectedCustomer.doorno, - latitude: selectedCustomer.latitude.toString(), - longitude: selectedCustomer.longitude.toString() - }); - - console.log('postUpdateResponse', postUpdateResponse); - if (postUpdateResponse.data.status) { - OpenToast(postUpdateResponse.data.message, 'success', 1500); - setOpen(false); - getallcustomersRefetch(); - } - } catch (error) { - console.log('postUpdate error', error); - } - } - }; - const KPI_META = [ - { key: 'total', label: 'Total Customers', color: '#662582', icon: MdOutlineGroups, value: pageCount?.Total ?? 0 }, - { key: 'loaded', label: 'Loaded in View', color: '#0ea5e9', icon: MdOutlineHowToReg, value: rows.length }, - { key: 'zone', label: 'Active Zone', color: '#10b981', icon: MdOutlinePlace, value: locaName || 'All Zones' } - ]; - - return ( - <> - {(customerSummaryIsLoading || customersIsLoading) && } - - {/* ============================================= || Header | ============================================= */} - } - placeholder="Select Zone" - paperComponent={SoftPaper} - sx={{ width: { xs: '100%', sm: 280 }, zIndex: 100 }} - /> - } - /> - - {/* ============================================= || KPI Cards | ============================================= */} - - {KPI_META.map((item) => { - const Icon = item.icon; - return ( - - } - color={item.color} - loading={customerSummaryIsLoading} - /> - - ); - })} - - - {/* ============================================= || Search header | ============================================= */} - - - - - - - - - Directory - - - {pageCount?.Total ?? 0} total · {rows.length} loaded - - - - - - - - - - {/* ============================================= || Table || ============================================= */} - - {isMobile ? ( - - {customersIsLoading && } - {rows?.length === 0 && !customersIsLoading ? ( - - - - - - No customers to show - - - {searchword ? 'Try a different keyword.' : 'Pick a zone above to load the directory.'} - - - ) : ( - rows?.map((row, index) => ( - - - - - - - - {row.firstname || '—'} - - - ID #{row.customerid} - - - - - { - setSelectedCustomer(row); - setTimeout(() => setOpen(true), 0); - }} - sx={{ - bgcolor: soft('#8b5cf6'), - color: '#8b5cf6', - border: `1px solid ${edge('#8b5cf6')}`, - flexShrink: 0, - '&:hover': { bgcolor: '#8b5cf6', color: '#fff' } - }} - > - - - - - } - > - - - - {row.suburb ? ( - - {row.suburb} - - ) : ( - - )} - - - - {row.address || '—'} - - - - - )) - )} - {rows?.length !== 0 && ( -
    - {isFetchingNextPage || hasNextPage ? ( - - ) : ( - - No more customers - - )} -
    - )} -
    - ) : ( - - - - - # - Customer - Contact - Address - Location - Action - - - - - {customersIsLoading && } - {rows?.length === 0 && !customersIsLoading ? ( - - - - - - - - No customers to show - - - {searchword ? 'Try a different keyword.' : 'Pick a zone above to load the directory.'} - - - - - ) : ( - rows?.map((row, index) => ( - - - - {String(index + 1 + page * rowsPerPage).padStart(2, '0')} - - - - - - - - - - {row.firstname || '—'} - - - ID #{row.customerid} - - - - - - - - - - {row.contactno || '—'} - - - {row.email && ( - - {row.email} - - )} - - - - - - {row.address || '—'} - - - - - {row.suburb ? ( - - {row.suburb} - - ) : ( - - )} - - - - { - setSelectedCustomer(row); - setTimeout(() => setOpen(true), 0); - }} - sx={{ - bgcolor: soft('#8b5cf6'), - color: '#8b5cf6', - border: `1px solid ${edge('#8b5cf6')}`, - '&:hover': { bgcolor: '#8b5cf6', color: '#fff' } - }} - > - - - - - - )) - )} - {rows?.length !== 0 && ( - - -
    - {isFetchingNextPage || hasNextPage ? ( - - ) : ( - - No more customers - - )} -
    -
    -
    - )} -
    -
    -
    - )} -
    - {/* ======================================== || Edit Dialog || ======================================== */} - setOpen(false)} - aria-labelledby="alert-dialog-title" - aria-describedby="alert-dialog-description" - maxWidth="lg" - fullWidth - fullScreen={isMobile} - PaperProps={{ sx: { borderRadius: { xs: 0, sm: 3 } } }} - > - - - - - - - - Customer - - - Edit {selectedCustomer?.firstname || 'Customer'} - - - - - - - - Customer Name - { - setSelectedCustomer({ - ...selectedCustomer, - firstname: e.target.value - }); - }} - /> - - - Contact Number - - - { - const value = e.target.value.replace(/\D/g, ''); // allow only digits - - setSelectedCustomer((prev) => ({ - ...prev, - contactno: value - })); - }} - /> - - - - Address - (typeof option === 'string' ? option : option?.description || '')} - filterOptions={(x) => x} - options={options} - autoComplete - includeInputInList - filterSelectedOptions - value={selectedCustomer?.address} - noOptionsText="No locations" - onChange={(event, newValue) => { - setOptions(newValue ? [newValue, ...options] : options); - setValue(newValue); - console.log('newValue', newValue || ''); - setAddress(newValue?.description); - setSelectedCustomer({ - ...selectedCustomer, - address: newValue?.description - }); - }} - onInputChange={(event, newInputValue) => { - setInputValue(newInputValue); - }} - renderInput={(params) => } - renderOption={(props, option) => { - const matches = option.structured_formatting.main_text_matched_substrings || []; - - const parts = parse( - option.structured_formatting.main_text, - matches.map((match) => [match.offset, match.offset + match.length]) - ); - - return ( -
  • - - - - - - {parts?.map((part, index) => ( - - {part.text} - - ))} - - {option?.structured_formatting.secondary_text} - - - -
  • - ); - }} - /> -
    - - Location - { - const value = e.target.value; - setSelectedCustomer((prev) => ({ - ...prev, - suburb: value - })); - // setSuburb(e.target.value); - }} - /> - - - City - { - const value = e.target.value; - setSelectedCustomer((prev) => ({ - ...prev, - city: value - })); - // setCity(e.target.value); - }} - /> - - - State - { - const value = e.target.value; - setSelectedCustomer((prev) => ({ - ...prev, - state: value - })); - // setState(e.target.value); - }} - /> - - - Postcode - { - const value = e.target.value; - setSelectedCustomer((prev) => ({ - ...prev, - postcode: value - })); - // setPostcode(e.target.value); - }} - /> - - - Landmark - { - setSelectedCustomer({ - ...selectedCustomer, - landmark: e.target.value - }); - }} - /> - - - Latitude - - - - Longitude - - -
    -
    - - - - -
    - - ); -} diff --git a/src/pages/nearle/deliveries/deliveries.js b/src/pages/nearle/deliveries/deliveries.js index 3040d1a..c576bbd 100644 --- a/src/pages/nearle/deliveries/deliveries.js +++ b/src/pages/nearle/deliveries/deliveries.js @@ -78,7 +78,7 @@ import { startOfMonth, startOfWeek } from 'date-fns'; -import { DateRangePicker } from 'mui-daterange-picker'; +import { DateRangePicker } from 'components/nearle_components/DateRangePicker'; import * as React from 'react'; import Loader from 'components/Loader'; import { KeyboardArrowDownOutlined, KeyboardArrowUpOutlined } from '@mui/icons-material'; @@ -105,62 +105,11 @@ import StatCard from 'components/nearle_components/StatCard'; import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; // ============================================================================ -// Design tokens — extracted from the polished "Batch" dropdown so every -// surface on this page (filters, KPIs, tabs, status badges, dialogs) shares -// the same visual language. All helpers take a color and emit MUI sx values. +// Design tokens — shared across every DT-styled operator page so filters, +// KPIs, tabs, status badges, and dialogs stay visually consistent. +// See src/themes/dt/tokens.js for the canonical source. // ============================================================================ -const DT = { - radiusPill: 999, - radiusCard: 14, - radiusInner: 10, - radiusField: 10, - // Restrained, low-contrast elevation — corporate (Linear/Stripe), not flashy. - shadowSoft: '0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.05)', - shadowMd: '0 1px 3px rgba(15, 23, 42, 0.06)', - shadowPop: '0 12px 32px rgba(15, 23, 42, 0.12)', - textPrimary: '#0f172a', - textSecondary: '#64748b', - textMuted: '#94a3b8', - borderSubtle: '#e2e8f0', - borderHover: '#cbd5e1', - divider: '#f1f5f9', - surface: '#ffffff', - surfaceAlt: '#f8fafc', - brand: '#662582' -}; - -// Quick alpha helpers (hex + percentage suffix). Mirrors the batch-dropdown -// pattern (`${color}08`, `${color}18`, `${color}55`, `${color}26`). -const a = (c, suffix) => `${c}${suffix}`; -const tint = (c) => a(c, '08'); // very subtle surface tint -const soft = (c) => a(c, '18'); // soft chip / avatar bg -const ring = (c) => a(c, '26'); // focus ring color -const edge = (c) => a(c, '55'); // resting border - -// Pill input sx — used by every filter Autocomplete/TextField on the page. -// Accepts the accent color and returns sx for the outer TextField. Width is -// driven by parent flex/grid so this helper stays width-agnostic. -// Neutral, corporate filter field: white surface, hairline border, brand focus -// ring. Colour is no longer used to tint the whole control (that produced the -// "rainbow" filter bar) — accent now lives only in the small start-adornment -// icon, which aids scanning without flooding the surface. -const pillFieldSx = () => ({ - cursor: 'pointer', - '& .MuiOutlinedInput-root': { - borderRadius: DT.radiusField + 'px', - bgcolor: DT.surface, - fontWeight: 600, - color: DT.textPrimary, - paddingRight: '8px', - cursor: 'pointer', - transition: 'border-color 0.15s, box-shadow 0.15s', - '& fieldset': { borderColor: DT.borderSubtle, borderWidth: 1 }, - '&:hover fieldset': { borderColor: DT.borderHover }, - '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring(DT.brand)}` }, - '&.Mui-focused fieldset': { borderColor: DT.brand, borderWidth: 1.5 } - }, - '& .MuiAutocomplete-endAdornment .MuiSvgIcon-root': { color: DT.textMuted } -}); +import { DT, a, tint, soft, ring, edge, pillFieldSx } from 'themes/dt/tokens'; // Status palette — drives tab pills, row status badges, dialogs. const STATUS_META = { @@ -413,7 +362,7 @@ const Deliveries = () => { const response = await axios.post(`${process.env.REACT_APP_URL}/utils/notifyuser`, { token: selectedRow.userfcmtoken, notification: { - title: 'NearleXpress', + title: 'DoormileXpress', body: `${selectedRow.orderid} have been Cancelled`, sound: 'ring', image: '' @@ -912,7 +861,7 @@ const Deliveries = () => { setLocoName={setLocoName} setPage={setPage} pill - accentColor="#662582" + accentColor="#C01227" icon={} placeholder="Select Zone" paperComponent={SoftPaper} @@ -962,7 +911,7 @@ const Deliveries = () => { }} > - + @@ -1125,8 +1074,8 @@ const Deliveries = () => { transition: 'border-color 0.15s, box-shadow 0.15s', '& fieldset': { borderColor: '#e2e8f0', borderWidth: 1 }, '&:hover fieldset': { borderColor: '#cbd5e1' }, - '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring('#662582')}` }, - '&.Mui-focused fieldset': { borderColor: '#662582', borderWidth: 1.5 } + '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring('#C01227')}` }, + '&.Mui-focused fieldset': { borderColor: '#C01227', borderWidth: 1.5 } }, '& .MuiAutocomplete-endAdornment .MuiSvgIcon-root': { color: '#94a3b8' } }} @@ -1344,15 +1293,15 @@ const Deliveries = () => { flexShrink: 0, cursor: 'pointer', borderRadius: DT.radiusField + 'px', - border: `1px solid ${active ? meta.color : DT.borderSubtle}`, - bgcolor: active ? meta.color : DT.surface, + border: `1px solid ${active ? '#C01227' : DT.borderSubtle}`, + bgcolor: active ? '#C01227' : DT.surface, color: active ? '#fff' : DT.textSecondary, fontWeight: 600, boxShadow: 'none', transition: 'background-color 0.15s, border-color 0.15s, color 0.15s', '&:hover': { - borderColor: active ? meta.color : DT.borderHover, - bgcolor: active ? meta.color : DT.surfaceAlt + borderColor: active ? '#C01227' : DT.borderHover, + bgcolor: active ? '#C01227' : DT.surfaceAlt } }} > @@ -1442,10 +1391,10 @@ const Deliveries = () => { overflowX: 'auto', '&::-webkit-scrollbar': { width: '10px', height: '10px', cursor: 'pointer' }, '&::-webkit-scrollbar-thumb': { - backgroundColor: edge('#662582'), + backgroundColor: edge('#C01227'), borderRadius: '8px', cursor: 'pointer', - '&:hover': { backgroundColor: '#662582' } + '&:hover': { backgroundColor: '#C01227' } }, '&::-webkit-scrollbar-track': { backgroundColor: DT.surfaceAlt } }} @@ -1562,7 +1511,7 @@ const Deliveries = () => { handleMenuOpen(e, row)} - sx={{ borderRadius: 999, bgcolor: tint('#662582'), color: '#662582', border: `1px solid ${edge('#662582')}`, '&:hover': { bgcolor: soft('#662582') } }} + sx={{ borderRadius: 999, bgcolor: tint('#C01227'), color: '#C01227', border: `1px solid ${edge('#C01227')}`, '&:hover': { bgcolor: soft('#C01227') } }} > @@ -1650,7 +1599,7 @@ const Deliveries = () => { {row.step ? ( - {row.step} + {row.step} ) : ( )} @@ -1664,8 +1613,8 @@ const Deliveries = () => { {isOpen && ( - - + + Product Details @@ -2065,9 +2014,9 @@ const Deliveries = () => { height: 24, px: 0.875, borderRadius: 999, - bgcolor: tint('#662582'), - border: `1px solid ${edge('#662582')}`, - color: '#662582', + bgcolor: tint('#C01227'), + border: `1px solid ${edge('#C01227')}`, + color: '#C01227', fontWeight: 800, fontSize: 11 }} @@ -2133,10 +2082,10 @@ const Deliveries = () => { onClick={(e) => handleMenuOpen(e, row)} sx={{ borderRadius: 999, - bgcolor: tint('#662582'), - color: '#662582', - border: `1px solid ${edge('#662582')}`, - '&:hover': { bgcolor: soft('#662582') } + bgcolor: tint('#C01227'), + color: '#C01227', + border: `1px solid ${edge('#C01227')}`, + '&:hover': { bgcolor: soft('#C01227') } }} > @@ -2168,9 +2117,9 @@ const Deliveries = () => { background: '#fff' }} > - - - + + + Product Details @@ -2778,14 +2727,14 @@ const Deliveries = () => { borderRadius: 2, bgcolor: '#fff', '& fieldset': { borderColor: DT.borderSubtle }, - '&:hover fieldset': { borderColor: '#662582' }, - '&.Mui-focused fieldset': { borderColor: '#662582', borderWidth: 2 }, - '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring('#662582')}` } + '&:hover fieldset': { borderColor: '#C01227' }, + '&.Mui-focused fieldset': { borderColor: '#C01227', borderWidth: 2 }, + '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring('#C01227')}` } } }} > {['pending','accepted','started','arrived','delivered','cancelled'].map((s) => { - const m = STATUS_META[s] || { label: s, color: '#662582', icon: MdHistoryToggleOff }; + const m = STATUS_META[s] || { label: s, color: '#C01227', icon: MdHistoryToggleOff }; const Ic = m.icon; return ( @@ -2812,9 +2761,9 @@ const Deliveries = () => { borderRadius: 2, bgcolor: '#fff', '& fieldset': { borderColor: DT.borderSubtle }, - '&:hover fieldset': { borderColor: '#662582' }, - '&.Mui-focused fieldset': { borderColor: '#662582', borderWidth: 2 }, - '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring('#662582')}` } + '&:hover fieldset': { borderColor: '#C01227' }, + '&.Mui-focused fieldset': { borderColor: '#C01227', borderWidth: 2 }, + '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring('#C01227')}` } } }} /> diff --git a/src/pages/nearle/dispatch/CLAUDE.md b/src/pages/nearle/dispatch/CLAUDE.md index 39eab6d..2fec8fa 100644 --- a/src/pages/nearle/dispatch/CLAUDE.md +++ b/src/pages/nearle/dispatch/CLAUDE.md @@ -43,7 +43,7 @@ Dispatch.js uses `react-leaflet` for declarative tile/marker rendering, BUT a lo ## 3. The reconcile rule (re-stated because it's load-bearing) -After **any** manual edit on `/nearle/dispatch/preview` (drag-and-drop step reorder, swap rider, change delivery sequence), the page **must** call `POST routes.workolik.com/optimization/reconcile-steps` before `POST jupiter.nearle.app/deliveries/createdeliveries`. +After **any** manual edit on `/doormile/dispatch/preview` (drag-and-drop step reorder, swap rider, change delivery sequence), the page **must** call `POST routes.workolik.com/optimization/reconcile-steps` before `POST jupiter.nearle.app/deliveries/createdeliveries`. Skipping reconcile corrupts route sequences in the database. This is the single biggest production bug to avoid in this area. @@ -74,7 +74,7 @@ The dispatch page renders 100+ markers and polylines on every render. Watch for - Drag-and-drop uses `react-dnd` with `react-dnd-html5-backend`. Don't swap libraries. - After every drop, debounce a call to `reconcileSteps` from `api.js`. Don't call it synchronously on every drag tick — the optimiser will rate-limit you. -- The "Assign" button calls `finalCreatedeliveries` → triggers `notifyRider` for each rider in the payload → redirects to `/nearle/deliveries`. Don't reorder these three steps. +- The "Assign" button calls `finalCreatedeliveries` → triggers `notifyRider` for each rider in the payload → redirects to `/doormile/deliveries`. Don't reorder these three steps. --- diff --git a/src/pages/nearle/dispatch/Dispatch.css b/src/pages/nearle/dispatch/Dispatch.css index 25247c2..e254f05 100644 --- a/src/pages/nearle/dispatch/Dispatch.css +++ b/src/pages/nearle/dispatch/Dispatch.css @@ -3,11 +3,11 @@ --bg-sub: #f8fafc; --bg-card: #ffffff; --border: #e2e8f0; - --border-active: #9255AB; + --border-active: #D25463; --text: #1e293b; --text-muted: #64748b; - --accent: #9255AB; - --accent-soft: rgba(146, 85, 171, 0.08); + --accent: #D25463; + --accent-soft: rgba(210, 84, 99, 0.08); --kitchen: #f59e0b; --kitchen-soft: rgba(245, 158, 11, 0.1); --success: #22c55e; @@ -68,7 +68,7 @@ width: 32px; height: 32px; border-radius: 8px; - background: linear-gradient(135deg, #9255AB, #2563eb); + background: linear-gradient(135deg, #D25463, #2563eb); display: flex; align-items: center; justify-content: center; @@ -308,7 +308,7 @@ background: var(--accent); border-color: var(--accent); color: #fff; - box-shadow: 0 4px 12px rgba(146, 85, 171, 0.25); + box-shadow: 0 4px 12px rgba(210, 84, 99, 0.25); } /* SVG icon slot inside each tab button — fixed square, color inherits from button @@ -389,7 +389,7 @@ .dispatch-container .strat-stat-orders { background: var(--accent-soft); - border-color: rgba(146, 85, 171, 0.25); + border-color: rgba(210, 84, 99, 0.25); } .dispatch-container .strat-stat-orders .strat-stat-value { @@ -511,22 +511,22 @@ } .dispatch-container .date-chip.is-open { - border-color: #662582; - box-shadow: 0 0 0 3px rgba(102, 37, 130, 0.2), - 0 12px 30px rgba(102, 37, 130, 0.22); + border-color: #C01227; + box-shadow: 0 0 0 3px rgba(192, 18, 39, 0.2), + 0 12px 30px rgba(192, 18, 39, 0.22); } .dispatch-container .date-chip:hover { - border-color: rgba(102, 37, 130, 0.45); + border-color: rgba(192, 18, 39, 0.45); box-shadow: 0 2px 4px rgba(15, 23, 42, 0.06), - 0 8px 22px rgba(102, 37, 130, 0.15); + 0 8px 22px rgba(192, 18, 39, 0.15); transform: translateY(-1px); } .dispatch-container .date-chip:focus-within { - border-color: #662582; - box-shadow: 0 0 0 3px rgba(102, 37, 130, 0.2), - 0 8px 22px rgba(102, 37, 130, 0.22); + border-color: #C01227; + box-shadow: 0 0 0 3px rgba(192, 18, 39, 0.2), + 0 8px 22px rgba(192, 18, 39, 0.22); } /* Center card — visible chrome the operator reads. Renders as a - ( - - )} - content={() => componentRef.current} - /> - - - - {/* minWidth keeps the invoice at a legible fixed layout on phones — - the parent's overflowX:auto then lets it scroll horizontally - instead of squishing the header into vertical slivers. 720px sits - within the print page width, so printing is unaffected. */} -
    - - - - - - {' '} - - {/* - {`Invoice No: ${"\u00a0\u00a0\u00a0"}${selected.invoiceno}`} - */} - - - Invoice No : - - {`${'\u00a0\u00a0'}${selected.invoiceno}`} - - - - - - Date :{' '} - - - {dayjs(selected.transactiondate).format('DD-MM-YYYY')} - - - - - Due Date : - - {dayjs(selected.dueDate).format('DD-MM-YYYY')} - - {/* - - Invoice No : - - - {`${"\u00a0\u00a0\u00a0"}${selected.invoiceno}`} - - */} - - - - - - - - - - From: - - Nearle Technology Privite Limited. - - 424, 4thfloor, - - Red rose towers, - DB Road, RS Puram, - 641002. - care@nearle.in - 9047968666 - - - - - - - - - - - - To: - - {selected.tenantname} - {selected.address} - {selected.suburb} - {selected.city} - {selected.state}{' '} - - - - - - - - - - - - - - S.No - Particulars - Unit - Quantity - Rate - {/* {selected && selected.pricingtypeid === 73 && ( */} - Other Charges - {/* )} */} - Amount - - - {selected.tenantsalesdetails && ( - - - 1 - - - {`Invoice from ${dayjs(selected.tenantsalesdetails[0].fromdate).format('DD-MM-YYYY')} to ${dayjs( - selected.tenantsalesdetails[0].todate - ).format('DD-MM-YYYY')}`} - - - - {selected.tenantsalesdetails[0].pricingtype} - - - - {`${selected.tenantsalesdetails[0].quantity.toFixed(2)} km`} - - - {`₹ ${selected.tenantsalesdetails[0].baserate.toFixed(2)}`} - - {/* {selected.tenantsalesdetails[0].pricingtypeid == 73 && ( */} - - {`₹ ${selected.tenantsalesdetails[0].othercharges}.00`} - - {/* )} */} - - {`₹ ${selected.tenantsalesdetails[0].amount}.00`} - - - - )} -
    -
    - - - - - - - Sub Total: - {formatNumberToRupees(selected.salesamount)} - - - Discount: - - - {formatNumberToRupees(selected.discountamt)} - - - - Tax: - - + {formatNumberToRupees(selected.taxamount)} - - - - - Grand Total: - - {formatNumberToRupees(Math.round(selected.totalamount))} - - - - - -
    - - - Notes: {selected.remarks} - - -
    -
    - {/* ================================================= || updatePayment Dialog || ================================================= */} - { - setpaydialog(false); - }} - maxWidth={'sm'} - fullWidth - > - - - - ₹ - - - Update Payment - - - - - - Reference No - { - setRefnumber(e.target.value); - }} - /> - - - Remarks - { - setRemarks(e.target.value); - }} - /> - - - - - - - - - ); -}; - -export default InvoicePreview; diff --git a/src/pages/nearle/login.js b/src/pages/nearle/login.js index 03fb252..fe27c32 100644 --- a/src/pages/nearle/login.js +++ b/src/pages/nearle/login.js @@ -1,45 +1,90 @@ import { useState, useEffect } from 'react'; -import { enqueueSnackbar, closeSnackbar } from 'notistack'; -import AnimateButton from 'components/@extended/AnimateButton'; -import OtpInput from 'react18-input-otp'; - -import { Box, Card, CardContent, Stack, TextField, Button, Typography, Link, FormLabel, IconButton, InputAdornment } from '@mui/material'; -import { useTheme } from '@mui/material/styles'; +import { enqueueSnackbar } from 'notistack'; import axios from 'axios'; import { useNavigate } from 'react-router-dom'; import Loader from 'components/Loader'; -import logo from 'assets/images/logo-nearle1.png'; -import expressImage from 'assets/images/express.png'; +import logo from 'assets/images/doormile-logo.png'; + import { useSelector, useDispatch } from 'react-redux'; import { OpenToast } from 'components/third-party/OpenToast'; import { closeGlobalToast, GlobalToast } from 'components/nearle_components/GlobalToast'; -import Visibility from '@mui/icons-material/Visibility'; -import VisibilityOff from '@mui/icons-material/VisibilityOff'; import { setLoginUser } from 'store/reducers/loginUserSlice'; import { markSessionStart } from 'utils/session'; +import { DT } from 'themes/dt/tokens'; + +// Astryx design system — see themes/astryx.js for the Doormile brand theme +// and CLAUDE.md's block for the CLI workflow. +// NOTE: custom CSS (xstyle/stylex.create()) isn't wired up yet — see the +// comment in config-overrides.js. Everything below uses Astryx component +// props only; the brand gradient panel and the two logo images are plain +// native elements with inline `style` for that reason. +import { AppShell } from '@astryxdesign/core/AppShell'; +import { Theme } from '@astryxdesign/core/theme'; +import { HStack } from '@astryxdesign/core/HStack'; +import { VStack } from '@astryxdesign/core/VStack'; +import { Center } from '@astryxdesign/core/Center'; +import { Card } from '@astryxdesign/core/Card'; +import { Heading } from '@astryxdesign/core/Heading'; +import { Text } from '@astryxdesign/core/Text'; +import { TextInput } from '@astryxdesign/core/TextInput'; +import { Button } from '@astryxdesign/core/Button'; +import { Link } from '@astryxdesign/core/Link'; +import { doormileTheme } from 'themes/astryx'; + +const brandPanelStyle = { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + position: 'relative', + overflow: 'hidden', + width: '46%', + height: '100vh', + color: '#fff', + padding: 48, + background: `linear-gradient(150deg, ${DT.brand} 0%, #D25463 100%)` +}; + +const logoLockupStyle = { position: 'absolute', top: 48, left: 48, maxHeight: 60 }; + +const bulletDotStyle = { + width: 22, + height: 22, + borderRadius: '50%', + backgroundColor: 'rgba(255, 255, 255, 0.18)', + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + fontSize: 13, + fontWeight: 700, + flexShrink: 0 +}; + +// doormile-logo.png is a white asset; recolour to brand red for this +// white-background card (the brand-panel logo stays white as-is). +const formLogoStyle = { + maxHeight: 48, + filter: 'brightness(0) saturate(100%) invert(15%) sepia(93%) saturate(5437%) hue-rotate(346deg) brightness(81%) contrast(92%)' +}; + +const BULLETS = ['Real-time fleet visibility', 'AI-optimised dispatch routes', 'Tenant, pricing & invoice control']; const Login = () => { const dispatch = useDispatch(); const fcmtoken = useSelector((state) => state.fcm); - const permission = useSelector((state) => state.fcm.permission); - const theme = useTheme(); const [loading, setLoading] = useState(false); let navigate = useNavigate(); const [otp, setOtp] = useState(''); - const [currentotp, setCurrentotp] = useState(''); const [userinfo, setUserinfo] = useState({}); const [username, setUsername] = useState(''); const [passwordStatus, setPasswordStatus] = useState(0); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [isPassword, setIspassword] = useState(false); - const [showPassword, setShowPassword] = useState(false); - const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [userid, setUserid] = useState(0); useEffect(() => { if (localStorage.getItem('firstname')) { - navigate('/nearle/dispatch'); + navigate('/doormile/dispatch'); } }, []); @@ -100,7 +145,7 @@ const Login = () => { localStorage.setItem('userfcmtoken', userinfo.userfcmtoken); markSessionStart(); fetchAppLocations(userinfo.userid); - navigate('/nearle/dispatch'); + navigate('/doormile/dispatch'); } else { OpenToast(res.data.message, 'error', 3000); } @@ -123,7 +168,7 @@ const Login = () => { markSessionStart(); closeGlobalToast(); // to close the pin snackbar - navigate('/nearle/dispatch'); + navigate('/doormile/dispatch'); }; const opentoast = (message) => { @@ -152,320 +197,157 @@ const Login = () => { } }; + const handleSubmit = (e) => { + e.preventDefault(); + if (passwordStatus == 0) { + loginsend(); + } else if (passwordStatus == 1) { + if (!password || !confirmPassword || password != confirmPassword) { + OpenToast('Check Password', 'warning', 3000); + } else { + updateUser(); + } + } else if (passwordStatus == 2) { + if (!password) { + OpenToast('Invalid Password', 'warning', 3000); + } + loginsend(); + } + }; + return ( - - {loading && } + + + {loading && } + + {/* ---- Left brand panel (plain element: see the note at the top of + this file about custom styling not going through Astryx yet) ---- */} +
    + Doormile - {/* ---- Left brand panel (hidden on small screens) ---- */} - - {/* Logo at the top-left corner */} - Operate your dispatch + + + Operate your dispatch, end to end. + + + Orders, AI route optimisation, live rider tracking and billing — all in the Doormile Express operator console. + - {/* decorative light glows */} - - + + {BULLETS.map((t) => ( + + + {t} + + ))} + + +
    - - - Operate your dispatch, -
    - end to end. -
    - - Orders, AI route optimisation, live rider tracking and billing — all in the NearlExpress operator console. - + {/* ---- Right form panel ---- */} +
    + + + + Doormile + Welcome back + Sign in to the Doormile Express console + - - {['Real-time fleet visibility', 'AI-optimised dispatch routes', 'Tenant, pricing & invoice control'].map((t) => ( - - - ✓ - - {t} - - ))} - - - - - {/* ---- Right form panel ---- */} - - - - {/* Logo */} - - loginpagelogo - - - {/* Title */} - - Welcome back - - - Sign in to the NearlExpress console - - - -
    { - e.preventDefault(); - if (passwordStatus == 0) { - loginsend(); - } else if (passwordStatus == 1) { - if (!password || !confirmPassword || password != confirmPassword) { - OpenToast('Check Password', 'warning', 3000); - } else { - updateUser(); - } - } else if (passwordStatus == 2) { - if (!password) { - OpenToast('Invalid Password', 'warning', 3000); - } - loginsend(); - } - // if (currentotp) { - // if (currentotp == otp) { - // loginsuccessful(); - // fetchAppLocations(); - // } else { - // opentoast('Invalid pin'); - // } - // } - }} - > - - {/* Email */} - setUsername(e.target.value.toLocaleLowerCase())} - InputProps={{ readOnly: passwordStatus }} - /> - {/* Setup Password */} - {passwordStatus == 1 && ( - - - Setup Password - - setPassword(e.target.value)} - InputProps={{ - endAdornment: ( - - setShowPassword((prev) => !prev)} edge="end"> - {showPassword ? : } - - - ) - }} + + + setUsername(value.toLocaleLowerCase())} + isDisabled={!!passwordStatus} /> - setConfirmPassword(e.target.value)} - InputProps={{ - endAdornment: ( - - setShowConfirmPassword((prev) => !prev)} edge="end"> - {showConfirmPassword ? : } - - - ) - }} - /> - - )} - {/* Enter Password */} - {passwordStatus == 2 && ( - - - Enter Password - - setPassword(e.target.value)} - InputProps={{ - endAdornment: ( - - setShowPassword((prev) => !prev)} edge="end"> - {showPassword ? : } - - - ) - }} - /> - - )} + {/* Setup Password */} + {passwordStatus == 1 && ( + + Setup Password + setPassword(value)} + /> + setConfirmPassword(value)} + status={ + confirmPassword !== '' && password !== confirmPassword + ? { type: 'error', message: 'Passwords do not match' } + : undefined + } + /> + + )} - {/* OTP */} - {isPassword && ( - - - Enter Password - { - setOtp(''); - loginsend(); - }} - > - Retry - - + {/* Enter Password */} + {passwordStatus == 2 && ( + + Enter Password + setPassword(value)} + /> + + )} - {/* setOtp(otp)} - numInputs={4} - containerStyle={{ justifyContent: 'space-between' }} - inputStyle={{ - width: 48, - height: 48, - borderRadius: 8, - border: `1px solid ${borderColor}`, - fontSize: 18 - }} - focusStyle={{ - outline: 'none', - border: `1px solid ${theme.palette.primary.main}`, - boxShadow: theme.customShadows.primary - }} - /> */} - setPassword(e.target.value)} /> - - )} - {/* Submit */} - - - - -
    -
    -
    + {/* OTP / retry */} + {isPassword && ( + + + Enter Password + { + setOtp(''); + loginsend(); + }} + > + Retry + + + setPassword(value)} isLabelHidden /> + + )} - {/* footer */} - - - © All rights reserved - - - Terms and Conditions - - - Privacy Policy - - -
    -
    - +
    +
    +
    +
    ); }; diff --git a/src/pages/nearle/login1.js b/src/pages/nearle/login1.js index ab23a9f..1d1715b 100644 --- a/src/pages/nearle/login1.js +++ b/src/pages/nearle/login1.js @@ -22,7 +22,11 @@ import { useTheme } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import AnimateButton from 'components/@extended/AnimateButton'; -import logo from 'assets/images/logo-nearle1.png'; +import logo from 'assets/images/doormile-logo.png'; + +// doormile-logo.png is a white asset; recolour it to brand red for this page's light background. +const DOORMILE_RED_FILTER = + 'brightness(0) saturate(100%) invert(15%) sepia(93%) saturate(5437%) hue-rotate(346deg) brightness(81%) contrast(92%)'; import axios from 'axios'; import { useNavigate } from 'react-router-dom'; @@ -228,7 +232,7 @@ const Login = () => { // sx={{ ml: 3, mt: 3 }} sx={{ ml: { xs: 0, md: 3 }, mt: { xs: 3, md: 1 }, textAlign: { xs: 'center', md: 'left' } }} > - legendary + Doormile
    { const { data: paymentModes = [], - isLoading: paymentModesLoading, - isError: paymentModesError, - error: paymentModesErrorMessage + isLoading: paymentModesLoading } = useQuery({ queryKey: ['paymentmodes'], queryFn: fetchPaymentType @@ -246,10 +245,7 @@ const OrdersPreview = () => { const { data: ridersList = [], - isLoading: ridersListLoading, - isError: ridersListError, - error: ridersListErrorMessage, - refetch: ridersListRefetch + isLoading: ridersListLoading } = useQuery({ queryKey: ['ridersList', appId], // Unique key for caching & re-fetching queryFn: fetchRidersList, @@ -282,13 +278,13 @@ const OrdersPreview = () => { onSuccess: (data, variables) => { console.log('data', data); console.log('varialbles', variables); - notifyRiderMutation.mutate(rider.userfcmtoken || riderToken); // Call notifyRider after success + notifyRiderMutation.mutate(rider?.userfcmtoken || riderToken); // Call notifyRider after success if (data.status == 'accepted') { OpenToast('Delivery Created Successfully', 'success', 2000); } setTimeout(() => { setIsLoading(false); - navigate('/nearle/deliveries'); + navigate('/nearle/orders'); }, 2000); }, onError: (error) => { @@ -581,11 +577,6 @@ const OrdersPreview = () => { {index + 1} - {/* {aiMode == 1 && ( - - - - )} */} @@ -656,12 +647,6 @@ const OrdersPreview = () => { {val.ordernotes} - {/* {aiMode == 1 && ( - - {val.username} - ID : {val.userid} - - )} */} { disabled={aiMode === 0 && (!rider || !payment)} onClick={handleManualCreateDelivery} > - Assign Orders + Finalise
    diff --git a/src/pages/nearle/orders/OrdersRedesign.css b/src/pages/nearle/orders/OrdersRedesign.css index 950f7af..cc2c39c 100644 --- a/src/pages/nearle/orders/OrdersRedesign.css +++ b/src/pages/nearle/orders/OrdersRedesign.css @@ -1,554 +1,50 @@ -/* OrdersRedesign.css - Premium Aesthetics & Micro-Animations */ +/* OrdersRedesign.css — premium aesthetics + micro-animations + Ported from xpressconsole; trimmed to only the classes the + redesigned multipleOrders.js (and its peers) consume here. */ -/* ============================================== */ -/* Pickup / Drop Location Panel (Redesign) */ -/* ============================================== */ -.location-panel { - position: relative; - padding: 12px 14px; - border-radius: 12px; - border: 1px solid #eef2f6; - background: linear-gradient(180deg, #ffffff 0%, #fbfcff 100%); - transition: box-shadow 0.3s ease, transform 0.3s ease; - height: 100%; -} - -.location-panel:hover { - box-shadow: 0 10px 30px -10px rgba(62, 73, 84, 0.10); -} - -.location-panel::before { - content: ''; - position: absolute; - left: 0; - top: 22px; - bottom: 22px; - width: 4px; - border-radius: 0 4px 4px 0; -} - -.pickup-panel::before { - background: linear-gradient(180deg, #1890ff, #096dd9); -} - -.drop-panel::before { - background: linear-gradient(180deg, #a855f7, #65387a); -} - -.lp-header { - display: flex; - justify-content: space-between; - align-items: center; - gap: 10px; - flex-wrap: wrap; - margin-bottom: 12px; - padding-bottom: 10px; - border-bottom: 1px dashed #e2e8f0; -} - -.lp-header-actions { - display: inline-flex; - align-items: center; - gap: 8px; - flex-wrap: wrap; -} - -/* Completion pill in panel header */ -.lp-completion-pill { - display: inline-flex; - align-items: center; - font-size: 11.5px; - font-weight: 700; - letter-spacing: 0.5px; - text-transform: uppercase; - padding: 4px 11px; - border-radius: 999px; - background: rgba(148, 163, 184, 0.14); - color: #64748b; - border: 1px solid rgba(148, 163, 184, 0.25); - line-height: 1; -} - -.lp-completion-pill--ok { - background: rgba(34, 197, 94, 0.12); - color: #15803d; - border-color: rgba(34, 197, 94, 0.30); -} - -.lp-completion-pill--ok::before { - content: '✓'; - margin-right: 4px; - font-weight: 900; -} - -/* Distance pill — sits alongside the completion pill in each panel header. - Same shape language as the completion pill so the header reads as a coherent row of chips. */ -.lp-distance-pill { - display: inline-flex; - align-items: center; - gap: 4px; - font-size: 11.5px; - font-weight: 700; - letter-spacing: 0.3px; - padding: 4px 10px; - border-radius: 999px; - background: linear-gradient(135deg, rgba(24, 144, 255, 0.10), rgba(168, 85, 247, 0.10)); - color: #1e293b; - border: 1px solid rgba(101, 56, 122, 0.20); - line-height: 1; - white-space: nowrap; -} - -.lp-distance-pill .lp-distance-arrow { - color: #94a3b8; - font-weight: 600; -} - -.pickup-panel .lp-distance-pill { - border-color: rgba(24, 144, 255, 0.25); -} - -.drop-panel .lp-distance-pill { - border-color: rgba(101, 56, 122, 0.25); -} - -/* Compact icon-only swap button placed inside each panel header's action row. - Color follows the panel's accent so it doesn't look like a generic floating button. */ -.lp-swap-btn { - width: 32px !important; - height: 32px !important; - border-radius: 10px !important; - border: 1px solid #e2e8f0 !important; - background: #ffffff !important; - color: #475569 !important; - transition: all 0.2s ease; -} - -.lp-swap-btn:hover { - background: #f8fafc !important; - border-color: #cbd5e1 !important; - color: #1e293b !important; - transform: rotate(180deg); -} - -.pickup-panel .lp-swap-btn:hover { - color: #1890ff !important; - border-color: rgba(24, 144, 255, 0.35) !important; -} - -.drop-panel .lp-swap-btn:hover { - color: #65387a !important; - border-color: rgba(101, 56, 122, 0.35) !important; -} - -.lp-swap-btn.Mui-disabled { - background: #f8fafc !important; - border-color: #eef2f6 !important; - color: #cbd5e1 !important; -} - -.lp-header-title { - display: flex; - align-items: center; - gap: 12px; -} - -.lp-badge { - width: 34px; - height: 34px; - border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; - font-size: 15px; - color: #fff; - flex-shrink: 0; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.10); -} - -.pickup-panel .lp-badge { - background: linear-gradient(135deg, #1890ff, #096dd9); - box-shadow: 0 6px 14px rgba(24, 144, 255, 0.30); -} - -.drop-panel .lp-badge { - background: linear-gradient(135deg, #a855f7, #65387a); - box-shadow: 0 6px 14px rgba(101, 56, 122, 0.30); -} - -.lp-title { - font-size: 15px; - font-weight: 700; - color: #1e293b; - line-height: 1.2; -} - -.lp-subtitle { - font-size: 11.5px; - color: #94a3b8; - margin-top: 1px; -} - -.lp-action-btn { - text-transform: none !important; - font-weight: 600 !important; - font-size: 12px !important; - border-radius: 8px !important; - padding: 5px 11px !important; - letter-spacing: 0.2px !important; - min-height: 30px !important; -} - -.pickup-panel .lp-action-btn { - color: #1890ff !important; - background: rgba(24, 144, 255, 0.08) !important; -} - -.pickup-panel .lp-action-btn:hover { - background: rgba(24, 144, 255, 0.16) !important; -} - -.drop-panel .lp-action-btn { - color: #65387a !important; - background: rgba(168, 85, 247, 0.10) !important; -} - -.drop-panel .lp-action-btn:hover { - background: rgba(168, 85, 247, 0.18) !important; -} - -.lp-action-btn.Mui-disabled { - color: #cbd5e1 !important; - background: #f8fafc !important; -} - -/* Ghost variant used for secondary actions (e.g. "Same as Pickup") */ -.lp-action-btn--ghost { - color: #475569 !important; - background: transparent !important; - border: 1px dashed #cbd5e1 !important; -} - -.lp-action-btn--ghost:hover { - color: #1e293b !important; - background: #f8fafc !important; - border-color: #94a3b8 !important; -} - -.lp-action-btn--ghost.Mui-disabled { - color: #cbd5e1 !important; - background: transparent !important; - border-color: #e2e8f0 !important; -} - -/* Inline helper under the address search input — replaces the boxed wrapper */ -.address-search-helper { - display: flex !important; - align-items: center !important; - gap: 6px !important; - font-size: 13px !important; - font-weight: 500 !important; - margin-top: 6px !important; - margin-left: 2px !important; -} - -.address-search-helper.pickup-helper { - color: #1890ff !important; -} - -.address-search-helper.drop-helper { - color: #65387a !important; -} - -/* Field group caption */ -.field-group-caption { - display: flex; - align-items: center; - gap: 8px; - font-size: 10.5px; - font-weight: 700; - letter-spacing: 0.7px; - text-transform: uppercase; - color: #64748b; - margin-bottom: 8px; -} - -.field-group-caption::after { - content: ''; - flex: 1; - height: 1px; - background: linear-gradient(90deg, #e2e8f0 0%, transparent 100%); -} - -.field-group-dot { - width: 7px; - height: 7px; - border-radius: 50%; - display: inline-block; -} - -.pickup-panel .field-group-dot { - background: #1890ff; -} - -.drop-panel .field-group-dot { - background: #65387a; -} - -/* Address search highlight */ -.address-search-block { - background: rgba(24, 144, 255, 0.04); - border-radius: 12px; - padding: 16px; - border: 1px solid rgba(24, 144, 255, 0.12); - transition: border-color 0.2s ease; -} - -.drop-panel .address-search-block { - background: rgba(168, 85, 247, 0.04); - border: 1px solid rgba(101, 56, 122, 0.12); -} - -.address-search-block:focus-within { - border-color: rgba(24, 144, 255, 0.4); -} - -.drop-panel .address-search-block:focus-within { - border-color: rgba(101, 56, 122, 0.4); -} - -.address-search-hint { - display: flex; - align-items: center; - gap: 6px; - font-size: 11.5px; - color: #64748b; - margin-bottom: 12px; - font-weight: 500; -} - -/* Save-for-later pill */ -.save-later-pill { - display: inline-flex; - align-items: center; - gap: 6px; - background: linear-gradient(135deg, rgba(34, 197, 94, 0.10), rgba(34, 197, 94, 0.04)); - border: 1px dashed #86efac; - border-radius: 999px; - padding: 4px 12px 4px 8px; - margin-top: 4px; -} - -.save-later-pill .MuiFormControlLabel-root { - margin: 0; -} - -.save-later-pill .MuiCheckbox-root { - padding: 4px; - color: #22c55e; -} - -.save-later-pill .MuiTypography-root { - font-size: 13.5px; - font-weight: 600; - color: #15803d; -} - -/* ============================================== */ -/* Vertical Route Flow (Pickup -> Drop) */ -/* ============================================== */ -.route-flow { - display: flex; - flex-direction: column; - gap: 18px; -} - -.route-flow-link { - display: flex; - align-items: center; - justify-content: flex-start; - position: relative; - padding: 22px 0 22px 31px; - gap: 18px; - min-height: 80px; -} - -.route-flow-link::before { - content: ''; - position: absolute; - left: 46px; - top: 0; - bottom: 0; - width: 2px; - background: repeating-linear-gradient(to bottom, #cbd5e1, #cbd5e1 4px, transparent 4px, transparent 8px); - z-index: 1; -} - -.route-flow-arrow { - position: relative; - z-index: 2; - display: inline-flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - border-radius: 50%; - background: #ffffff; - border: 2px solid #cbd5e1; - color: #64748b; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04); - transition: all 0.25s ease; -} - -.route-flow-arrow:hover { - background: linear-gradient(135deg, #1890ff, #65387a); - color: #fff; - border-color: transparent; - transform: scale(1.05); -} - -.route-flow-distance { - display: inline-flex; - align-items: center; - gap: 8px; - background: linear-gradient(135deg, rgba(24, 144, 255, 0.08), rgba(168, 85, 247, 0.08)); - border: 1px dashed rgba(101, 56, 122, 0.25); - border-radius: 999px; - padding: 6px 16px; - font-size: 14px; - font-weight: 600; - color: #475569; -} - -.route-flow-distance .dist-value { - color: #1890ff; - font-weight: 700; - font-size: 14.5px; -} - -.route-flow-distance .dist-label { - color: #64748b; - font-weight: 500; - font-size: 13.5px; -} - -.route-flow-distance .dist-route { - display: inline-flex; - align-items: center; - gap: 5px; - margin-right: 2px; -} - -.route-flow-distance .dist-dot { - width: 7px; - height: 7px; - border-radius: 50%; - display: inline-block; -} - -.route-flow-distance .dist-dot--pick { - background: #1890ff; - box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.18); -} - -.route-flow-distance .dist-dot--drop { - background: #65387a; - box-shadow: 0 0 0 2px rgba(101, 56, 122, 0.18); -} - -.route-flow-distance .dist-arrow { - color: #94a3b8; - font-size: 11px; - line-height: 1; -} - -.route-flow-distance .dist-placeholder { - color: #94a3b8; - font-weight: 500; - font-style: italic; - font-size: 13.5px; -} - -/* Swap pickup ↔ drop button in the route connector */ -.route-flow-swap { - margin-left: auto !important; - display: inline-flex !important; - align-items: center !important; - gap: 6px !important; - text-transform: none !important; - font-weight: 600 !important; - font-size: 13.5px !important; - letter-spacing: 0.2px !important; - color: #475569 !important; - background: #ffffff !important; - border: 1px solid #e2e8f0 !important; - border-radius: 999px !important; - padding: 6px 14px !important; - min-width: 0 !important; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04); - transition: all 0.2s ease; -} - -.route-flow-swap:hover { - color: #1e293b !important; - background: #f8fafc !important; - border-color: #cbd5e1 !important; - transform: translateY(-1px); - box-shadow: 0 4px 10px -2px rgba(0, 0, 0, 0.08); -} - -.route-flow-swap.Mui-disabled { - color: #cbd5e1 !important; - border-color: #eef2f6 !important; - background: #ffffff !important; - box-shadow: none; -} - - -/* Workspace background — dashboard shell. - Outer page is locked to 100vh on md+, no body scroll. Columns scroll internally. - sx-level rules on the JSX wrapper drive height/overflow per breakpoint — keep this - class background-only so it doesn't fight inline styles. */ +/* ---------- Page background ---------- */ .orders-workspace-bg { background: linear-gradient(135deg, #f8fafc 0%, #edf2f7 100%) !important; } -/* Internal-scroll columns: thin, on-hover scrollbar that doesn't intrude on the design */ -.dashboard-scroll-col { - scrollbar-width: thin; - scrollbar-color: rgba(148, 163, 184, 0.4) transparent; +/* ---------- Cards ---------- */ +.orders-card { + background: #ffffff !important; + border: 1px solid #eef2f6 !important; + border-radius: 12px !important; + box-shadow: 0 10px 25px -5px rgba(62, 73, 84, 0.04), + 0 4px 12px -2px rgba(62, 73, 84, 0.02) !important; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; + overflow: hidden; +} +.orders-card:hover { + box-shadow: 0 16px 35px -8px rgba(62, 73, 84, 0.08), + 0 6px 16px -3px rgba(62, 73, 84, 0.03) !important; } -.dashboard-scroll-col::-webkit-scrollbar { - width: 8px; +.orders-card .MuiOutlinedInput-root { + font-size: 13px !important; + border-radius: 10px !important; +} +.orders-card .MuiOutlinedInput-input { + font-size: 13px !important; + padding-top: 9px !important; + padding-bottom: 9px !important; +} +.orders-card .MuiInputLabel-root { + font-size: 13px !important; +} +.orders-card .MuiInputLabel-root.MuiInputLabel-shrink { + font-size: 11.5px !important; } -.dashboard-scroll-col::-webkit-scrollbar-track { - background: transparent; -} - -.dashboard-scroll-col::-webkit-scrollbar-thumb { - background-color: rgba(148, 163, 184, 0.35); - border-radius: 999px; - border: 2px solid transparent; - background-clip: padding-box; -} - -.dashboard-scroll-col:hover::-webkit-scrollbar-thumb { - background-color: rgba(100, 116, 139, 0.55); - background-clip: padding-box; -} - -/* Card section title with a leading gradient accent strip. - Used by "Other Details" and "Schedule" cards (and any other card section - that wants a premium-feeling left-edge accent before its h5 title). */ +/* ---------- Section title bar (gradient strip + h-title) ---------- */ .section-title-bar { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; } - .section-title-bar::before { content: ''; width: 4px; @@ -557,969 +53,20 @@ background: linear-gradient(180deg, #1890ff, #096dd9); flex-shrink: 0; } - -/* Accent variant for the Schedule card — uses the purple/violet gradient - from the drop-panel so users get a subtle visual cue distinguishing - sections without needing labels. */ .section-title-bar--accent::before { background: linear-gradient(180deg, #a855f7, #65387a); } - .section-title-bar .MuiTypography-root { margin-bottom: 0 !important; line-height: 1.2; } -/* Service Configuration Horizontal Header Row */ -.service-config-bar { - background: rgba(255, 255, 255, 0.75) !important; - backdrop-filter: blur(16px) !important; - -webkit-backdrop-filter: blur(16px) !important; - border: 1px solid rgba(255, 255, 255, 0.6) !important; - border-radius: 12px !important; - box-shadow: 0 4px 20px -5px rgba(148, 163, 184, 0.1) !important; - padding: 12px 20px !important; - margin-bottom: 12px !important; - flex-shrink: 0; -} - -/* Main Dashboard 3-Column Console Wrapper */ -.console-dashboard-layout { - display: flex; - gap: 16px; - flex: 1; - overflow: hidden; -} - -/* Balanced Left & Right Panes */ -.console-left-pane { - flex: 1.7; - /* ~63% width */ - display: flex; - flex-direction: column; - gap: 12px; - overflow-y: auto; - padding-right: 4px; -} - -.console-right-pane { - flex: 1; - /* ~37% width */ - display: flex; - flex-direction: column; - gap: 12px; - overflow-y: auto; - padding-right: 4px; -} - -/* Custom Scrollbar for Individual Columns to keep console extremely clean */ -.console-left-pane::-webkit-scrollbar, -.console-right-pane::-webkit-scrollbar { - width: 4px; -} - -.console-left-pane::-webkit-scrollbar-track, -.console-right-pane::-webkit-scrollbar-track { - background: transparent; -} - -.console-left-pane::-webkit-scrollbar-thumb, -.console-right-pane::-webkit-scrollbar-thumb { - background: rgba(148, 163, 184, 0.3); - border-radius: 99px; -} - -.console-left-pane::-webkit-scrollbar-thumb:hover, -.console-right-pane::-webkit-scrollbar-thumb:hover { - background: rgba(148, 163, 184, 0.5); -} - -/* Glassmorphism Card Style */ -.glass-card { - background: rgba(255, 255, 255, 0.8) !important; - backdrop-filter: blur(12px) !important; - -webkit-backdrop-filter: blur(12px) !important; - border: 1px solid rgba(255, 255, 255, 0.5) !important; - border-radius: 12px !important; - box-shadow: 0 10px 25px -5px rgba(62, 73, 84, 0.04), 0 4px 12px -2px rgba(62, 73, 84, 0.02) !important; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; -} - -.glass-card:hover { - box-shadow: 0 16px 35px -8px rgba(62, 73, 84, 0.07), 0 6px 16px -3px rgba(62, 73, 84, 0.03) !important; - border-color: rgba(255, 255, 255, 0.8) !important; -} - -/* Premium Focus Glows inside Location Panel inputs */ -.pickup-panel .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline { - border-color: #0ea5e9 !important; - box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.12); -} - -.drop-panel .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline { - border-color: #a855f7 !important; - box-shadow: 0 0 0 3px rgba(168, 85, 247, 0.12); -} - -/* Glowing active weight selector card variants */ -.weight-card-btn.active.weight-light { - background: rgba(14, 165, 233, 0.04) !important; - border-color: #0ea5e9 !important; - box-shadow: 0 6px 16px rgba(14, 165, 233, 0.15) !important; -} - -.weight-card-btn.active.weight-light::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - background: #0ea5e9; -} - -.weight-card-btn.active.weight-medium { - background: rgba(168, 85, 247, 0.04) !important; - border-color: #a855f7 !important; - box-shadow: 0 6px 16px rgba(168, 85, 247, 0.15) !important; -} - -.weight-card-btn.active.weight-medium::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - background: #a855f7; -} - -.weight-card-btn.active.weight-heavy { - background: rgba(99, 102, 241, 0.04) !important; - border-color: #662582 !important; - box-shadow: 0 6px 16px rgba(99, 102, 241, 0.15) !important; -} - -.weight-card-btn.active.weight-heavy::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - background: #662582; -} - -/* Premium Card Overrides */ -.orders-card { - background: #ffffff !important; - border: 1px solid #eef2f6 !important; - border-radius: 12px !important; - box-shadow: 0 10px 25px -5px rgba(62, 73, 84, 0.04), 0 4px 12px -2px rgba(62, 73, 84, 0.02) !important; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; - overflow: hidden; -} - -/* ============================================== */ -/* MUI Field Sizing — Readable on large screens */ -/* ============================================== */ - -/* Compact TextField input + label sizes inside Pickup/Drop panels and order cards */ -.location-panel .MuiOutlinedInput-root, -.orders-card .MuiOutlinedInput-root { - font-size: 13px !important; - border-radius: 10px !important; -} - -.location-panel .MuiOutlinedInput-input, -.orders-card .MuiOutlinedInput-input { - font-size: 13px !important; - padding-top: 9px !important; - padding-bottom: 9px !important; -} - -.location-panel .MuiInputLabel-root, -.orders-card .MuiInputLabel-root { - font-size: 13px !important; -} - -/* When label is shrunk (floating up), keep it slightly smaller for the float effect */ -.location-panel .MuiInputLabel-root.MuiInputLabel-shrink, -.orders-card .MuiInputLabel-root.MuiInputLabel-shrink { - font-size: 11.5px !important; -} - -/* MUI helper text (validation / hints under fields) */ -.location-panel .MuiFormHelperText-root, -.orders-card .MuiFormHelperText-root { - font-size: 11px !important; - margin-top: 3px !important; -} - -/* Autocomplete options dropdown */ -.MuiAutocomplete-popper .MuiAutocomplete-option { - font-size: 13px !important; - padding-top: 6px !important; - padding-bottom: 6px !important; - min-height: 34px !important; -} - -/* Card section titles (h5 / h6) inside order cards — tighter hierarchy */ -.orders-card .MuiTypography-h5 { - font-size: 15px !important; -} - -.orders-card .MuiTypography-h6 { - font-size: 13.5px !important; -} - -.orders-card:hover { - box-shadow: 0 16px 35px -8px rgba(62, 73, 84, 0.08), 0 6px 16px -3px rgba(62, 73, 84, 0.03) !important; -} - -/* Glassmorphic elements */ -.glass-panel { - background: rgba(255, 255, 255, 0.8) !important; - backdrop-filter: blur(12px) !important; - -webkit-backdrop-filter: blur(12px) !important; - border: 1px solid rgba(255, 255, 255, 0.5) !important; -} - -/* Trip Route Visual Connector */ -.connector-line-wrapper { - position: relative; - display: flex; - align-items: center; - justify-content: center; - height: 100%; -} - -.connector-line { - position: absolute; - top: 15%; - bottom: 15%; - width: 2px; - background: repeating-linear-gradient(to bottom, - #1890ff, - #1890ff 6px, - transparent 6px, - transparent 12px); - z-index: 1; -} - -.connector-icon-wrapper { - background: #ffffff; - border: 2px solid #1890ff; - border-radius: 50%; - width: 32px; - height: 32px; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 4px 10px rgba(24, 144, 255, 0.15); - z-index: 2; - transition: all 0.3s ease; -} - -.connector-line-wrapper:hover .connector-icon-wrapper { - transform: scale(1.15) rotate(15deg); - box-shadow: 0 6px 14px rgba(24, 144, 255, 0.25); -} - -/* Custom Interactive Cargo Weight Selector */ -.weight-selector-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 10px; - width: 100%; - margin: 8px 0; -} - -.weight-card-btn { - background: #ffffff; - border: 1.5px solid #eef2f6; - border-radius: 10px; - padding: 10px 8px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - cursor: pointer; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - text-align: center; - user-select: none; - position: relative; - overflow: hidden; -} - -.weight-card-btn:hover { - transform: translateY(-1.5px); - border-color: #cbd5e1; - background-color: #f8fafc; -} - -.weight-card-btn.active { - background: rgba(24, 144, 255, 0.04); - border-color: #1890ff; - box-shadow: 0 4px 12px -2px rgba(24, 144, 255, 0.12); -} - -.weight-card-btn.active::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - background: #1890ff; -} - -.weight-card-icon { - font-size: 18px; - margin-bottom: 4px; - color: #64748b; - transition: all 0.3s ease; -} - -.weight-card-btn.active .weight-card-icon { - color: #1890ff; - transform: scale(1.08); -} - -.weight-card-label { - font-size: 12.5px; - font-weight: 600; - color: #334155; - margin-bottom: 2px; -} - -.weight-card-desc { - font-size: 9.5px; - color: #64748b; - line-height: 1.2; -} - -/* Live Map Preview Container */ -.map-preview-wrapper { - position: relative; - border-radius: 12px; - overflow: hidden; - box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05); - border: 1px solid #eef2f6; - height: 260px; - min-height: 260px; -} - -.map-preview-wrapper .leaflet-container { - height: 100% !important; - min-height: 0 !important; -} - -.map-preview-wrapper > div { - min-height: 0 !important; -} - -/* Premium Cost & Metrics Dashboard — compact professional layout */ -.pricing-summary-card { - background: linear-gradient(135deg, #ffffff 0%, #fafbfc 100%) !important; - border: 1px solid #eef2f6 !important; - border-radius: 14px !important; - padding: 14px 16px !important; -} - -.pricing-header { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: 12px; - margin-bottom: 10px; - padding-bottom: 10px; - border-bottom: 1px solid #f1f5f9; -} - -.pricing-title { - font-size: 14px !important; - font-weight: 700 !important; - color: #1e293b !important; - letter-spacing: -0.01em; -} - -.pricing-subtitle { - font-size: 11px !important; - font-weight: 500 !important; - color: #94a3b8 !important; - text-transform: uppercase; - letter-spacing: 0.6px; -} - -.price-metric-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 7px 0; - border-bottom: 1px solid #f5f7fa; -} - -.price-metric-item:last-of-type { - border-bottom: none; - padding-bottom: 4px; -} - -.price-metric-label { - font-size: 12.5px; - color: #475569; - font-weight: 500; - display: flex; - align-items: center; - gap: 10px; - min-width: 0; -} - -.price-metric-icon { - width: 26px; - height: 26px; - flex-shrink: 0; - border-radius: 8px; - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 12px; -} - -.price-metric-icon.icon-distance { - background: rgba(24, 144, 255, 0.10); - color: #1890ff; -} - -.price-metric-icon.icon-base { - background: rgba(34, 197, 94, 0.10); - color: #16a34a; -} - -.price-metric-icon.icon-rate { - background: rgba(245, 158, 11, 0.12); - color: #d97706; -} - -.price-metric-sub { - color: #94a3b8; - font-weight: 500; - font-size: 11.5px; -} - -.price-metric-value { - font-size: 13.5px; - font-weight: 700; - color: #1e293b; - display: inline-flex; - align-items: baseline; - gap: 2px; - white-space: nowrap; -} - -.price-metric-value.highlight { - color: #1890ff; -} - -.price-metric-unit { - font-size: 11px; - font-weight: 500; - color: #94a3b8; - margin-left: 2px; -} - -.total-charge-badge { - background: linear-gradient(135deg, rgba(24, 144, 255, 0.08) 0%, rgba(101, 56, 122, 0.10) 100%); - border: 1px solid rgba(101, 56, 122, 0.18); - border-radius: 10px; - padding: 10px 14px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - margin-top: 12px; -} - -.total-charge-left { - display: inline-flex; - align-items: center; - gap: 8px; - min-width: 0; -} - -.total-charge-icon { - font-size: 13px; - color: #65387A; - flex-shrink: 0; -} - -.total-charge-label { - font-size: 11.5px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.6px; - color: #65387A; -} - -.total-charge-val { - font-size: 20px; - font-weight: 800; - color: #65387A; - line-height: 1.1; - letter-spacing: -0.01em; - white-space: nowrap; -} - -/* Gradient Action Button — compact professional */ -.gradient-btn-create { - background: linear-gradient(135deg, #1890ff 0%, #65387a 100%) !important; - color: #ffffff !important; - font-weight: 600 !important; - font-size: 13px !important; - letter-spacing: 0.01em !important; - text-transform: none !important; - border-radius: 10px !important; - padding: 8px 18px !important; - min-height: 38px !important; - box-shadow: 0 4px 12px -3px rgba(24, 144, 255, 0.30), 0 2px 4px rgba(101, 56, 122, 0.10) !important; - transition: all 0.22s cubic-bezier(0.4, 0, 0.2, 1) !important; - border: none !important; - display: inline-flex !important; - align-items: center !important; - justify-content: center !important; - gap: 8px !important; -} - -.gradient-btn-create .MuiButton-startIcon, -.gradient-btn-create .MuiButton-endIcon { - margin: 0 !important; -} - -.gradient-btn-create:hover { - filter: brightness(1.04); - box-shadow: 0 8px 18px -4px rgba(24, 144, 255, 0.40), 0 3px 8px rgba(101, 56, 122, 0.18) !important; -} - -.gradient-btn-create:active { - filter: brightness(0.98); -} - -.gradient-btn-create.Mui-disabled, -.gradient-btn-create:disabled { - background: #e2e8f0 !important; - color: #94a3b8 !important; - box-shadow: none !important; - cursor: not-allowed !important; -} - -/* Completion checklist */ -.completion-checklist { - margin: 16px 0; - padding: 0; - list-style: none; -} - -.completion-item { - display: flex; - align-items: center; - gap: 12px; - font-size: 14.5px; - color: #64748b; - margin-bottom: 10px; - transition: all 0.3s ease; - line-height: 1.4; -} - -.completion-item.completed { - color: #0f172a; - font-weight: 600; -} - -.completion-bullet { - width: 10px; - height: 10px; - border-radius: 50%; - background-color: #cbd5e1; - flex-shrink: 0; - transition: all 0.3s ease; -} - -.completion-item.completed .completion-bullet { - background-color: #10b981; - box-shadow: 0 0 8px #10b981; -} - -/* Saved Address Modal Cards */ -.address-card-btn { - width: 100%; - border: 1.5px solid #eef2f6 !important; - border-radius: 12px !important; - padding: 16px !important; - background: #ffffff !important; - text-transform: none !important; - transition: all 0.2s ease !important; - margin-bottom: 12px; -} - -.address-card-btn:hover { - border-color: #1890ff !important; - background-color: rgba(24, 144, 255, 0.02) !important; - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03); -} - -.address-card-btn:disabled { - background-color: #f8fafc !important; - border-color: #e2e8f0 !important; - opacity: 0.7; -} - -/* Status pulses */ -.pulse-indicator { - width: 8px; - height: 8px; - border-radius: 50%; - background: #10b981; - box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); - animation: pulse-green 2s infinite; -} - -@keyframes pulse-green { - 0% { - transform: scale(0.95); - box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); - } - - 70% { - transform: scale(1); - box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); - } - - 100% { - transform: scale(0.95); - box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); - } -} - -.pulse-yellow { - background: #f59e0b; - box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.7); - animation: pulse-yellow-anim 2s infinite; -} - -@keyframes pulse-yellow-anim { - 0% { - transform: scale(0.95); - box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.7); - } - - 70% { - transform: scale(1); - box-shadow: 0 0 0 6px rgba(245, 158, 11, 0); - } - - 100% { - transform: scale(0.95); - box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); - } -} - -/* Form layout responsiveness adjustments */ -@media (max-width: 959px) { - .connector-line-wrapper { - display: none; - } -} - -/* ============================================== */ -/* Side-by-Side Pickup / Drop (md+ breakpoint) */ -/* At md+ the route flow rotates to horizontal: */ -/* [ PICKUP ] [ vertical connector ] [ DROP ] */ -/* ============================================== */ -@media (min-width: 900px) { - .route-flow { - flex-direction: row; - align-items: stretch; - gap: 16px; - } - - /* Each panel takes equal share of the row; min-width: 0 prevents flex blow-out */ - .route-flow > .location-panel { - flex: 1 1 0; - min-width: 0; - } - - /* Stack the inner field grids vertically — at ~340px panel width, side-by-side fields - don't have room for label + icon adornment + input. Force one column per panel. */ - .route-flow > .location-panel .MuiGrid-container > .MuiGrid-item { - flex-basis: 100% !important; - max-width: 100% !important; - } - - /* Connector becomes a narrow vertical strip between the two panels */ - .route-flow-link { - flex-direction: column; - align-items: center; - justify-content: center; - padding: 8px 4px; - min-height: 0; - min-width: 92px; - flex-shrink: 0; - gap: 14px; - } - - /* The vertical dashed line was a guide for the vertical layout — hide it in row mode. - The arrow circle + colored panel borders carry enough visual cue on their own. */ - .route-flow-link::before { - display: none; - } - - /* Rotate the down-arrow to point right (pickup → drop direction) */ - .route-flow-arrow svg { - transform: rotate(-90deg); - transition: transform 0.25s ease; - } - - /* Swap button no longer floats right — it stacks beneath the distance pill */ - .route-flow-swap { - margin-left: 0 !important; - margin-top: 0 !important; - } - - /* Distance pill: stack vertically inside the narrow connector column — - route dots on top, value below — and drop the "Distance" label since the - pill's existence is self-explanatory in this position. */ - .route-flow-distance { - flex-direction: column; - gap: 4px; - padding: 8px 10px; - text-align: center; - line-height: 1.2; - } - - .route-flow-distance .dist-label { - display: none; - } - - /* Compact the placeholder text so it fits when no addresses are set yet */ - .route-flow-distance .dist-placeholder { - font-size: 11px; - line-height: 1.3; - } -} - -/* On very wide screens (xl+), restore the 2-col inner grid inside each panel — - panels are now wide enough (~440px+) for label + icon + input pairs. */ -@media (min-width: 1536px) { - .route-flow > .location-panel .MuiGrid-container > .MuiGrid-item.MuiGrid-grid-sm-6 { - flex-basis: 50% !important; - max-width: 50% !important; - } -} - -/* Act as senior frontend engineer and UI/UX specialist: - Scale down visual elements, paddings, and font sizes on laptop screens - to reclaim precious screen space and maintain a high-density, premium look. */ -@media (max-width: 1400px) { - .orders-card .MuiTypography-h5 { - font-size: 17px !important; - } - - .orders-card .MuiTypography-h6 { - font-size: 14.5px !important; - } - - .lp-title { - font-size: 15.5px; - } - - .lp-subtitle { - font-size: 12px; - margin-top: 1px; - } - - .lp-badge { - width: 36px; - height: 36px; - border-radius: 10px; - font-size: 15px; - } - - .lp-action-btn { - font-size: 12.5px !important; - padding: 5px 10px !important; - } - - .route-flow-link { - padding: 16px 0 16px 26px; - min-height: 60px; - } - - .route-flow-link::before { - left: 41px; - } - - .route-flow-arrow { - width: 28px; - height: 28px; - } - - .route-flow-distance { - padding: 4px 12px; - font-size: 12.5px; - } - - .route-flow-swap { - font-size: 12px !important; - padding: 4px 10px !important; - } - - .map-preview-wrapper { - height: 220px; - min-height: 220px; - } - - .weight-card-btn { - padding: 8px 6px; - } - - .weight-card-label { - font-size: 11.5px; - } - - .weight-card-desc { - font-size: 9px; - } - - .orders-card { - padding: 16px !important; /* Tighten general card padding from 24px */ - } - - .orders-card.page-header-row { - padding: 10px 16px !important; /* Tighten the header row padding */ - } -} - -/* Reclaim unused screen space around the workspace card on desktop and laptop viewports */ -@media (min-width: 900px) { - /* Expand the maximum width of the layout container so it spans the screen and removes empty margins */ - .MuiContainer-root:has(.orders-workspace-bg) { - max-width: 1600px !important; - padding-left: 16px !important; - padding-right: 16px !important; - } - - /* Reduce the spacing outside the page container */ - main:has(.orders-workspace-bg) { - padding: 16px !important; - } -} - -/* ============================================== */ -/* Compact header dropdowns (Location / Client / Business Location) */ -/* ============================================== */ -.header-compact-tf .MuiOutlinedInput-root { - border-radius: 10px !important; - height: 40px !important; - padding-left: 10px !important; - font-size: 12.5px !important; - background: #ffffff; -} - -.header-compact-tf .MuiOutlinedInput-input { - padding-top: 6px !important; - padding-bottom: 6px !important; - font-size: 12.5px !important; -} - -.header-compact-tf .MuiInputLabel-root { - font-size: 11.5px !important; - letter-spacing: 0.02em; - font-weight: 600; - color: #64748b !important; -} - -.header-compact-tf .MuiInputLabel-shrink { - transform: translate(12px, -7px) scale(0.82) !important; - background: #ffffff; - padding: 0 4px; -} - -.header-compact-tf .MuiOutlinedInput-notchedOutline { - border-color: #e2e8f0; -} - -.header-compact-tf:hover .MuiOutlinedInput-notchedOutline { - border-color: #cbd5e1; -} - -.header-compact-tf .Mui-focused .MuiOutlinedInput-notchedOutline { - border-width: 1.5px !important; -} - -/* Autocomplete-specific tweaks: vertically center the clear / popup icons */ -.header-compact-input .MuiAutocomplete-endAdornment { - top: 50%; - transform: translateY(-50%); - right: 8px; - display: inline-flex; - align-items: center; - height: auto; - gap: 2px; -} - -.header-compact-input .MuiAutocomplete-endAdornment .MuiSvgIcon-root { - font-size: 16px; - display: block; -} - -.header-compact-input .MuiAutocomplete-clearIndicator, -.header-compact-input .MuiAutocomplete-popupIndicator { - padding: 3px !important; - width: 22px; - height: 22px; - display: inline-flex; - align-items: center; - justify-content: center; - color: #94a3b8 !important; -} - -.header-compact-input .MuiAutocomplete-clearIndicator:hover, -.header-compact-input .MuiAutocomplete-popupIndicator:hover { - background: rgba(148, 163, 184, 0.12) !important; - color: #475569 !important; -} - -.header-compact-input .MuiAutocomplete-popupIndicator { - margin-right: 0; -} - -.header-compact-input .MuiOutlinedInput-root { - padding-top: 0 !important; - padding-bottom: 0 !important; - padding-right: 60px !important; -} - -.header-compact-input .MuiAutocomplete-input { - padding: 4px 4px 4px 0 !important; - height: auto !important; -} - -/* Title row alignment tweak for tighter header */ -.page-header-row { - min-height: 0 !important; -} - -/* ============================================== */ -/* Delivery Preferences Card */ -/* (Special Dispatch Notes + SMS Updates) */ -/* ============================================== */ +/* ---------- Delivery preferences card ---------- */ .delivery-prefs-card { background: linear-gradient(135deg, #ffffff 0%, #fbfcff 100%) !important; border: 1px solid #eef2f6 !important; border-radius: 14px !important; } - .delivery-prefs-header { display: flex; align-items: baseline; @@ -1529,7 +76,6 @@ padding-bottom: 10px; border-bottom: 1px solid #f1f5f9; } - .delivery-prefs-title { font-size: 14px !important; font-weight: 700 !important; @@ -1537,7 +83,6 @@ letter-spacing: -0.01em; line-height: 1.2; } - .delivery-prefs-sub { font-size: 10.5px !important; font-weight: 500 !important; @@ -1547,19 +92,16 @@ text-align: right; line-height: 1.2; } - .delivery-prefs-row { display: flex; flex-direction: column; gap: 10px; } - .delivery-prefs-field { display: flex; flex-direction: column; gap: 5px; } - .delivery-prefs-label { display: inline-flex; align-items: center; @@ -1571,306 +113,176 @@ color: #64748b; } -/* SMS toggle tile — a card-like clickable strip */ -.sms-toggle-tile { +/* ---------- Pricing summary card ---------- */ +.pricing-summary-card { + background: linear-gradient(135deg, #ffffff 0%, #fafbfc 100%) !important; + border: 1px solid #eef2f6 !important; + border-radius: 14px !important; + padding: 14px 16px !important; +} +.pricing-header { display: flex; - align-items: center; + align-items: baseline; justify-content: space-between; - gap: 10px; - padding: 8px 12px; - border-radius: 10px; - border: 1px solid #eef2f6; - background: #fafbfc; - cursor: pointer; - user-select: none; - transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease; + gap: 12px; + margin-bottom: 10px; + padding-bottom: 10px; + border-bottom: 1px solid #f1f5f9; } - -.sms-toggle-tile:hover { - border-color: #cbd5e1; - background: #ffffff; -} - -.sms-toggle-tile.is-active { - background: linear-gradient(135deg, rgba(24, 144, 255, 0.06) 0%, rgba(101, 56, 122, 0.05) 100%); - border-color: rgba(24, 144, 255, 0.28); - box-shadow: 0 3px 10px -3px rgba(24, 144, 255, 0.18); -} - -.sms-toggle-left { - display: inline-flex; - align-items: center; - gap: 9px; - min-width: 0; -} - -.sms-toggle-icon { - width: 28px; - height: 28px; - flex-shrink: 0; - border-radius: 8px; - display: inline-flex; - align-items: center; - justify-content: center; - background: #eef2f6; - color: #94a3b8; - transition: all 0.2s ease; -} - -.sms-toggle-tile.is-active .sms-toggle-icon { - background: linear-gradient(135deg, #1890ff, #65387a); - color: #ffffff; - box-shadow: 0 3px 10px rgba(101, 56, 122, 0.22); -} - -.sms-toggle-title { - font-size: 12.5px !important; - font-weight: 700 !important; - color: #1e293b !important; - line-height: 1.2 !important; - letter-spacing: -0.005em; -} - -.sms-toggle-sub { - font-size: 10.5px !important; - color: #94a3b8 !important; - font-weight: 500 !important; - margin-top: 1px !important; - line-height: 1.2 !important; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.sms-toggle-tile .MuiSwitch-root { - flex-shrink: 0; -} - -/* ============================================== */ -/* Pickup → Drop Two-Step Stepper */ -/* ============================================== */ -.route-stepper { - display: flex; - align-items: stretch; - gap: 0; - padding: 4px; - margin-bottom: 12px; - background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); - border: 1px solid #e2e8f0; - border-radius: 12px; -} - -.route-step { - flex: 1; - display: flex; - align-items: center; - gap: 10px; - padding: 6px 10px; - border-radius: 9px; - cursor: pointer; - transition: background-color 0.22s ease, box-shadow 0.22s ease, transform 0.22s ease; - user-select: none; - background: transparent; - outline: none; -} - -.route-step:hover { - background: rgba(255, 255, 255, 0.6); -} - -.route-step.is-active { - background: #ffffff; - box-shadow: 0 6px 18px -8px rgba(15, 23, 42, 0.12), 0 2px 6px -2px rgba(15, 23, 42, 0.06); - transform: translateY(-1px); -} - -.route-step.is-locked { - cursor: not-allowed; - opacity: 0.6; -} - -.route-step.is-locked:hover { - background: transparent; -} - -.route-step-index { - width: 26px; - height: 26px; - flex-shrink: 0; - border-radius: 50%; - display: inline-flex; - align-items: center; - justify-content: center; - font-weight: 700; - font-size: 12px; - color: #94a3b8; - background: #ffffff; - border: 1.5px solid #e2e8f0; - transition: all 0.22s ease; -} - -.step-pickup.is-active .route-step-index { - background: linear-gradient(135deg, #1890ff, #096dd9); - border-color: transparent; - color: #ffffff; - box-shadow: 0 4px 12px rgba(24, 144, 255, 0.32); -} - -.step-drop.is-active .route-step-index { - background: linear-gradient(135deg, #a855f7, #65387a); - border-color: transparent; - color: #ffffff; - box-shadow: 0 4px 12px rgba(101, 56, 122, 0.32); -} - -.step-pickup.is-done:not(.is-active) .route-step-index { - background: rgba(34, 197, 94, 0.12); - border-color: rgba(34, 197, 94, 0.35); - color: #16a34a; -} - -.route-step-text { - display: flex; - flex-direction: column; - line-height: 1.2; -} - -.route-step-title { - font-size: 13px !important; +.pricing-title { + font-size: 14px !important; font-weight: 700 !important; color: #1e293b !important; letter-spacing: -0.01em; } - -.route-step.is-locked .route-step-title { - color: #94a3b8 !important; -} - -.route-step-sub { - font-size: 10.5px !important; +.pricing-subtitle { + font-size: 11px !important; font-weight: 500 !important; color: #94a3b8 !important; - margin-top: 1px !important; + text-transform: uppercase; + letter-spacing: 0.6px; } -.route-step-connector { - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - padding: 0 6px; - position: relative; - min-width: 28px; -} - -.route-step-line { - width: 100%; - height: 2px; - background: #e2e8f0; - border-radius: 2px; - transition: background 0.3s ease; -} - -.route-step-connector.is-done .route-step-line { - background: linear-gradient(90deg, #1890ff, #a855f7); -} - -.route-step-line-arrow { - position: absolute; - display: inline-flex; - align-items: center; - justify-content: center; - width: 22px; - height: 22px; - border-radius: 50%; - background: #ffffff; - border: 1.5px solid #e2e8f0; - color: #cbd5e1; - font-size: 10px; - transition: all 0.3s ease; -} - -.route-step-connector.is-done .route-step-line-arrow { - border-color: rgba(168, 85, 247, 0.4); - color: #a855f7; -} - -/* Step navigation footer inside each panel */ -.step-nav { - margin-top: 12px; - padding-top: 10px; - border-top: 1px dashed #e2e8f0; +/* ---------- Total charge badge ---------- */ +.total-charge-badge { + background: linear-gradient(135deg, rgba(24, 144, 255, 0.08) 0%, rgba(101, 56, 122, 0.10) 100%); + border: 1px solid rgba(101, 56, 122, 0.18); + border-radius: 10px; + padding: 10px 14px; display: flex; align-items: center; justify-content: space-between; - gap: 10px; - flex-wrap: wrap; + gap: 12px; + margin-top: 12px; +} +.total-charge-left { + display: inline-flex; + align-items: center; + gap: 8px; + min-width: 0; +} +.total-charge-icon { + font-size: 13px; + color: #65387A; + flex-shrink: 0; +} +.total-charge-label { + font-size: 11.5px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.6px; + color: #65387A; +} +.total-charge-val { + font-size: 20px; + font-weight: 800; + color: #65387A; + line-height: 1.1; + letter-spacing: -0.01em; + white-space: nowrap; } -.step-nav-hint { - font-size: 11.5px !important; - color: #64748b !important; - font-weight: 500 !important; -} - -.step-nav-btn { - text-transform: none !important; - font-weight: 600 !important; - border-radius: 8px !important; - padding: 6px 14px !important; - font-size: 12px !important; - letter-spacing: 0.01em !important; - transition: all 0.22s ease !important; - min-height: 32px !important; -} - -.step-nav-next { - background: linear-gradient(135deg, #1890ff, #65387a) !important; +/* ---------- Gradient action button ---------- */ +.gradient-btn-create { + background: linear-gradient(135deg, #1890ff 0%, #65387a 100%) !important; color: #ffffff !important; - box-shadow: 0 6px 18px -6px rgba(101, 56, 122, 0.35) !important; + font-weight: 600 !important; + font-size: 13px !important; + letter-spacing: 0.01em !important; + text-transform: none !important; + border-radius: 10px !important; + padding: 8px 18px !important; + min-height: 38px !important; + box-shadow: 0 4px 12px -3px rgba(24, 144, 255, 0.30), + 0 2px 4px rgba(101, 56, 122, 0.10) !important; + transition: all 0.22s cubic-bezier(0.4, 0, 0.2, 1) !important; + border: none !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + gap: 8px !important; } - -.step-nav-next:hover { - filter: brightness(1.05); - transform: translateY(-1px); - box-shadow: 0 10px 22px -8px rgba(101, 56, 122, 0.45) !important; +.gradient-btn-create .MuiButton-startIcon, +.gradient-btn-create .MuiButton-endIcon { + margin: 0 !important; } - -.step-nav-next.Mui-disabled { +.gradient-btn-create:hover { + filter: brightness(1.04); + box-shadow: 0 8px 18px -4px rgba(24, 144, 255, 0.40), + 0 3px 8px rgba(101, 56, 122, 0.18) !important; +} +.gradient-btn-create:active { filter: brightness(0.98); } +.gradient-btn-create.Mui-disabled, +.gradient-btn-create:disabled { background: #e2e8f0 !important; color: #94a3b8 !important; box-shadow: none !important; + cursor: not-allowed !important; } -.step-nav-back { +/* ---------- Compact header inputs (Location/Client/Business) ---------- */ +.header-compact-tf .MuiOutlinedInput-root { + border-radius: 10px !important; + height: 40px !important; + padding-left: 10px !important; + font-size: 12.5px !important; + background: #ffffff; +} +.header-compact-tf .MuiOutlinedInput-input { + padding-top: 6px !important; + padding-bottom: 6px !important; + font-size: 12.5px !important; +} +.header-compact-tf .MuiInputLabel-root { + font-size: 11.5px !important; + letter-spacing: 0.02em; + font-weight: 600; + color: #64748b !important; +} +.header-compact-tf .MuiInputLabel-shrink { + transform: translate(12px, -7px) scale(0.82) !important; + background: #ffffff; + padding: 0 4px; +} +.header-compact-tf .MuiOutlinedInput-notchedOutline { border-color: #e2e8f0; } +.header-compact-tf:hover .MuiOutlinedInput-notchedOutline { border-color: #cbd5e1; } +.header-compact-tf .Mui-focused .MuiOutlinedInput-notchedOutline { border-width: 1.5px !important; } + +.header-compact-input .MuiAutocomplete-endAdornment { + top: 50%; + transform: translateY(-50%); + right: 8px; + display: inline-flex; + align-items: center; + height: auto; + gap: 2px; +} +.header-compact-input .MuiAutocomplete-endAdornment .MuiSvgIcon-root { + font-size: 16px; + display: block; +} +.header-compact-input .MuiAutocomplete-clearIndicator, +.header-compact-input .MuiAutocomplete-popupIndicator { + padding: 3px !important; + width: 22px; + height: 22px; + display: inline-flex; + align-items: center; + justify-content: center; + color: #94a3b8 !important; +} +.header-compact-input .MuiAutocomplete-clearIndicator:hover, +.header-compact-input .MuiAutocomplete-popupIndicator:hover { + background: rgba(148, 163, 184, 0.12) !important; color: #475569 !important; - background: #f1f5f9 !important; - border: 1px solid #e2e8f0 !important; } - -.step-nav-back:hover { - background: #e2e8f0 !important; - color: #1e293b !important; +.header-compact-input .MuiAutocomplete-popupIndicator { margin-right: 0; } +.header-compact-input .MuiOutlinedInput-root { + padding-top: 0 !important; + padding-bottom: 0 !important; + padding-right: 60px !important; +} +.header-compact-input .MuiAutocomplete-input { + padding: 4px 4px 4px 0 !important; + height: auto !important; } - -@media (max-width: 599px) { - .route-step-sub { - display: none !important; - } - .route-step { - padding: 8px 10px; - gap: 8px; - } - .route-step-index { - width: 28px; - height: 28px; - font-size: 13px; - } - .step-nav { - flex-direction: column-reverse; - align-items: stretch; - } - .step-nav-btn { - width: 100%; - } -} \ No newline at end of file diff --git a/src/pages/nearle/orders/OrdersTableSkeleton.js b/src/pages/nearle/orders/OrdersTableSkeleton.js index 80bb899..a146695 100644 --- a/src/pages/nearle/orders/OrdersTableSkeleton.js +++ b/src/pages/nearle/orders/OrdersTableSkeleton.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ import { TableRow, TableCell, Skeleton, Stack } from '@mui/material'; export const OrdersTableSkeleton = ({ rowsPerPage = 5, col = 1 }) => { diff --git a/src/pages/nearle/orders/RidersPinPointOSM.js b/src/pages/nearle/orders/RidersPinPointOSM.js new file mode 100644 index 0000000..3eec0b2 --- /dev/null +++ b/src/pages/nearle/orders/RidersPinPointOSM.js @@ -0,0 +1,79 @@ +/* eslint-disable no-unused-vars */ +import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; +import L from 'leaflet'; +import 'leaflet/dist/leaflet.css'; + +// distance function (same) +function distance(lat1, lng1, lat2, lng2) { + const R = 6371; + const dLat = (lat2 - lat1) * (Math.PI / 180); + const dLng = (lng2 - lng1) * (Math.PI / 180); + + const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) * Math.sin(dLng / 2) ** 2; + + return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); +} + +const center = [11.015181, 76.953682]; + +const riders = [ + { id: 1, lat: 11.04362, lng: 76.924667 }, + { id: 2, lat: 11.00988, lng: 76.949966 }, + { id: 3, lat: 11.020983, lng: 76.966331 } +]; + +export default function RidersPinPointOSM() { + const sortedRiders = riders + .map((r) => ({ + ...r, + distance: distance(center[0], center[1], r.lat, r.lng) + })) + .sort((a, b) => a.distance - b.distance); + + // purple center marker + const centerIcon = L.icon({ + iconUrl: 'https://maps.google.com/mapfiles/ms/icons/purple-dot.png', + iconSize: [32, 32] + }); + + // basic numbered marker icon + const createMarkerIcon = (number) => + L.divIcon({ + className: 'custom-marker', + html: ` +
    + ${number} +
    + `, + iconSize: [30, 30], + iconAnchor: [15, 15] + }); + + return ( + + {/* OSM tiles */} + + + {/* Purple center marker */} + + + {/* Sorted rider markers */} + {sortedRiders.map((r, index) => ( + + Rider {index + 1} + + ))} + + ); +} diff --git a/src/pages/nearle/orders/createorder1.js b/src/pages/nearle/orders/createorder1.js index 0e0e7eb..1407dc5 100644 --- a/src/pages/nearle/orders/createorder1.js +++ b/src/pages/nearle/orders/createorder1.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ import * as React from 'react'; import { useEffect, useState, useRef, Fragment } from 'react'; import { @@ -6,33 +7,42 @@ import { Grid, Typography, Stack, + Box, Button, TextField, Autocomplete, + CardActions, Chip, + Avatar, Divider, DialogTitle, DialogContent, Checkbox, DialogActions, CircularProgress, + ButtonGroup, + FormLabel, IconButton, + Drawer, + InputLabel, + Select, + MenuItem, Switch, + CardHeader, + Card, OutlinedInput, FormGroup, - FormControlLabel, - Box, - Card, - useMediaQuery + FormControlLabel } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import { calculateDrivingDistance, calculateTotalCharge } from '../../../utils/distance'; import { Empty } from 'antd'; -import { FaPhoneAlt, FaBox, FaBoxes, FaTruck, FaArrowRight, FaArrowLeft, FaCheck, FaRoute, FaMoneyBillWave, FaChartLine, FaReceipt, FaPaperPlane } from 'react-icons/fa'; +import { FaPhoneAlt } from 'react-icons/fa'; import { GiDoorHandle } from 'react-icons/gi'; import { FaLandmarkDome } from 'react-icons/fa6'; import ClearIcon from '@mui/icons-material/Clear'; import { useNavigate } from 'react-router'; +import { MdLocationCity } from 'react-icons/md'; import { TbMapPinCode } from 'react-icons/tb'; import { FaLocationDot } from 'react-icons/fa6'; import axios from 'axios'; @@ -50,124 +60,122 @@ import dayjs from 'dayjs'; import { enqueueSnackbar } from 'notistack'; var utc = require('dayjs/plugin/utc'); dayjs.extend(utc); -import { SearchOutlined, CloseOutlined, CalendarOutlined, ClockCircleOutlined, FileTextOutlined, MessageOutlined } from '@ant-design/icons'; +import LocationOnIcon from '@mui/icons-material/LocationOn'; +import parse from 'autosuggest-highlight/parse'; +import { debounce } from '@mui/material/utils'; +import CardContent from 'themes/overrides/CardContent'; +import { PhoneAndroid } from '@mui/icons-material'; +import { SearchOutlined, CloseOutlined } from '@ant-design/icons'; import MyLocationIcon from '@mui/icons-material/MyLocation'; import HighlightOffIcon from '@mui/icons-material/HighlightOff'; -import { OpenToast } from 'components/third-party/OpenToast'; -import { MapContainer, TileLayer, Marker, Polyline, useMap } from 'react-leaflet'; -import L from 'leaflet'; -import 'leaflet/dist/leaflet.css'; -import './OrdersRedesign.css'; -import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; -import AnimateButton from 'components/@extended/AnimateButton'; +import { Paper } from '@mui/material'; +import { + MdAddShoppingCart, + MdMyLocation, + MdLocationOn, + MdSchedule, + MdLocalShipping, + MdAttachMoney, + MdNotes, + MdCheckCircle, + MdReceiptLong, + MdInventory2, + MdStraighten, + MdPersonPin, + MdPerson, + MdHistoryToggleOff +} from 'react-icons/md'; -const pickupIcon = typeof window !== 'undefined' ? new L.Icon({ - iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-blue.png', - shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41] -}) : null; - -const dropoffIcon = typeof window !== 'undefined' ? new L.Icon({ - iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png', - shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41] -}) : null; - -const MapBoundsController = ({ startPoint, endPoint }) => { - const map = useMap(); - useEffect(() => { - const points = []; - const pLat = parseFloat(startPoint?.latitude); - const pLng = parseFloat(startPoint?.longitude); - const dLat = parseFloat(endPoint?.latitude); - const dLng = parseFloat(endPoint?.longitude); - - if (!isNaN(pLat) && !isNaN(pLng) && pLat !== 0 && pLng !== 0) { - points.push([pLat, pLng]); - } - if (!isNaN(dLat) && !isNaN(dLng) && dLat !== 0 && dLng !== 0) { - points.push([dLat, dLng]); - } - - if (points.length === 1) { - map.setView(points[0], 14); - } else if (points.length === 2) { - map.fitBounds(points, { padding: [50, 50] }); - } - }, [startPoint?.latitude, startPoint?.longitude, endPoint?.latitude, endPoint?.longitude, map]); - return null; +// ============================================================================ +// Design tokens — shared with the rest of the redesigned operator pages. +// ============================================================================ +const DT = { + radiusPill: 999, + radiusCard: 16, + shadowSoft: '0 14px 40px rgba(15, 23, 42, 0.10)', + shadowMd: '0 8px 24px rgba(15, 23, 42, 0.08)', + shadowPop: '0 18px 50px rgba(15, 23, 42, 0.18)', + textPrimary: '#0f172a', + textSecondary: '#64748b', + textMuted: '#94a3b8', + borderSubtle: '#e2e8f0', + divider: '#f1f5f9', + surface: '#ffffff', + surfaceAlt: '#f8fafc' }; +const dtA = (c, suffix) => `${c}${suffix}`; +const tint = (c) => dtA(c, '08'); +const soft = (c) => dtA(c, '18'); +const ring = (c) => dtA(c, '26'); +const edge = (c) => dtA(c, '55'); -const OrderMap = ({ startPoint, endPoint, appLocaLat, appLocaLng }) => { - const defaultCenter = [ - parseFloat(appLocaLat) || 11.0168, - parseFloat(appLocaLng) || 76.9558 - ]; +const BRAND = '#C01227'; +const BRAND_LIGHT = '#D25463'; - const hasPick = startPoint?.latitude && startPoint?.longitude && parseFloat(startPoint.latitude) !== 0; - const hasDrop = endPoint?.latitude && endPoint?.longitude && parseFloat(endPoint.latitude) !== 0; - - const pickCoords = hasPick ? [parseFloat(startPoint.latitude), parseFloat(startPoint.longitude)] : null; - const dropCoords = hasDrop ? [parseFloat(endPoint.latitude), parseFloat(endPoint.longitude)] : null; - - const [routePoints, setRoutePoints] = React.useState([]); - - React.useEffect(() => { - if (!hasPick || !hasDrop) { - setRoutePoints([]); - return; - } - - const getOSRMRoute = async () => { - const url = `https://router.project-osrm.org/route/v1/driving/${startPoint.longitude},${startPoint.latitude};${endPoint.longitude},${endPoint.latitude}?overview=full&geometries=geojson`; - try { - const res = await fetch(url); - const data = await res.json(); - if (data.routes?.length) { - const points = data.routes[0].geometry.coordinates.map(([lng, lat]) => [lat, lng]); - setRoutePoints(points); - } else { - setRoutePoints([pickCoords, dropCoords]); - } - } catch (err) { - console.error('OSRM Error:', err); - setRoutePoints([pickCoords, dropCoords]); - } - }; - - getOSRMRoute(); - }, [startPoint.latitude, startPoint.longitude, endPoint.latitude, endPoint.longitude, hasPick, hasDrop]); - - return ( -
    - - - {hasPick && } - {hasDrop && } - {routePoints.length > 0 ? ( - - ) : ( - hasPick && hasDrop && +// Soft card section header — coloured banner above each form section. +const SectionHeader = ({ color, icon, title, subtitle, action }) => ( + + + + {icon} + + + {title} + {subtitle && ( + + {subtitle} + )} - - -
    - ); -}; + + + {action} + +); + +// Section wrapper Paper used as the outer card for each form block. +const SectionCard = ({ children, sx = {} }) => ( + + {children} + +); + +// Popup paper for Autocomplete dropdowns — matches the rest of the design. +const SoftPaper = (props) => ( + +); function loadScript(src, position, id) { if (!position) { @@ -184,96 +192,190 @@ function loadScript(src, position, id) { const Createorder1 = () => { Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY); // ================================================= || GoogleMaps (Drawer) || ================================================= - const loaded = React.useRef(false); - const navigate = useNavigate(); - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('md')); - const locationRef = useRef(null); - const tenantRef = useRef(null); + const [value, setValue] = React.useState(null); + const [value1, setValue1] = React.useState(null); + const [inputValue, setInputValue] = React.useState(''); const [inputValue1, setInputValue1] = React.useState(''); const [inputValue2, setInputValue2] = React.useState(''); + const [inputValue3, setInputValue3] = React.useState(''); + const [options, setOptions] = React.useState([]); + const [options1, setOptions1] = React.useState([]); + const loaded = React.useRef(false); + const loaded1 = React.useRef(false); + const [mobilenumber, setMobilenumber] = useState(''); + const [emailaddress, setEmailaddress] = useState(''); + const [city, setCity] = useState(''); + const [city1, setCity1] = useState(''); + const [zipcode, setZipcode] = useState(''); + const [zipcode1, setZipcode1] = useState(''); + const [state, setState] = useState(''); + const [state1, setState1] = useState(''); + const [suburb, setSuburb] = useState(''); + const [suburb1, setSuburb1] = useState(''); + const [pickContactName, setPickContactName] = useState(''); + const [dropContactName, setDropContactName] = useState(''); + const [pickdoorno, setPickDoorno] = useState(''); + const [dropDoorno, setDropDoorno] = useState(''); + const [pickLandmark, setPickLandmark] = useState(''); + const [dropLandmark, setDropLandmark] = useState(''); + const [address, setAddress] = useState(''); + const [address1, setAddress1] = useState(''); + const [latlong, setLatlong] = useState({}); + const [latlong1, setLatlong1] = useState({}); + const autocompleteService = useRef(null); const [tenanatLocoId, setTenanatLocoId] = useState(localStorage.getItem('locationid')); const [isLocation, setIsLocation] = useState(false); const textFieldRef1 = useRef(null); - const textFieldRef1a = useRef(null); const textFieldRef2 = useRef(null); - const [appId, setAppId] = useState(0); - const [open, setOpen] = useState(false); - const [clientdetail, setClientdetail] = useState([]); - const [startdate, setStartdate] = useState(dayjs().format('MM-DD-YYYY')); - // const [starttime, setStatrttime] = useState(`${dayjs().format('MM-DD-YYYY')} 08:00:00`); - const [starttime, setStatrttime] = useState(); - // const [endtime, setEndtime] = useState(`${dayjs().format('MM-DD-YYYY')} 20:00:00`); - const [endtime, setEndtime] = useState(); - const [timeslotarr, setTimeslotarr] = useState([]); - const [otherinstructions, setOtherinstructions] = useState(''); - const [loading2, setLoading2] = useState(false); - const [loading, setLoading] = useState(false); - const [btnLoading, setBtnLoading] = useState(false); - const [alertmessage, setAlertmessage] = useState(''); - const [admintoken, setAdmintoken] = useState(); - const [tenant, setTenant] = useState({}); - const [selectedtime, setSelectedtime] = useState(''); - const [tenantlist, setTenantlist] = useState([]); - const [startPoint, setStartPoint] = useState({ latitude: 0, longitude: 0 }); - const [endPoint, setEndPoint] = useState({ latitude: 0, longitude: 0 }); - const [showDistance, setShowDistance] = useState(false); - const [distance, setDistance] = useState(0); - const [basePrice, setBasePrice] = useState(0); - const [pricePerKm, setPricePerKm] = useState(0); - const [minKm, setMinKm] = useState(0); - const [totalCharge, setTotalCharge] = useState(0); - const [subCat, setSubCat] = useState([]); - const [subCatName, setSubCatName] = useState('Select '); - const [subCatId, setSubCatId] = useState(0); - const [weight, setWeight] = useState(''); - const [tenantid, setTenantid] = useState(0); - const [locationid, setLocationid] = useState(0); - const [selectedCatChip, setSelectedCatChip] = useState(null); - const [isCustomerOpen, setIsCustomerOpen] = useState(false); - const [searchCustList, setSearchCustList] = useState(''); - const [customerlist, setCustomerlist] = useState([]); - const [defaultPickup, setDefaultPickup] = useState(null); - const [pickCust, setPickCust] = useState(null); - const [dropCust, setDropCust] = useState(null); - const [pickordrop, setpickordrop] = useState(0); // 1 ->pick 2 -> drop - const [addId1, setAddId1] = useState(0); - const [addId2, setAddId2] = useState(0); - const [tenantLocations, setTenantlocations] = useState([]); - const [appLocaLat, setAppLocaLat] = useState(); - const [appLocaLng, setAppLocaLng] = useState(); - const [appLocaRadius, setAppLocaRadius] = useState(); - const [locations, setLocations] = useState('Select Location'); - const userid = localStorage.getItem('userid'); - const [isNumChange1, setIsNumChange1] = useState(0); - const [isNumChange2, setIsNumChange2] = useState(0); - const [showCheck1, setShowCheck1] = useState(0); - const [showCheck2, setShowCheck2] = useState(0); - const [pickNum, setPickNum] = useState(); - const [dropNum, setdropNum] = useState(); - const [numErr1, setNumErr1] = useState(false); - const [numErr2, setNumErr2] = useState(false); - const [isSms, setIsSms] = useState(0); const [collectionamt, setCollectionamt] = useState(0); const [quantity, setQuantity] = useState(1); - const [tenantValue, setTenantValue] = useState(null); - const [locationValue, setLocationValue] = useState(null); - const [pickupSlotsList, setPickupSlotsList] = useState(null); - const [pickupSlot, setPickupSlot] = useState(null); - const [routeStep, setRouteStep] = useState(1); // 1 = Pickup, 2 = Drop - const pickupStepComplete = !!( - pickCust?.firstname && - pickCust?.contactno && - String(pickCust.contactno).length === 10 && - pickCust?.doorno && - pickCust?.suburb && - pickCust?.postcode - ); - useEffect(() => { - console.log('pickupSlotsList', pickupSlotsList); - console.log('pickupSlot', pickupSlot); - }, [pickupSlotsList, pickupSlot]); + const handleOkClick1 = () => { + // Set focus back to the text field after clicking the "OK" chip + if (textFieldRef1.current) { + textFieldRef1.current.focus(); + } + }; + + const handleOkClick2 = () => { + // Set focus back to the text field after clicking the "OK" chip + if (textFieldRef2.current) { + textFieldRef2.current.focus(); + } + }; + + const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 } + ]; + + // // // ====================================================== || address (pick)|| ====================================================== + // useEffect(() => { + // if (address) { + // try { + // Geocode.fromAddress(address).then( + // (response) => { + // if (response.status == 'OK') { + // const { lat, lng } = response.results[0].geometry.location; + // console.log({ lat, lng }); + // setLatlong({ + // lat, + // lng + // }); + // console.log(response); + // if (response.results[0].address_components) { + // let place = response.results[0]; + // let cityA, zipcodeA, stateA, suburbA; + // for (let i = 0; i < place.address_components.length; i++) { + // for (let j = 0; j < place.address_components[i].types.length; j++) { + // switch (place.address_components[i].types[j]) { + // case 'locality': + // cityA = place.address_components[i].long_name; + // break; + // case 'administrative_area_level_1': + // stateA = place.address_components[i].long_name; + // break; + // case 'postal_code': + // zipcodeA = place.address_components[i].long_name; + // break; + // case 'sublocality': + // suburbA = place.address_components[i].long_name; + // break; + // } + // } + // } + // setCity(cityA || ''); + // setState(stateA || ''); + // setZipcode(zipcodeA || ''); + // setSuburb(suburbA || ''); + // console.log({ lat, lng, cityA, stateA, zipcodeA, suburbA }); + // setPickCust({ + // ...pickCust + // // city: cityA, + // // state: stateA, + // // postcode: zipcodeA, + // // suburb: suburbA + // // latitude: lat, + // // longitude: lng + // }); + // // setStartPoint({ latitude: lat, longitude: lng }); + // } + // } + // }, + // (error) => { + // console.log(error); + // } + // ); + // } catch (err) { + // console.log(err); + // } + // } + // }, [address]); + // // // ====================================================== || address 1 (drop)|| ====================================================== + // useEffect(() => { + // if (address) { + // try { + // Geocode.fromAddress(address1).then( + // (response) => { + // if (response.status == 'OK') { + // const { lat, lng } = response.results[0].geometry.location; + + // setLatlong1({ + // lat, + // lng + // }); + // console.log(response); + // if (response.results[0].address_components) { + // let place = response.results[0]; + // let cityB, zipcodeB, stateB, suburbB; + // for (let i = 0; i < place.address_components.length; i++) { + // for (let j = 0; j < place.address_components[i].types.length; j++) { + // switch (place.address_components[i].types[j]) { + // case 'locality': + // cityB = place.address_components[i].long_name; + // break; + // case 'administrative_area_level_1': + // stateB = place.address_components[i].long_name; + // break; + // case 'postal_code': + // zipcodeB = place.address_components[i].long_name; + // break; + // case 'sublocality': + // suburbB = place.address_components[i].long_name; + // break; + // } + // } + // } + // setCity(cityB || ''); + // setState(stateB || ''); + // setZipcode(zipcodeB || ''); + // setSuburb(suburbB || ''); + // console.log({ lat, lng, cityB, stateB, zipcodeB, suburbB }); + // setDropCust({ + // ...dropCust + // // city: cityB, + // // state: stateB, + // // postcode: zipcodeB, + // // suburb: suburbB + // // latitude: lat, + // // longitude: lng + // }); + // // setEndPoint({ latitude: lat, longitude: lng }); + // } + // } + // }, + // (error) => { + // console.log(error); + // } + // ); + // } catch (err) { + // console.log(err); + // } + // } + // }, [address1]); if (typeof window !== 'undefined' && !loaded.current) { if (!document.querySelector('#google-maps')) { @@ -286,49 +388,178 @@ const Createorder1 = () => { loaded.current = true; } - // ==============================|| fetchAppLocations ||============================== // - const fetchAppLocations = async () => { - try { - const locationRes = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`); - console.log('fetchAppLocations', locationRes.data.details); - setLocations(locationRes.data.details); - } catch (err) { - console.log('locationRes', err); - } - }; + // const fetch = React.useMemo( + // () => + // debounce((request, callback) => { + // autocompleteService.current.getPlacePredictions(request, callback); + // }, 400), + // [] + // ); + // const fetch1 = React.useMemo( + // () => + // debounce((request, callback) => { + // autocompleteService.current.getPlacePredictions(request, callback); + // }, 400), + // [] + // ); + + // ====================================================== || options (pick)|| ====================================================== + + // React.useEffect(() => { + // let active = true; + // if (!autocompleteService.current && window.google) { + // autocompleteService.current = new window.google.maps.places.AutocompleteService(); + // } + // if (!autocompleteService.current) { + // return undefined; + // } + // if (inputValue === '') { + // setOptions(value ? [value] : []); + // return undefined; + // } + // fetch({ input: inputValue }, (results) => { + // if (active) { + // let newOptions = []; + + // if (value) { + // newOptions = [value]; + // } + + // if (results) { + // newOptions = [...newOptions, ...results]; + // } + + // setOptions(newOptions); + // } + // }); + + // return () => { + // active = false; + // }; + // }, [value, inputValue, fetch]); + + // // ====================================================== || options1 (drop)|| ====================================================== + // React.useEffect(() => { + // let active = true; + // if (!autocompleteService.current && window.google) { + // autocompleteService.current = new window.google.maps.places.AutocompleteService(); + // } + // if (!autocompleteService.current) { + // return undefined; + // } + // if (inputValue1 === '') { + // setOptions1(value1 ? [value1] : []); + // return undefined; + // } + // fetch1({ input: inputValue1 }, (results) => { + // if (active) { + // let newOptions = []; + + // if (value1) { + // newOptions = [value1]; + // } + + // if (results) { + // newOptions = [...newOptions, ...results]; + // } + + // setOptions1(newOptions); + // } + // }); + + // return () => { + // active = false; + // }; + // }, [value1, inputValue1, fetch1]); + + const appId = localStorage.getItem('applocationid'); + const navigate = useNavigate(); + const [open, setOpen] = useState({}); + const [open1, setOpen1] = useState(''); + const [open2, setOpen2] = useState(false); + const [open3, setOpen3] = useState(false); + const [open4, setOpen4] = useState(false); + const [shift, setShift] = useState(1); + const [clientlist, setClientlist] = useState([]); + const [clientdetail, setClientdetail] = useState([]); + const [eventname, setEventname] = useState(''); + const [startdate, setStartdate] = useState(dayjs().format('MM-DD-YYYY')); + const [enddate, setEnddate] = useState(dayjs().add(1, 'day').format('MM-DD-YYYY')); + // const [starttime, setStatrttime] = useState(`${dayjs().format('MM-DD-YYYY')} 08:00:00`); + const [starttime, setStatrttime] = useState(); + // const [endtime, setEndtime] = useState(`${dayjs().format('MM-DD-YYYY')} 20:00:00`); + const [endtime, setEndtime] = useState(); + const [timeslotarr, setTimeslotarr] = useState([]); + const [currentsno, setCurrentsno] = useState(''); + const [roleoptions, setRoleoptions] = useState([]); + const theme = useTheme(); + const [otherinstructions, setOtherinstructions] = useState(''); + const [attireslist, setAttireslist] = useState([]); + const [serviceaddonslist, setServiceaddonslist] = useState([]); + const [orderaddonobj, setOrderaddonobj] = useState([]); + const [stafflist, setStafflist] = useState([]); + const [loading2, setLoading2] = useState(false); + const [loading, setLoading] = useState(false); + const [btnLoading, setBtnLoading] = useState(false); + const [shiftarr, setShiftarr] = useState([]); + const [shiftarr1, setShiftarr1] = useState([]); + const [orderarr, setOrderarr] = useState([]); + const [alertmessage, setAlertmessage] = useState(''); + const [tabstatus, setTabstatus] = useState(''); + const [tenantinfo, setTenantinfo] = useState({}); + const [searchword, setSearchword] = useState(''); + const [clientdetailarr, setClientdetailarr] = useState([]); + const [clientdetailbusinessarr, setClientdetailbusinessarr] = useState([]); + const [admintoken, setAdmintoken] = useState(); + const [tenantlocationlist, setTenantlocationlist] = useState([]); + const [tenant, setTenant] = useState({}); + const [clientinfo, setClientinfo] = useState({}); + const [selectedtime, setSelectedtime] = useState(''); + const [tenantlist, setTenantlist] = useState([]); + const [tenantid, setTenantid] = useState(); + const [tenantlocation, setTenantlocation] = useState(''); + const [pickupswitch, setPickupswitch] = useState(true); + const [deliverytype, setDeliverytype] = useState('B'); + const [dropswitch, setDropswitch] = useState(false); + const [startPoint, setStartPoint] = useState({ latitude: 0, longitude: 0 }); + const [endPoint, setEndPoint] = useState({ latitude: 0, longitude: 0 }); + const [showDistance, setShowDistance] = useState(false); + const [distance, setDistance] = useState(0); + const [basePrice, setBasePrice] = useState(0); + const [pricePerKm, setPricePerKm] = useState(0); + const [minKm, setMinKm] = useState(0); + const [totalCharge, setTotalCharge] = useState(0); + const [subCat, setSubCat] = useState([]); + const [subCatName, setSubCatName] = useState('Select '); + const [subCatId, setSubCatId] = useState(); + const [weight, setWeight] = useState(''); + const tid = localStorage.getItem('tenantid'); + const [selectedCatChip, setSelectedCatChip] = useState(null); + const [isCustomerOpen, setIsCustomerOpen] = useState(false); + const [searchCustList, setSearchCustList] = useState(''); + const [customerlist, setCustomerlist] = useState([]); + const [pickCust, setPickCust] = useState({}); + const [dropCust, setDropCust] = useState({}); + const [pickordrop, setpickordrop] = useState(0); // 1 ->pick 2 -> drop + const [addId1, setAddId1] = useState(0); + const [addId2, setAddId2] = useState(0); + const [tenantLocations, setTenantlocations] = useState([]); + const [appLocaLat, setAppLocaLat] = useState(); + const [appLocaLng, setAppLocaLng] = useState(); + const [appLocaRadius, setAppLocaRadius] = useState(); + const [isNumChange1, setIsNumChange1] = useState(0); + const [isNumChange2, setIsNumChange2] = useState(0); + const [showCheck1, setShowCheck1] = useState(0); + const [showCheck2, setShowCheck2] = useState(0); + const [pickNum, setPickNum] = useState(); + const [dropNum, setdropNum] = useState(); + const [numErr1, setNumErr1] = useState(false); + const [numErr2, setNumErr2] = useState(false); + const [isSms, setIsSms] = useState(0); + useEffect(() => { - fetchAppLocations(); - }, []); - - // ===================================================== || fetchtenantinfolist || ===================================================== - - const fetchtenantinfolist = async () => { - setLoading(true); - await axios - .get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${appId}&status=active`) - - .then((res) => { - console.log(res); - if (res.data.status) { - let arr = []; - res.data.details.map((val) => { - arr.push({ - ...val, - label: `${val.tenantname}` - }); - }); - setTenantlist(arr); - } - setLoading(false); - }) - .catch((err) => { - console.log(err); - setLoading(false); - }); - }; - useEffect(() => { - appId && fetchtenantinfolist(); - }, [appId]); + console.log(isSms); + }, [isSms]); const handleChipClick = (chipLabel) => { setSelectedCatChip(chipLabel); @@ -344,9 +575,9 @@ const Createorder1 = () => { } }); - const fetchTenantPricing = async (id) => { + const fetchTenantPricing = async () => { try { - const pricingResponse = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantpricing/?tenantid=${id}`); + const pricingResponse = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantpricing/?tenantid=${tid}`); console.log('pricingResponse', pricingResponse.data.details); setBasePrice(pricingResponse.data.details.baseprice); setPricePerKm(pricingResponse.data.details.priceperkm); @@ -355,6 +586,9 @@ const Createorder1 = () => { console.log('fetchTenantPricing error', error); } }; + useEffect(() => { + fetchTenantPricing(); + }, []); useEffect(() => { console.log('startPoint', startPoint); @@ -365,7 +599,26 @@ const Createorder1 = () => { } }, [startPoint, endPoint]); - // google distance matrix logic + const getDistance = () => { + const dist = geolib.getPreciseDistance(startPoint, endPoint, 1); + console.log(`Distance: ${dist} meters`); + const distanceInKm = (dist / 1000).toFixed(2); + const roundedDistance = Math.round(distanceInKm); + console.log('roundedDistance', roundedDistance); + setDistance(roundedDistance.toFixed(2)); + if (roundedDistance < minKm) { + setTotalCharge(basePrice); + } else { + const total = (roundedDistance - minKm) * pricePerKm + basePrice; + setTotalCharge(total); + } + setShowDistance(true); + if (roundedDistance > appLocaRadius) { + setShowDistance(true); + setOpen4(true); + } + }; + const calculateDistance = async (pickup, drop) => { try { const roundedDistance = await calculateDrivingDistance(pickup, drop); @@ -379,7 +632,7 @@ const Createorder1 = () => { setShowDistance(true); if (roundedDistance > appLocaRadius) { setShowDistance(true); - setOpen(true); + setOpen4(true); } // ⏱️ Approximate duration @@ -391,12 +644,6 @@ const Createorder1 = () => { } }; - useEffect(() => { - if (tenantid) { - clientdetails(); - } - }, [searchCustList?.length > 3, searchCustList == '', tenantid]); - useEffect(() => { if (timeslotarr[0]) { let arr = []; @@ -405,23 +652,90 @@ const Createorder1 = () => { arr.push(val); } }); + + if (arr[0]) { + setOrderarr([ + { + sno: 1, + address: '', + customerid: '', + deliverytime: dayjs(arr[0]) || '', + deliverylocationid: '', + clientname: '', + contactno: '', + latitude: '', + longitude: '' + } + ]); + } } }, [timeslotarr]); + useEffect(() => { + if (searchword) { + let arr = clientdetail.filter((val) => { + return ( + val.address.toLowerCase().includes(searchword.toLowerCase()) || + val.firstname.toLowerCase().includes(searchword.toLowerCase()) || + val.contactno.toLowerCase().includes(searchword.toLowerCase()) + ); + }); + console.log(arr); + setClientdetailarr([...arr]); + } else { + setClientdetailarr([...clientdetail]); + } + }, [searchword]); + + // const { ref: materialRef } = usePlacesWidget({ + // apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY, + // onPlaceSelected: (place) => { + // console.log(place); + + // // setAddress(place.formatted_address) + // let city1, zipcode1, state1, suburb1; + // for (let i = 0; i < place.address_components.length; i++) { + // for (let j = 0; j < place.address_components[i].types.length; j++) { + // switch (place.address_components[i].types[j]) { + // case 'locality': + // city1 = place.address_components[i].long_name; + // break; + // case 'administrative_area_level_1': + // state1 = place.address_components[i].long_name; + // break; + // case 'postal_code': + // zipcode1 = place.address_components[i].long_name; + // break; + // case 'sublocality': + // suburb1 = place.address_components[i].long_name; + // break; + // } + // } + // } + // // setCity(city1 || '') + // // setState(state1 || ''); + // // setZipcode(zipcode1 || ''); + // // setSuburb(suburb1 || '') + // }, + + // options: { + // types: ['address' || 'geocode'] + // } + // }); + // ==================================================== || fetchtenantinfo || ==================================================== const fetchtenantinfo = async () => { setLoading(true); - console.log('tenantid', tenantid); + console.log('tid', tid); await axios - .get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tenantid}`) + .get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`) .then((res) => { console.log('fetchtenantinfo', res); if (res.data.status) { setTenant(res.data.details); - + setTenantid(res.data.details.tenantid); fetchAppAdminTokens(); - setSubCatName(res.data.details.subcategoryname); setSubCatId(res.data.details.subcategoryid); } setLoading(false); @@ -432,22 +746,22 @@ const Createorder1 = () => { }); }; useEffect(() => { - if (tenantid) { - fetchtenantinfo(); - } - }, [tenantid]); - // ==================================================== || getsubcategories || ==================================================== + fetchtenantinfo(); + }, []); const getsubcategories = async () => { await axios .get(`${process.env.REACT_APP_URL}/utils/getsubcategories/?moduleid=6`) .then((res) => { console.log('subcateRes', res.data.details); - if (res.data.status) { + if (res.data.status && res.data.details) { setSubCat(res.data.details); + } else { + setSubCat([]); } }) .catch((err) => { console.log(err); + setSubCat([]); }); }; useEffect(() => { @@ -466,7 +780,6 @@ const Createorder1 = () => { setAppLocaLat(latitude); setAppLocaLng(longitude); setAppLocaRadius(radius); - console.log('radius', radius); setStatrttime(`${dayjs().format('MM-DD-YYYY')} ${opentime}`); setEndtime(`${dayjs().format('MM-DD-YYYY')} ${closetime}`); console.log('starttime', `${dayjs().format('MM-DD-YYYY')} ${opentime}`); @@ -490,10 +803,8 @@ const Createorder1 = () => { }); }; useEffect(() => { - if (appId) { - fetchTiming(); - } - }, [appId]); + fetchTiming(); + }, []); // =============================================== || fetchAppAdminTokens (via appId) || =============================================== const fetchAppAdminTokens = async () => { @@ -501,7 +812,9 @@ const Createorder1 = () => { await axios .get(`${process.env.REACT_APP_URL}/utils/getapplocationconfig/?applocationid=${appId}`) .then((res) => { - const userfcmtokemArray = res.data.details.applocationadmins.map((admin) => admin.userfcmtokem); // fcm => firebase cloud messaging + const userfcmtokemArray = res.data.details.applocationadmins.map((admin) => admin.userfcmtokem); + console.log('fetchAppAdminTokens', res); + console.log('userfcmtokemArray', userfcmtokemArray); if (res.data.status) { setAdmintoken(userfcmtokemArray); } @@ -525,11 +838,21 @@ const Createorder1 = () => { enqueueSnackbar(message, { variant: variant, anchorOrigin: { vertical: 'top', horizontal: 'right' }, - autoHideDuration: time ? time : 1500 + autoHideDuration: time ? time : 2000 }); console.log(alertmessage); }; + function closeAddressModal() { + setOpen2(false); + } + function closetimemodal() { + setOpen3(false); + setCurrentsno(''); + } + + // =============================================== || createsubmitobj1 (create orders) || =============================================== + const createsubmitobj2 = async () => { let arr = {}; arr = { @@ -537,7 +860,7 @@ const Createorder1 = () => { applocationid: tenant.applolcationid, cancellled: '', categoryid: +tenant.categoryid, - configid: 9, + configid: 7, customerid: isNumChange1 == 0 ? +pickCust.customerid || 0 : 0, deliveryaddress: dropCust.address || '', deliverycharge: +totalCharge.toFixed(2) || 0, @@ -551,44 +874,50 @@ const Createorder1 = () => { deliverylocationid: dropCust.deliverylocationid || 0, deliverylong: dropCust.longitude.toString(), deliverytime: `${dayjs(startdate).format('YYYY-MM-DD')} ${dayjs(selectedtime.$d).format('HH:mm:ss')}`, - deliverytype: 'B', + deliverytype: pickCust.customerid !== 0 || dropCust.customerid !== 0 ? 'B' : 'C', delivered: '', itemcount: 1, kms: distance.toString() || 0, - locationid: +locationid, + locationid: +tenanatLocoId, //main or branch moduleid: +tenant.moduleid, orderamount: +totalCharge.toFixed(2) || 0, ordercharges: 0.0, orderdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), + orderheaderid: 0, + orderid: '', // ordernotes: otherinstructions, orderstatus: 'created', ordervalue: +totalCharge.toFixed(2) || 0, partnerid: tenant.partnerid, - partneruserid: +userid, paymentstatus: 1, paymenttype: 42, + pending: '', pickupaddress: pickCust.address || '', pickupcity: pickCust.city || '', pickupcontactno: pickCust.contactno || '', pickupcustomer: pickCust.firstname || '', pickuplandmark: pickCust.landmark || '', - pickuplat: pickCust.latitude.toString() || '', + pickuplat: pickCust.latitude.toString(), pickuplocation: pickCust.suburb || '', pickuplocationid: pickCust.deliverylocationid || 0, - pickuplong: pickCust.longitude.toString() || '', + pickuplong: pickCust.longitude.toString(), + processing: '', + ready: '', + remarks: '', smsdelivery: isSms, subcategoryid: +subCatId, + taxamount: 0.0, tenantid: tenant.tenantid, - collectionamt: +collectionamt, - quantity: +quantity, - weight, - pickupSlot + tenantuserid: parseInt(localStorage.getItem('userid')), + collectionamt: +collectionamt || 0, + quantity: +quantity || 1 }, + pickup: { address: pickCust.address || '', applocationid: tenant.applolcationid, city: pickCust.city || '', - configid: 1, + configid: 7, contactno: pickCust.contactno || '', customertoken: '', customerid: isNumChange1 == 0 ? pickCust.customerid || 0 : 0, @@ -601,25 +930,25 @@ const Createorder1 = () => { landmark: pickCust.landmark || '', latitude: pickCust.latitude.toString() || '', longitude: pickCust.longitude.toString() || '', + locationid: pickCust.deliverylocationid || 0, postcode: pickCust.postcode || '', primaryaddress: 1, - locationid: pickCust.deliverylocationid || 0, profileimage: '', state: pickCust.state || '', suburb: pickCust.suburb || '', tenantid: tenant.tenantid }, + drop: { address: dropCust.address || '', applocationid: tenant.applolcationid, city: dropCust.city || '', - configid: 1, + configid: 7, contactno: dropCust.contactno || '', customertoken: '', customerid: isNumChange2 == 0 ? dropCust.customerid || 0 : 0, devicetype: '', deviceid: '', - locationid: dropCust.deliverylocationid || 0, dialcode: '+91', doorno: dropCust.doorno || '', email: dropCust.email || '', @@ -627,6 +956,7 @@ const Createorder1 = () => { landmark: dropCust.landmark || '', latitude: dropCust.latitude.toString(), longitude: dropCust.longitude.toString(), + locationid: dropCust.deliverylocationid || 0, postcode: dropCust.postcode || '', primaryaddress: 1, profileimage: '', @@ -671,6 +1001,7 @@ const Createorder1 = () => { } else { try { const createRes = await axios.post(`${process.env.REACT_APP_URL2}/orders/createorder`, arr); + // const createRes = await axios.post(`${process.env.REACT_APP_URL}/orders/createorder`, arr); if (createRes.data.status) { console.log('createRes', createRes); enqueueSnackbar('Order Created Successfully', { @@ -679,36 +1010,106 @@ const Createorder1 = () => { autoHideDuration: 1000 }); if (admintoken) { - // notifyadmin(admintoken); - sendnotifications(); + notifyadmin(admintoken); + // sendnotifications(); } + navigate('/nearle/orders'); } else { - opentoast('Error in creating orders', 'warning'); + opentoast('Something went wrong, Cannot create order', 'warning'); } setLoading(false); + console.log(createRes); } catch (error) { - opentoast(error.message, 'warning'); console.log('createResErr', error); } } }; + useEffect(() => { + console.log('shiftarr'); + console.log(shiftarr); + }, [shiftarr]); + + const clicked1 = (e) => { + setShift(e); + }; + + const dialogopen = (i, result) => { + console.log(i, result); + setOpen({ shiftsno: result.sno, sno: i.sno }); + }; + const dialogclose = () => { + setOpen(''); + }; // ========================================================= || clientdetails || ========================================================= const clientdetails = async () => { setLoading2(true); try { let url = searchCustList == '' - ? `${process.env.REACT_APP_URL}/customers/gettenantcustomers/?tenantid=${tenantid}&pageno=1&pagesize=30` - : `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tenantid}&keyword=${searchCustList}`; + ? // ? `${process.env.REACT_APP_URL}/customers/getbytid/?tenantid=${tid}&pageno=1&pagesize=1` + `${process.env.REACT_APP_URL}/customers/gettenantcustomers/?tenantid=${tid}&pageno=1&pagesize=20` + : `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tid}&keyword=${searchCustList}`; + await axios .get(url) .then((res) => { + console.log('clientdetails', res.data.details); + if (res.data.status && res.data.details) { + setClientdetail(res.data.details || []); + setCustomerlist(res.data.details || []); + let arr = []; + res.data.details.map((val) => { + arr.push({ + label: `${val.firstname} | ${val.contactno}`, + ...val + }); + }); + setClientdetailarr(arr); + } else { + setClientdetail([]); + setCustomerlist([]); + setClientdetailarr([]); + } + setLoading2(false); + }) + .catch((err) => { + console.log(err); + setLoading2(false); + opentoast('server error', 'warning'); + }); + } catch (err) { + console.log(err); + setLoading2(false); + } + }; + useEffect(() => { + console.log('clientdetails, by search'); + clientdetails(); + // clientdetailsbusiness(); + }, [searchCustList.length > 3, searchCustList == '']); + // ========================================================= || clientdetailsbusiness || ========================================================= + const clientdetailsbusiness = async () => { + setLoading2(true); + try { + await axios + .get(`${process.env.REACT_APP_URL}/customers/getbytid/?tenantid=${tid}&locationid=1`) + .then((res) => { + console.log('clientdetailsbusiness', res.data.details); if (res.data.status) { - console.log('clientdetails', res.data.details); setClientdetail(res.data.details); - setCustomerlist(res.data.details); + // if (!searchword) { + // setClientdetailarr(res.data.details) + // } + let arr = []; + res.data.details.map((val) => { + arr.push({ + label: `${val.firstname} | ${val.contactno}`, + ...val + }); + }); + setClientdetailbusinessarr(arr); } setLoading2(false); }) @@ -763,7 +1164,7 @@ const Createorder1 = () => { // ============================================= || Google Maps Autocomplete(pick) || ============================================= useEffect(() => { // Initialize Google Maps Autocomplete - if (inputValue1) { + if (inputValue2) { const autocompleteInput = document.getElementById('addressAuto1'); const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, { // types: ['(cities)'], // You can adjust the types parameter based on your requirements @@ -775,15 +1176,17 @@ const Createorder1 = () => { radius: appLocaRadius * 1000 }).getBounds() }); - + let arr = []; // Event listener for autocomplete place changed autocomplete.addListener('place_changed', () => { const place = autocomplete.getPlace(); - setInputValue1(`${place.name}, ${place.formatted_address}`); + setInputValue2(`${place.name}, ${place.formatted_address}`); console.log('new place', place); // Do something with the selected place console.log(' pick (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place // to trigger getDistance setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() }); + setValue(place); + setAddress(`${place.name} ${place.formatted_address}`); setPickCust({ ...pickCust, address: `${place.name} ${place.formatted_address}` }); const address = { address: `${place.name} ${place.formatted_address}`, @@ -842,11 +1245,11 @@ const Createorder1 = () => { console.log('Pick Address:', address); }); } - }, [inputValue1]); + }, [inputValue2]); // ============================================= || Google Maps Autocomplete(Drop) || ============================================= useEffect(() => { - if (inputValue2) { + if (inputValue3) { // Initialize Google Maps Autocomplete const autocompleteInput = document.getElementById('addressAuto2'); const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, { @@ -863,11 +1266,12 @@ const Createorder1 = () => { // Event listener for autocomplete place changed autocomplete.addListener('place_changed', () => { const place = autocomplete.getPlace(); - setInputValue2(`${place.name}, ${place.formatted_address}`); + setInputValue3(`${place.name}, ${place.formatted_address}`); console.log('new place', place); // Do something with the selected place console.log('drop (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place setEndPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() }); - + setValue1(place); + setAddress1(`${place.name} ${place.formatted_address}`); setDropCust({ ...dropCust, address: `${place.name} ${place.formatted_address}` }); const address = { address: `${place.name} ${place.formatted_address}`, @@ -926,1596 +1330,1529 @@ const Createorder1 = () => { console.log('Drop Address:', address); }); } - }, [inputValue2]); + }, [inputValue3]); // ============================================= || gettenantlocations (branches) || ============================================= - const gettenantlocations = async (id) => { + const gettenantlocations = async () => { try { - const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${id}`); + const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${tid}`); console.log('gettenantlocations', res.data.details); - if (res.data.details.length == 1) { + if (res.data && res.data.details) { setTenantlocations(res.data.details); - setDefaultPickup(res.data.details[0]); - setLocationid(res.data.details[0].locationid); - setLocationValue(res.data.details[0].locationid); - setPickupSlotsList(res.data.details[0].slots); + if (res.data.details.length == 1) { + setIsLocation(true); + setTenanatLocoId(res.data.details[0].locationid); + } } else { - setTenantlocations(res.data.details); + setTenantlocations([]); } } catch (err) { console.log('gettenantlocations', err); + setTenantlocations([]); } }; + useEffect(() => { + gettenantlocations(); + }, []); return ( <> {loading && } - {loading2 && } - - {/* Header Section — single white card spanning the full row: - title on the left, service configurators inline on the right. */} - - + + - - - Create New Order - - - - - - {/* Choose App location */} - - `${option.locationname}`} - className="header-compact-input" - onChange={(event, value, reason) => { - if (reason === 'clear') { - setAppId(0); - setTenantid(0); - setTenantValue(null); - setTenantlist([]); - setLocationid(0); - setLocationValue(null); - setTenantlocations([]); - setPickCust(null); - setDropCust(null); - } else { - setAppId(value.applocationid); - setPickCust(null); - setDropCust(null); - setTenantid(0); - setTenantValue(null); - setLocationid(0); - setLocationValue(null); - setTenantlocations([]); - } - }} - renderInput={(params) => ( - - - {params.InputProps.startAdornment} - - ) - }} - /> - )} - /> - - - {/* Choose Client */} - - { - if (!appId) { - event.preventDefault(); - OpenToast('Please select a your location first!', 'warning', 3000); - setTimeout(() => { - locationRef.current?.focus(); - }, 0); - } - }} - onChange={(e, val, reason) => { - if (reason == 'clear') { - setTenantid(0); - setTenantValue(null); - setLocationid(0); - setLocationValue(null); - setTenantlocations([]); - setPickCust(null); - setDropCust(null); - } else { - setTenantid(val?.tenantid || 0); - setTenantValue(val); - setLocationid(0); - setLocationValue(null); - fetchTenantPricing(val.tenantid); - gettenantlocations(val.tenantid); - setDropCust(null); - } - }} - renderInput={(params) => ( - - - {params.InputProps.startAdornment} - - ) - }} - /> - )} - /> - - - {/* Business Location */} - - {tenantLocations.length == 1 ? ( - - ) + + + + > + + + + + Create Order + + + + + Fill pickup, drop, schedule & pricing details + + + + + + {tenantLocations.length === 1 ? ( + + {tenantLocations[0].locationname} + ) : ( `${option.locationname} (${option.suburb})` || ''} - onOpen={(event) => { - if (!appId && !tenantid) { - event.preventDefault(); - OpenToast('Please select Location and Tenant first!', 'warning', 3000); - setTimeout(() => { - locationRef.current?.focus(); - }, 0); - } else if (!tenantid) { - event.preventDefault(); - OpenToast('Please select a your Tenant first!', 'warning', 3000); - setTimeout(() => { - tenantRef.current?.focus(); - }, 0); - } - }} - onChange={(event, value, reason) => { - if (reason === 'clear') { - setLocationid(0); - setLocationValue(null); - setPickCust(null); - } else { - setLocationid(value.locationid || 0); - setLocationValue(value); - setDefaultPickup(value); - setPickupSlotsList(value?.slots); - } - }} + getOptionLabel={(option) => + option && option.locationname ? `${option.locationname} (${option.suburb || ''})` : '' + } + isOptionEqualToValue={(option, value) => option?.locationid === value?.locationid} + PaperComponent={SoftPaper} + sx={{ width: { xs: '100%', sm: 300 } }} + size="small" renderInput={(params) => ( - - {params.InputProps.startAdornment} - + + + + + ) }} - /> - )} - /> - )} - - - - - {/* Workspace 2-Column responsive layout */} - - - {/* Left Column: Form & Configurator (7.5 columns on lg+) */} - - - {/* Card 2: Route Planner (Pickup & Drop) — tighter padding on md+ since - the two panels now sit side-by-side and need the horizontal room. */} - - {/* Two-step stepper: Pickup → Drop */} - - setRouteStep(1)} - role="button" - tabIndex={0} - > - - {pickupStepComplete && routeStep !== 1 ? : '1'} - - - Pickup - Where to collect - - - - - - - - - - - { - if (pickupStepComplete) { - setRouteStep(2); - } else { - opentoast('Please complete Pickup details first', 'warning', 2000); - } - }} - role="button" - tabIndex={0} - > - 2 - - Drop - Where to deliver - - - - - - - {/* Pickup Details Block */} - - - - - - - - Pickup - Where to collect the parcel - - - - - - - - {/* Contact group */} - - Contact - - - - - - - ), - style: { borderRadius: '10px' } - }} - variant="outlined" - label="Contact Name" - value={pickCust?.firstname || ''} - onChange={(e) => { - setPickCust({ ...pickCust, firstname: e.target.value }); - }} - /> - - - - - - ), - style: { borderRadius: '10px' } - }} - variant="outlined" - label="Contact Number" - value={pickCust?.contactno || ''} - onChange={(e) => { - if (e.target.value.length <= 10) { - setPickCust({ ...pickCust, contactno: e.target.value }); - } - if (pickNum == e.target.value) { - setShowCheck1(0); - } else { - setShowCheck1(1); - } - if (e.target.value.length < 10) { - setNumErr1(true); - } else { - setNumErr1(false); - } - }} - /> - - - - {/* Address Autocomplete */} - - Address Lookup - - {addId1 == 0 ? ( - setInputValue1(e.target.value)} - helperText="Start typing to auto-fill the address details below" - FormHelperTextProps={{ className: 'address-search-helper pickup-helper' }} - InputProps={{ - startAdornment: ( - - - - ), - endAdornment: ( - { - setInputValue1(''); - setPickCust({ - ...pickCust, - doorno: '', - suburb: '', - city: '', - postcode: '', - landmark: '' - }); - setShowDistance(false); - setStartPoint({ latitude: 0, longitude: 0 }); - }} - size="small" - > - - - ), - style: { borderRadius: '10px', background: '#fff' } - }} - /> - ) : ( - - - - ), - endAdornment: ( - { - setAddId1(0); - setPickCust({ - ...pickCust, - doorno: '', - suburb: '', - city: '', - postcode: '', - landmark: '' - }); - setShowDistance(false); - setStartPoint({ latitude: 0, longitude: 0 }); - }} - size="small" - > - - - ), - style: { borderRadius: '10px', background: '#fff' } - }} - value={pickCust?.address || ''} - onChange={(e) => { - setPickCust({ ...pickCust, address: e.target.value }); - if (e.target.value == '') { - setAddId1(0); - setShowDistance(false); - setStartPoint({ latitude: 0, longitude: 0 }); - } - }} - /> - )} - - {/* Address details */} - - Address Details - - - - - - - ), - style: { borderRadius: '10px' } - }} - variant="outlined" - label="Door No / Street" - value={pickCust?.doorno || ''} - onChange={(e) => { - setPickCust({ ...pickCust, doorno: e.target.value }); - }} - /> - - - - - - ), - style: { borderRadius: '10px' } - }} - variant="outlined" - label="Location" - value={pickCust?.suburb || ''} - onChange={(e) => { - setPickCust({ ...pickCust, suburb: e.target.value }); - }} - /> - - - - - - ), - style: { borderRadius: '10px' } - }} - variant="outlined" - label="Postcode" - value={pickCust?.postcode || ''} - onChange={(e) => { - setPickCust({ ...pickCust, postcode: e.target.value }); - }} - /> - - - - - - ), - style: { borderRadius: '10px' } - }} - variant="outlined" - label="Landmark" - value={pickCust?.landmark || ''} - onChange={(e) => { - setPickCust({ ...pickCust, landmark: e.target.value }); - }} - /> - - - - {/* Save for later */} - {showCheck1 == 1 && ( - - - { - setIsNumChange1(e.target.checked ? 1 : 0); - }} - /> - } - label="Save contact for later" - /> - - - )} - - {/* Step navigation */} - - - {pickupStepComplete - ? 'Pickup looks good. Proceed to Drop details.' - : 'Fill the required Pickup fields to continue.'} - - - - - - {/* Drop Details Block */} - - - - - - - - Drop - Where to deliver the parcel - - - - - - - - {/* Contact group */} - - Contact - - - - - - - ), - style: { borderRadius: '10px' } - }} - value={dropCust?.firstname || ''} - onChange={(e) => { - setDropCust({ ...dropCust, firstname: e.target.value }); - }} - /> - - - - - - ), - style: { borderRadius: '10px' } - }} - value={dropCust?.contactno || ''} - onChange={(e) => { - if (e.target.value.length <= 10) { - setDropCust({ ...dropCust, contactno: e.target.value }); - } - if (dropNum == e.target.value) { - setShowCheck2(0); - } else { - setShowCheck2(1); - } - if (e.target.value.length < 10) { - setNumErr2(true); - } else { - setNumErr2(false); - } - }} - /> - - - - {/* Address Autocomplete */} - - Address Lookup - - {addId2 == 0 ? ( - setInputValue2(e.target.value)} - helperText="Start typing to auto-fill the address details below" - FormHelperTextProps={{ className: 'address-search-helper drop-helper' }} - InputProps={{ - startAdornment: ( - - - - ), - endAdornment: ( - { - setInputValue2(''); - setDropCust({ - ...dropCust, - doorno: '', - suburb: '', - city: '', - postcode: '', - landmark: '' - }); - setShowDistance(false); - setEndPoint({ latitude: 0, longitude: 0 }); - }} - size="small" - > - - - ), - style: { borderRadius: '10px', background: '#fff' } - }} - /> - ) : ( - - - - ), - endAdornment: ( - { - setAddId2(0); - setDropCust({ - ...dropCust, - doorno: '', - suburb: '', - city: '', - postcode: '', - landmark: '' - }); - setShowDistance(false); - setEndPoint({ latitude: 0, longitude: 0 }); - }} - size="small" - > - - - ), - style: { borderRadius: '10px', background: '#fff' } - }} - value={dropCust?.address || ''} - onChange={(e) => { - setDropCust({ ...dropCust, address: e.target.value }); - if (e.target.value == '') { - setAddId2(0); - setShowDistance(false); - setEndPoint({ latitude: 0, longitude: 0 }); - } - }} - /> - )} - - {/* Address details */} - - Address Details - - - - - - - ), - style: { borderRadius: '10px' } - }} - value={dropCust?.doorno || ''} - onChange={(e) => { - setDropCust({ ...dropCust, doorno: e.target.value }); - }} - /> - - - - - - ), - style: { borderRadius: '10px' } - }} - value={dropCust?.suburb || ''} - onChange={(e) => { - setDropCust({ ...dropCust, suburb: e.target.value }); - }} - /> - - - - - - ), - style: { borderRadius: '10px' } - }} - value={dropCust?.postcode || ''} - onChange={(e) => { - setDropCust({ ...dropCust, postcode: e.target.value }); - }} - /> - - - - - - ), - style: { borderRadius: '10px' } - }} - value={dropCust?.landmark || ''} - onChange={(e) => { - setDropCust({ ...dropCust, landmark: e.target.value }); - }} - /> - - - - {/* Save for later */} - {showCheck2 == 1 && ( - - - { - setIsNumChange2(e.target.checked ? 1 : 0); - }} - /> - } - label="Save contact for later" - /> - - - )} - - {/* Step navigation */} - - - - Review the route below once Drop is filled. - - - - - - - {/* Card 3: Cargo & Dispatch Logistics */} - - - - Cargo & Dispatch Logistics - - - - - {/* Section Header: Cargo Details */} - - - - Cargo Details - - - - - - {/* Row 1: Category, Cash Collect, Quantity */} - - - option?.subcategoryname || ''} - fullWidth sx={{ '& .MuiOutlinedInput-root': { - borderRadius: '12px', - height: '38px', - paddingTop: '0px !important', - paddingBottom: '0px !important' - } - }} - renderInput={(params) => ( - - - {params.InputProps.startAdornment} - - ) - }} - /> - )} - onChange={(event, value, reason) => { - if (value) { - console.log(value); - setSubCatName(value.subcategoryname || ''); - setSubCatId(value.subcategoryid || 0); - } else if (reason) { - setSubCatName(null); - setSubCatId(null); + borderRadius: DT.radiusPill + 'px', + bgcolor: '#fff', + fontWeight: 600, + '& fieldset': { borderColor: edge(BRAND), borderWidth: 1.5 }, + '&:hover fieldset': { borderColor: BRAND }, + '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring(BRAND)}` }, + '&.Mui-focused fieldset': { borderColor: BRAND, borderWidth: 2 } } }} /> - - + )} + onChange={(event, value, reason) => { + if (value) { + setTenanatLocoId(value.locationid); + setIsLocation(true); + } + if (reason === 'clear') { + setIsLocation(false); + } + }} + /> + )} + + + - - - { - setCollectionamt(e.target.value); - }} - inputProps={{ min: 0 }} - sx={{ '& .MuiOutlinedInput-root': { borderRadius: '12px', height: '38px' } }} - InputProps={{ - startAdornment: ( - - - - ) - }} - /> - - + + + + + + {/* ================================================= || Pickup || ================================================= */} + + + } + title="Pickup Details" + subtitle="Where the order is picked up" + action={ + + } + /> + + + + + {/* ====================================== ||Contact Name (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Contact Name" + value={pickCust.firstname} + onChange={(e) => { + setPickCust({ ...pickCust, firstname: e.target.value }); + }} + /> + + {/* ====================================== ||Contact Number(pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Contact Number" + value={pickCust.contactno} + onChange={(e) => { + if (e.target.value.length <= 10) { + setPickCust({ ...pickCust, contactno: e.target.value }); + } + if (pickNum == e.target.value) { + setShowCheck1(0); + } else { + setShowCheck1(1); + } + if (e.target.value.length < 10) { + setNumErr1(true); + } else { + setNumErr1(false); + } + }} + /> + + {/* ====================================== || Address (pick) || ====================================== */} + + + {addId1 == 0 ? ( +
    + setInputValue2(e.target.value)} + InputProps={{ + endAdornment: ( + { + setInputValue2(''); + setPickCust({ + ...pickCust, + doorno: '', + suburb: '', + city: '', + postcode: '', + landmark: '' + }); + setShowDistance(false); + setStartPoint({ latitude: 0, longitude: 0 }); + }} + size="small" + > + + + ) + }} + /> +
    + ) : ( + { + setAddId1(0); + setPickCust({ + ...pickCust, + // firstname: '', + // contactno: '', + doorno: '', + suburb: '', + city: '', + postcode: '', + landmark: '' + }); + setShowDistance(false); + setStartPoint({ latitude: 0, longitude: 0 }); + }} + > + + + ) + }} + variant="outlined" + placeholder="Select" + value={pickCust.address} + onChange={(e) => { + setPickCust({ ...pickCust, address: e.target.value }); + if (e.target.value == '') { + setAddId1(0); + setShowDistance(false); + setStartPoint({ latitude: 0, longitude: 0 }); + } + }} + /> + )} +
    +
    - - - { - setQuantity(e.target.value); - }} - inputProps={{ min: 1 }} - sx={{ '& .MuiOutlinedInput-root': { borderRadius: '12px', height: '38px' } }} - InputProps={{ - startAdornment: ( - - - - ) - }} - /> - - + {/* ====================================== ||Door No (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Door No / Street" + value={pickCust.doorno} + onChange={(e) => { + setPickCust({ ...pickCust, doorno: e.target.value }); + }} + /> + + {/* ====================================== || Suburb (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Location" + value={pickCust.suburb} + onChange={(e) => { + setPickCust({ ...pickCust, suburb: e.target.value }); + }} + /> + + {/* ====================================== || City (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="City" + value={pickCust.city} + onChange={(e) => { + setPickCust({ ...pickCust, city: e.target.value }); + }} + /> + + {/* ====================================== || postcode (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Postcode" + value={pickCust.postcode} + onChange={(e) => { + setPickCust({ ...pickCust, postcode: e.target.value }); + }} + /> + + {/* ====================================== || Landmark (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Landmark" + value={pickCust.landmark} + onChange={(e) => { + setPickCust({ ...pickCust, landmark: e.target.value }); + }} + /> + + {/* ====================================== ||Checkbox save for later (pick) || ====================================== */} + {showCheck1 == 1 && ( + + + { + setIsNumChange1(e.target.checked ? 1 : 0); + }} + /> + } + label="Save For Later" + /> + + + )} +
    +
    +
    +
    +
    +
    + {/* ================================================= || Drop || ================================================= */} + + + } + title="Drop Details" + subtitle="Where the order is delivered" + action={ + + } + /> + + + + + + Drop Details + + + {/* Customer */} + {/* { + if (val) { + setDropswitch(true); + setClientinfo({}); + } else { + setDropswitch(false); + setClientinfo({}); + } + }} + size="small" + /> + Business */} + + + + {/* new2 */} + {/* `${option.firstname} (${option.contactno})`} + sx={{ mt: 0, mb: 1 }} + renderInput={(params) => ( + { + setSearchCustList(e.target.value); + }} + InputProps={{ + ...params.InputProps, + inputProps: { + ...params.inputProps, + maxLength: 10 + } + }} + /> + )} + onChange={(e, val, reason) => { + if (val) { + if (pickCust.customerid === val.customerid) { + opentoast('Select Another Customer', 'error'); + setSearchCustList(''); + } else { + console.log('DropClient', val); + setDropCust(val); + setEndPoint({ latitude: val.latitude, longitude: val.longitude }); + setAddId2(1); } } + if (reason == 'clear') { + setSearchCustList(''); + } + }} + noOptionsText={ + /^[0-9]{10}$/.test(searchCustList) ? ( + + { + setDropCust({ ...dropCust, contactno: searchCustList }); + handleOkClick2(); + setSearchCustList(''); + }} + /> + + ) : null } - }} - disablePast - /> - - + /> */} + - - { - if (reason === 'clear' || !newValue) { - setSelectedtime(null); - setPickupSlot(null); - } else { - const formattedTime = dayjs(newValue.time, 'HH:mm').format('hh:mm A'); - setSelectedtime(formattedTime); - const finalDateTime = dayjs(`${startdate} ${formattedTime}`, 'MM-DD-YYYY hh:mm A').format( - 'YYYY-MM-DD hh:mm A' - ); - setPickupSlot(finalDateTime); - } - }} - getOptionLabel={(option) => option ? `${option.name} (${dayjs(option.time, 'HH:mm').format('hh:mm A')})` : ''} - renderInput={(params) => ( - + + {/* ====================================== ||Contact Name (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.firstname} + onChange={(e) => { + setDropCust({ ...dropCust, firstname: e.target.value }); + }} + /> + + {/* ====================================== ||Contact Number (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.contactno} + onChange={(e) => { + if (e.target.value.length <= 10) { + setDropCust({ ...dropCust, contactno: e.target.value }); + } + if (dropNum == e.target.value) { + setShowCheck2(0); + } else { + setShowCheck2(1); + } + if (e.target.value.length < 10) { + setNumErr2(true); + } else { + setNumErr2(false); + } + }} + /> + + + + {addId2 == 0 ? ( +
    + setInputValue3(e.target.value)} + InputProps={{ + endAdornment: ( + { + setInputValue3(''); + setDropCust({ + ...dropCust, + doorno: '', + suburb: '', + city: '', + postcode: '', + landmark: '' + }); + setShowDistance(false); + setEndPoint({ latitude: 0, longitude: 0 }); + }} + size="small" + > + + + ) + }} + /> +
    + ) : ( + { + setAddId2(0); + setDropCust({ + ...dropCust, + firstname: '', + contactno: '', + doorno: '', + suburb: '', + city: '', + postcode: '', + landmark: '' + }); + setShowDistance(false); + setEndPoint({ latitude: 0, longitude: 0 }); + }} + > + + + ) + }} + variant="outlined" + placeholder="Select" + value={dropCust.address} + onChange={(e) => { + setPickCust({ ...dropCust, address: e.target.value }); + if (e.target.value == '') { + setAddId2(0); + setShowDistance(false); + setEndPoint({ latitude: 0, longitude: 0 }); + } + }} + /> + )} +
    +
    + + {/* ====================================== ||Door No (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.doorno} + onChange={(e) => { + setDropCust({ ...dropCust, doorno: e.target.value }); + }} + /> + + {/* ====================================== ||Suburb (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.suburb} + onChange={(e) => { + setDropCust({ ...dropCust, suburb: e.target.value }); + }} + /> + + {/* ====================================== ||City (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.city} + onChange={(e) => { + setDropCust({ ...dropCust, city: e.target.value }); + }} + /> + + + {/* ====================================== ||Postcode (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.postcode} + onChange={(e) => { + setDropCust({ ...dropCust, postcode: e.target.value }); + }} + /> + + {/* ====================================== ||Landmark (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.landmark} + onChange={(e) => { + setDropCust({ ...dropCust, landmark: e.target.value }); + }} + /> + + {/* ====================================== ||Checkbox save for later (drop) || ====================================== */} + {showCheck2 == 1 && ( + + + { + setIsNumChange2(e.target.checked ? 1 : 0); + }} + /> + } + label="Save For Later" + /> + + + )} +
    +
    + {/* + + */} +
    +
    + +
    + {/* ================================================= || Time || ================================================= */} + + + } + title="Schedule" + subtitle="Pickup date & time slot" + /> + + + + Date + + { + let dateres11 = dayjs().diff(dayjs(`${dayjs(e).format('YYYY-MM-DD')}`), 'd'); + console.log('dateres11'); + console.log(dateres11); + setSelectedtime(''); + if (dateres11 <= 0) { + console.log('startdate', e); + setStartdate(e); + setEnddate(e); + + let arr = []; + timeslotarr.map((val) => { + if ( + dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0 + ) { + arr.push(val); + } + }); + if (arr[0]) { + setOrderarr([ + { + sno: 1, + address: '', + customerid: '', + deliverytime: dayjs(arr[0]) || '', + deliverylocationid: '', + clientname: '', + contactno: '', + latitude: '', + longitude: '' + } + ]); + } else { + setOrderarr([]); + } + } else { + setAlertmessage('choose Upcoming Date'); + opentoast('choose Upcoming Date', 'warning'); + setStartdate(NaN); + } + }} + value={dayjs(startdate)} + sx={{ width: '100%', mt: 2 }} + disablePast + /> + + + Time + + + + {timeslotarr.map((val, index) => { + if ( + dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0 + ) { + const active = dayjs(selectedtime).format('HH:mm') == dayjs(val).format('HH:mm'); + return ( + { + if (distance > appLocaRadius) { + setOpen4(true); + } else if (showDistance) { + setSelectedtime(val); + } else { + opentoast('Select Pickup and Drop', 'error'); + } + }} + sx={{ + display: 'inline-flex', + alignItems: 'center', + gap: 0.5, + px: 1.25, + py: 0.5, + cursor: 'pointer', + borderRadius: 999, + border: `1.5px solid ${active ? '#f59e0b' : edge('#f59e0b')}`, + bgcolor: active ? '#f59e0b' : tint('#f59e0b'), + color: active ? '#fff' : '#f59e0b', + fontSize: 12, + fontWeight: 800, + transition: 'all 0.15s', + boxShadow: active ? `0 6px 18px ${ring('#f59e0b')}` : 'none', + '&:hover': { + borderColor: '#f59e0b', + boxShadow: active ? `0 6px 18px ${ring('#f59e0b')}` : `0 0 0 3px ${ring('#f59e0b')}` + } + }} + > + {dayjs(val).format('hh:mm A')} + + ); + } + })} + + + + + + + + + + } + title="Distance & Pricing" + subtitle="Auto-calculated from pickup → drop" + /> + + {showDistance && ( + + + + + + + + + )} + + Category + `${option.subcategoryname}` || ''} + sx={{ my: 2, zIndex: '100' }} fullWidth - InputLabelProps={{ shrink: true }} - sx={{ '& .MuiOutlinedInput-root': { borderRadius: '12px', height: '38px' } }} - InputProps={{ - ...params.InputProps, - startAdornment: ( - <> - - {params.InputProps.startAdornment} - - ) + renderInput={(params) => } + onChange={(event, value, reason) => { + if (value) { + console.log(value); + setSubCatName(value.subcategoryname); + setSubCatId(value.subcategoryid); + } }} /> - )} - /> - - + + Weight + + { + handleChipClick('1-10kgs'); + setWeight('1-10kgs'); + }} + /> + { + handleChipClick('11-20kgs'); + setWeight('11-20kgs'); + }} + /> + { + handleChipClick('21-30kgs'); + setWeight('21-30kgs'); + }} + /> + + + SMS Delivery + { + setIsSms(e.target.checked ? 1 : 0); + }} + /> + + + - - + - {/* Right Column: Live Map & Cost Summary Panel (4.5 columns on lg+, sticky) */} - + } + title="Collection & Quantity" + subtitle="Cash to collect on delivery, total items" + /> + + + + setCollectionamt(e.target.value)} + inputProps={{ min: 0 }} + /> + + + setQuantity(e.target.value)} + inputProps={{ min: 1 }} + /> + + + + + + {/* ================================================= || Notes || ================================================= */} + + } + title="Notes" + subtitle="Add anything the rider should know" + /> + + setOtherinstructions(e.target.value)} + /> + + + {!showDistance && ( + + Set pickup & drop to enable + + )} + + + + + + + + {/* + + + Select Time + + + + + + {timeslotarr.map((val) => { + if ( + dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0 + // && currentsno + ) { + return ( + <> + + { + setSelectedtime(dayjs(val).format('hh:mm A')); + }} + /> + + + ); + } + })} + + + + + + + + */} + + {/* ============================================= || saved address Dialog || ============================================= */} + setIsCustomerOpen(false)} + fullWidth + maxWidth="sm" + PaperProps={{ sx: { borderRadius: 3 } }} + > + - - - {/* Map Card */} - - - - Live Route Preview + + + + + + + Saved Locations -
    - -
    -
    - - {/* Delivery Preferences — Dispatch Notes & SMS Updates */} - - - Delivery Preferences - Customer notifications & dispatch instructions - - - - - - setOtherinstructions(e.target.value)} - sx={{ - '& .MuiOutlinedInput-root': { - borderRadius: '10px', - padding: '0 10px', - alignItems: 'center', - fontSize: '12px', - background: '#ffffff', - height: '32px' - }, - '& .MuiOutlinedInput-input': { - padding: '0 !important', - fontSize: '12px !important', - lineHeight: '32px' - } - }} - /> - - - setIsSms(isSms === 1 ? 0 : 1)} - role="button" - tabIndex={0} - > - - - - - - SMS Updates - Auto-notify customer on dispatch & delivery - - - { - e.stopPropagation(); - setIsSms(e.target.checked ? 1 : 0); - }} - /> - - - - - {/* Pricing breakdown card */} - - - Pricing & Dispatch - Live cost estimate - - -
    -
    - - - - Delivery Distance -
    -
    - {showDistance ? `${distance} km` : '—'} -
    -
    - -
    -
    - - - - - Base Fare - · {minKm} km - -
    -
    - {basePrice ? `₹${basePrice.toFixed(2)}` : '₹0.00'} -
    -
    - -
    -
    - - - - Rate per km -
    -
    - {pricePerKm ? `₹${pricePerKm.toFixed(2)}` : '₹0.00'} - /km -
    -
    - - {/* Total Cost Display */} - {showDistance && ( -
    -
    - -
    Total Delivery Charge
    -
    -
    ₹{totalCharge.toFixed(2)}
    -
    - )} - - {/* Submit button */} - - - - - -
    + + Pick a saved customer for {pickordrop === 1 ? 'Pickup' : 'Drop'} + +
    - - - - - {/* Saved address Dialog (Modal) */} - { - setIsCustomerOpen(false); - setSearchCustList(''); - }} - fullWidth - fullScreen={isMobile} - sx={{ - '& .MuiDialog-paper': { - borderRadius: { xs: 0, sm: '16px' }, - overflow: 'hidden' - } - }} - > - - - - {`Select Saved Address (${pickordrop === 1 ? 'Pickup' : 'Drop'})`} - - - + + setSearchCustList(e.target.value)} sx={{ - bgcolor: 'white', - borderRadius: '10px', - '& .MuiOutlinedInput-input': { - p: '10px 14px' - } + flex: 1, + '& .MuiOutlinedInput-input': { p: '6px 4px', fontSize: 13, fontWeight: 600 }, + '& fieldset': { border: 'none' } }} - startAdornment={ - - - - } endAdornment={ - { - setSearchCustList(''); - }} - > - - + searchCustList && ( + setSearchCustList('')} sx={{ p: 0.25, color: BRAND }}> + + + ) } autoComplete="off" /> - - - - - - - - {customerlist?.length == 0 ? ( - - - - ) : ( - - {customerlist && - customerlist.map((address, index) => ( - - ))} - )} - +
    + + {customerlist.length === 0 ? ( + + + + + + No saved locations + + + {searchCustList ? 'Try a different keyword.' : 'Save a customer from the order form to reuse it later.'} + + + ) : ( + + {customerlist.map((address, index) => { + const isUsed = pickCust.customerid === address.customerid; + return ( + { + if (isUsed) return; + setIsCustomerOpen(false); + if (pickordrop === 1) { + setAddId1(1); + setStartPoint({ latitude: address.latitude, longitude: address.longitude }); + setPickCust(address); + setPickNum(address.contactno); + } else { + setAddId2(1); + setEndPoint({ latitude: address.latitude, longitude: address.longitude }); + setDropCust(address); + setdropNum(address.contactno); + } + }} + sx={{ + display: 'flex', + alignItems: 'flex-start', + gap: 1.25, + p: 1.5, + borderRadius: 2, + border: '1px solid', + borderColor: isUsed ? edge('#94a3b8') : DT.borderSubtle, + bgcolor: isUsed ? DT.surfaceAlt : '#fff', + cursor: isUsed ? 'not-allowed' : 'pointer', + opacity: isUsed ? 0.6 : 1, + transition: 'all 0.15s', + '&:hover': isUsed + ? {} + : { + borderColor: edge(BRAND), + bgcolor: tint(BRAND), + boxShadow: DT.shadowSoft + } + }} + > + + {String(address.firstname || '?').charAt(0).toUpperCase()} + + + + + {address.firstname} + + + {address.contactno} + + {isUsed && ( + + Already used + + )} + + + {address.address} + + + + ); + })} + + )} + + + + +
    - - - - - - - - {/* Location error Dialog */} - { - setOpen(false); - }} - fullWidth - sx={{ - '& .MuiDialog-paper': { - borderRadius: { xs: 3, sm: '16px' }, - p: 1.5, - m: { xs: 1.5, sm: 4 }, - width: { xs: 'calc(100% - 24px)', sm: 'auto' }, - maxWidth: '400px' - } - }} - > - - - - Dispatch Blocked - - - - - - Our delivery partners cannot service this coordinate combination. The route distance exceeds the active operating radius for this app location. - - - - { - setOpen(false); - }} - > - Acknowledge & Close - - + + + + + + + Out of Service Area + + + This drop point is outside the supported radius + + + + + + + Service is not available at this location. Try a different drop point within the coverage zone. + + + + + + + ); }; diff --git a/src/pages/nearle/orders/details.js b/src/pages/nearle/orders/details.js index f52c6ed..97a4732 100644 --- a/src/pages/nearle/orders/details.js +++ b/src/pages/nearle/orders/details.js @@ -1,8 +1,7 @@ +/* eslint-disable no-unused-vars */ import { - useEffect, - useState, - Fragment - // useReducer + useEffect, useState, Fragment + // useReducer } from 'react'; import BorderColorIcon from '@mui/icons-material/BorderColor'; import { @@ -17,20 +16,21 @@ import { // UserOutlined, EnvironmentOutlined, EditTwoTone - // DeleteTwoTone + // DeleteTwoTone } from '@ant-design/icons'; // import WomanIcon from '@mui/icons-material/Woman'; // import { Link } from 'react-router-dom'; // import SoupKitchenIcon from '@mui/icons-material/SoupKitchen'; import DirectionsCarIcon from '@mui/icons-material/DirectionsCar'; -import { KeyboardArrowUp, KeyboardArrowDown } from '@mui/icons-material'; +import SendIcon from '@mui/icons-material/Send'; +import { KeyboardArrowUp, KeyboardArrowDown } from '@mui/icons-material' // import { PopupTransition } from 'components/@extended/Transitions'; // import { useDispatch } from 'react-redux'; // import { openSnackbar } from 'store/reducers/snackbar'; // assets import { DeleteFilled, NotificationOutlined } from '@ant-design/icons'; -var utc = require('dayjs/plugin/utc'); +var utc = require('dayjs/plugin/utc') // import { groupBy } from "core-js/actual/array/group-by"; // import "lodash.chunk"; // var chunk = require('lodash.chunk'); @@ -81,15 +81,13 @@ import MainCard from 'components/MainCard'; import Loader from 'components/Loader'; // import AlertCustomerDelete from 'sections/apps/customer/AlertCustomerDelete'; import dayjs from 'dayjs'; -dayjs.extend(utc); +dayjs.extend(utc) // import { Link as RouterLink } from 'react-router-dom'; // import PlayCircleFilled from '@mui/icons-material/PlayCircleFilled'; // import SmileFilled from '@mui/icons-material/Mood'; // import HeartFilled from '@mui/icons-material/Favorite'; import { useTheme } from '@mui/material/styles'; -import useMediaQuery from '@mui/material/useMediaQuery'; -import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; import { CloseOutlined, WarningOutlined, @@ -100,6 +98,91 @@ import { // DeleteTwoTone } from '@ant-design/icons'; import { enqueueSnackbar } from 'notistack'; +import { + MdLocalShipping, + MdHourglassEmpty, + MdCheckCircle, + MdCancel, + MdAccessTime, + MdHistoryToggleOff, + MdAssignmentTurnedIn, + MdEdit, + MdArrowBack, + MdReceiptLong +} from 'react-icons/md'; +import { Paper, Box } from '@mui/material'; + +// ============================================================================ +// Design tokens — shared with the rest of the redesigned operator pages. +// ============================================================================ +const DT = { + radiusPill: 999, + radiusCard: 16, + shadowSoft: '0 14px 40px rgba(15, 23, 42, 0.10)', + shadowMd: '0 8px 24px rgba(15, 23, 42, 0.08)', + shadowPop: '0 18px 50px rgba(15, 23, 42, 0.18)', + textPrimary: '#0f172a', + textSecondary: '#64748b', + textMuted: '#94a3b8', + borderSubtle: '#e2e8f0', + divider: '#f1f5f9', + surface: '#ffffff', + surfaceAlt: '#f8fafc' +}; +const dtA = (c, suffix) => `${c}${suffix}`; +const tint = (c) => dtA(c, '08'); +const soft = (c) => dtA(c, '18'); +const ring = (c) => dtA(c, '26'); +const edge = (c) => dtA(c, '55'); + +const BRAND = '#C01227'; +const BRAND_LIGHT = '#D25463'; + +// Semantic per-status palette — also drives StatusBadge. +const STATUS_META = { + pending: { label: 'Pending', color: '#f59e0b', icon: MdHourglassEmpty }, + assigned: { label: 'Assigned', color: '#0ea5e9', icon: MdAssignmentTurnedIn }, + confirmed: { label: 'Confirmed', color: '#10b981', icon: MdCheckCircle }, + modified: { label: 'Modified', color: '#06b6d4', icon: MdHistoryToggleOff }, + processing: { label: 'Processing', color: BRAND, icon: MdAccessTime }, + active: { label: 'Active', color: '#8b5cf6', icon: MdLocalShipping }, + closed: { label: 'Closed', color: '#06b6d4', icon: MdCheckCircle }, + completed: { label: 'Completed', color: '#10b981', icon: MdCheckCircle }, + cancelled: { label: 'Cancelled', color: '#ef4444', icon: MdCancel } +}; + +const StatusBadge = ({ status, size = 'md' }) => { + if (!status) return null; + const meta = STATUS_META[String(status).toLowerCase()] || { + label: status, + color: DT.textMuted, + icon: MdHistoryToggleOff + }; + const Icon = meta.icon; + const px = size === 'lg' ? 1.25 : 1; + const py = size === 'lg' ? 0.5 : 0.375; + const fs = size === 'lg' ? 12 : 11; + return ( + + {meta.label} + + ); +}; const Details = () => { // const [searchParams] = useSearchParams(); @@ -116,7 +199,7 @@ const Details = () => { const [tenantaddress, setTenantaddress] = useState(''); const [dialogopen, setDialogopen] = useState(false); const [orderstatus, setOrderstatus] = useState(''); - const [currentrole] = useState(''); + const [currentrole, setCurrentrole] = useState(''); const [taxamount, setTaxamount] = useState(''); const [subtotal, setSubtotal] = useState(''); const [grandtotal, setGrandtotal] = useState(''); @@ -128,12 +211,14 @@ const Details = () => { const [staffarr, setStaffarr] = useState([]); const [orderheaderid, setOrderheaderid] = useState(''); const [tenantid, setTenantid] = useState(''); - const [starttime] = useState(''); - const [endtime] = useState(''); + const [starttime, setStarttime] = useState(''); + const [endtime, setEndtime] = useState(''); // const [orderstatus,setOrderstatus]=useStatus(''); const [pendingtime, setPendingtime] = useState(''); // const [processdate,setProcessdate]=useState(''); - const [categoryarr, setcategoryarr] = useState([]); + const [orderdetailid, setOrderdetailid] = useState(''); + const [productid, setProductid] = useState(''); + const [categoryarr, setcategoryarr] = useState([]) const [currentshiftobj, setCurrentshiftobj] = useState({ shifts: 0, assigned: 0, @@ -141,22 +226,22 @@ const Details = () => { shiftid: 0, price: 0 }); - const [tabstatus, setTabstatus] = useState(0); + const [tabstatus, setTabstatus] = useState(0) const { state } = useLocation(); const [assignedpendingcount, setAssignedpendingcount] = useState(''); // const [, forceUpdate] = useReducer(x => x + 1, 0); // const dispatch = useDispatch(); const navigate = useNavigate(); const [expandopen, setExpandopen] = useState(['', '']); - const [startdate, setStartdate] = useState(''); - const [invoiceeligible, setInvoiceeligible] = useState(false); + const [startdate, setStartdate] = useState('') + const [invoiceeligible, setInvoiceeligible] = useState(false) useEffect(() => { - console.log('categoryarr'); + console.log("categoryarr") console.log(orderarr, eventlocation, venuetype, starttime, endtime); // forceUpdate(); - }, [categoryarr]); + }, [categoryarr]) // const navigate = useNavigate(); useEffect(() => { @@ -164,22 +249,23 @@ const Details = () => { setOrderheaderid(state.orderheaderid); setTenantid(state.tenantid); } - console.log(state); + console.log(state) // fetchroleslist(1) - console.log(Date.now()); - }, []); + console.log(Date.now()) + }, []) useEffect(() => { if (state) { - setOrderheaderid(state.orderheaderid); - setTenantid(state.tenantid); + setOrderheaderid(state.orderheaderid ); + setTenantid(state.tenantid ); } - console.log(state); + console.log(state) // fetchroleslist(1) - console.log(Date.now()); - }, [state.orderheaderid, state.tenantid]); + console.log(Date.now()) + }, [ + state.orderheaderid, state.tenantid + ]) const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('md')); // const fetchorderdetails = async () => { // setLoading(true); @@ -205,40 +291,47 @@ const Details = () => { .then((res) => { console.log(res); - let result = res.data.Details.find((res1) => res1.orderheaderid == orderheaderid); + let result = res.data.Details.find((res1) => res1.orderheaderid == orderheaderid) // orderheaderid - console.log('result'); + console.log("result") - console.log(result); + console.log(result) setOrderaddons(result.orderaddons); - setVenuetype(result.venuetype); - setOtherinstructions(result.remarks); - setStartdate(result.startdate); + setVenuetype(result.venuetype) + setOtherinstructions(result.remarks) + setStartdate(result.startdate) + + setClientname(result.tenantname); setEventlocation(''); setTenantaddress(result.tenantaddress); setEventname(result.eventname); + + setOrderdate(dayjs(result.orderdate).format('MM/DD/YYYY') || ''); setOrderid(result.orderid); setOrderstatus(result.orderstatus); setTaxamount(result.taxamount); - setSubtotal(result.orderamount); + setSubtotal(result.orderamount) setGrandtotal(result.ordervalue); setCancelleddate(dayjs(result.cancelled).format('MM/DD/YYYY') || ''); // if (result.orderstatus === 'pending') { - setPendingtime(result.pending); + setPendingtime(result.pending) // } + setLoading(false); }) .catch((err) => { console.log(err); setLoading(false); }); - }; + } + + const fetchorderattires = async () => { setLoading(true); @@ -265,7 +358,7 @@ const Details = () => { // setcategoryarr(result); console.log('categoryarr'); - setcategoryarr(res.data.Details); + setcategoryarr(res.data.Details) console.log(res.data.Details); setLoading(false); @@ -274,52 +367,94 @@ const Details = () => { console.log(err); setLoading(false); }); - }; + } + const fetchroleslist = async (cid, starttime1, endtime1, hid, sid) => { + let fromdate = dayjs(starttime1).subtract(59, 'minutes') + .format('YYYY-MM-DD HH:mm:ss'); + // let todate = dayjs(endtime1).utc().format('YYYY-MM-DD HH:mm:ss'); + // let fromdate = dayjs(starttime1).utc().format('YYYY-MM-DD HH:mm:ss'); + let todate = dayjs(endtime1) + .format('YYYY-MM-DD HH:mm:ss'); + let url1; + if (starttime1) { + url1 = `${process.env.REACT_APP_URL2}/staffs/pools/getstaffs/?categoryid=${cid}&headerid=${hid}&shiftid=${sid}&starttime=${fromdate}&endtime=${todate}`; + } else { + url1 = `${process.env.REACT_APP_URL2}/staffs/pools/getstaffs/?categoryid=${cid}&headerid=${hid}&shiftid=${sid}` + } + setLoading(true); + setStafflist([]) + await axios + // .get(`${process.env.REACT_APP_URL2}/staffs/pools/getstaffs/?categoryid=${cid}&headerid=${hid}&starttime=${fromdate}&endtime=${todate}`) + .get(url1) + .then((res) => { + console.log('rolelist'); + console.log(res); + // console.log(fromdate, todate) + // console.log(dayjs(starttime1).format('YYYY-MM-DD HH:mm:ss')) + // console.log(dayjs(endtime1).format('HH:mm:ss')) + + + setStafflist(res.data.Details || []) + // let result = res.data.Details.find((res1) => res1.orderheaderid == searchParams.get('id')) + // orderheaderid + // console.log(result) + // setOrderaddons(result.orderaddons); + // setVenuetype(result.venuetype) + // setOtherinstructions(result.remarks) + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + } const fetchstafflist = async (odid) => { - setLoading(true); + setLoading(true) try { // await axios.get(`${process.env.REACT_APP_URL}/orders/orderanalytics?orderdate=${chosendate}`) - await axios - .get(`${process.env.REACT_APP_URL2}/orders/getassignedinfo?orderdetailid=${odid}`) + await axios.get(`${process.env.REACT_APP_URL2}/orders/getassignedinfo?orderdetailid=${odid}`) .then((res) => { - console.log(res); + console.log(res) if (res.data.status) { - setStafflist(res.data.Details); + setStafflist(res.data.Details) } - setLoading(false); + setLoading(false) + }).catch((err) => { + console.log(err) + setLoading(false) }) - .catch((err) => { - console.log(err); - setLoading(false); - }); + } catch (err) { console.log(err); - setLoading(false); + setLoading(false) } - }; + + } const cancelorder = async () => { - await axios - .put(`${process.env.REACT_APP_URL2}/orders/cancel`, { - // "Orderheaderid": parseInt(orderheaderid), - // "Tenantid": parseInt(tenantid), - // "Orderstatus": "cancelled", - // "Currentdatetime": dayjs().format('YYYY-MM-DD HH:mm:ss'), - // "Cod": false, - // "Remarks": "", - orderheaderid: parseInt(orderheaderid), - // "orderdetailid":78, - // "shiftid":788, - orderstatus: 'cancelled', - cancelled: dayjs().format('YYYY-MM-DD HH:mm:ss'), - unserviceable: invoiceeligible ? 0 : 1 - }) + + await axios.put(`${process.env.REACT_APP_URL2}/orders/cancel`, { + + // "Orderheaderid": parseInt(orderheaderid), + // "Tenantid": parseInt(tenantid), + // "Orderstatus": "cancelled", + // "Currentdatetime": dayjs().format('YYYY-MM-DD HH:mm:ss'), + // "Cod": false, + // "Remarks": "", + "orderheaderid": parseInt(orderheaderid), + // "orderdetailid":78, + // "shiftid":788, + "orderstatus": "cancelled", + "cancelled": dayjs().format('YYYY-MM-DD HH:mm:ss'), + "unserviceable": (invoiceeligible) ? 0 : 1, + + }) .then((res) => { - console.log(res); + console.log(res) if (res.data.status) { if (orderheaderid && tenantid) { // fetchorderdetails(); @@ -327,114 +462,121 @@ const Details = () => { fetchorderattires(); } } + + }).catch((err) => { + console.log(err) }) - .catch((err) => { - console.log(err); - }); - }; + + } const unassign = async (val) => { + let obj = { orderheaderid: orderheaderid, orderprocessid: val.orderprocessid, orderdetailid: val.orderdetailid, - orderstatus: 'pending', + orderstatus: "pending", pending: dayjs().format('YYYY-MM-DD HH:mm:ss'), // processing:0, // cancelled:0, // completed:0, // accepted:0, status: 1 - }; - console.log(obj); + } + console.log(obj) - await axios - .put(`${process.env.REACT_APP_URL2}/orders/updateprocessstatus`, obj) + await axios.put(`${process.env.REACT_APP_URL2}/orders/updateprocessstatus`, obj) .then((res) => { - console.log(res); - if (res.data.message === 'Successful') { + console.log(res) + if (res.data.message === "Successful") { // if (orderheaderid && tenantid) { enqueueSnackbar('Role unassigned successfully', { - variant: 'success', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, + variant: 'success', anchorOrigin: { vertical: 'top', horizontal: 'right' }, autoHideDuration: 2000 - }); + }) if (currentshiftobj.assigned > currentshiftobj.shifts) { - sendunassignnotification(val); + sendunassignnotification(val) } + + fetchorderaddons(); fetchorderattires(); setOpen(false); fetchassignedcount(); - dialogclose(); + dialogclose() setTimeout(() => { fetchassignedcount(); - }, 2000); + + }, 2000) } + + }).catch((err) => { + console.log(err) }) - .catch((err) => { - console.log(err); - }); - }; + + } + const sendunassignnotification = (val) => { - console.log(val); + console.log(val) let data2; - let tokenarr = [val.userfcmtoken]; - let arr1 = [ - { - notificationid: 0, - notificationdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), - Title: 'Staff Un-Asigned', - message: `${val.firstname} has been Un-assigned to the order ${orderid}`, - configid: 2, - tenantid: tenantid, - orderheaderid: orderheaderid, - orderprocessid: val.orderprocessid, - shiftid: val.shiftid, - userid: val.userid, - orderid: orderid, - success: 0, - orderstatus: 'assigned', - processing: dayjs().format('YYYY-MM-DD HH:mm:ss'), - notifytype: 2, - notifyreason: 'Staff Un-Assigned' - // "sound": "ring", - // "click_action": "FLUTTER_NOTIFICATION_CLICK", - // "firstname": val.firstname - } - ]; + let tokenarr = [val.userfcmtoken] + let arr1 = [{ + "notificationid": 0, + "notificationdate": dayjs().format('YYYY-MM-DD HH:mm:ss'), + "Title": "Staff Un-Asigned", + "message": `${val.firstname} has been Un-assigned to the order ${orderid}`, + "configid": 2, + "tenantid": tenantid, + "orderheaderid": orderheaderid, + "orderprocessid": val.orderprocessid, + "shiftid": val.shiftid, + "userid": val.userid, + "orderid": orderid, + "success": 0, + "orderstatus": 'assigned', + "processing": dayjs().format('YYYY-MM-DD HH:mm:ss'), + "notifytype": 2, + "notifyreason": 'Staff Un-Assigned' + // "sound": "ring", + // "click_action": "FLUTTER_NOTIFICATION_CLICK", + // "firstname": val.firstname + + }]; + + + data2 = { - Title: 'Staff Un-Asigned', - message: `A Staff has been Un-assigned to the order ${orderid}`, - tenantid: tenantid, - orderheaderid: orderheaderid, - orderid: orderid, - configid: 2 + "Title": "Staff Un-Asigned", + "message": `A Staff has been Un-assigned to the order ${orderid}`, + "tenantid": tenantid, + "orderheaderid": orderheaderid, + "orderid": orderid, + "configid": 2, // "click_action": "FLUTTER_NOTIFICATION_CLICK" - }; + } let fcmmodel = { - priority: 'high', - registration_ids: tokenarr, - data: data2, - notification: { - body: `An order has been Un-assigned ${orderid}`, - title: 'Legendary', - sound: 'ring', - content_available: true, - click_action: 'FLUTTER_NOTIFICATION_CLICK' + "priority": "high", + "registration_ids": tokenarr, + "data": data2, + "notification": { + "body": `An order has been Un-assigned ${orderid}`, + "title": "Legendary", + "sound": "ring", + "content_available": true, + "click_action": "FLUTTER_NOTIFICATION_CLICK" } - }; + } let grpnotifyobj = { - notifications: arr1, - fcmmodel: fcmmodel - }; - console.log('grpnotifyobj unassign'); - console.log(grpnotifyobj); - sendgroupnotification(grpnotifyobj); - }; + "notifications": arr1, + "fcmmodel": fcmmodel + } + console.log("grpnotifyobj unassign") + console.log(grpnotifyobj) + sendgroupnotification(grpnotifyobj) + } useEffect(() => { console.log(orderheaderid); @@ -442,7 +584,7 @@ const Details = () => { // fetchorderdetails(); fetchorderaddons(); fetchorderattires(); - fetchassignedcount(); + fetchassignedcount() // fetchuserdetails(); console.log(location.state || ''); // setOrderid(location.state.orderid || ''); @@ -457,7 +599,7 @@ const Details = () => { setLoading(false); } // fetchorderdetails(); - console.log(orderheaderid, tenantid); + console.log(orderheaderid, tenantid) }, [orderheaderid, tenantid, assignedpendingcount]); const handleClose = () => { @@ -466,75 +608,80 @@ const Details = () => { const dialogclose = () => { setDialogopen(false); setStaffarr([]); - setExpandopen(['', '']); + setExpandopen(['', '']) }; useEffect(() => { - console.log(currentshiftobj); - }); + console.log(currentshiftobj) + }) + const assignok = async () => { - let arr = []; + let arr = [] let arr1 = []; staffarr.map((val) => { arr.push({ - orderprocessid: 0, + "orderprocessid": 0, // "processdate": `${dayjs(new Date()).format('YYYY-MM-DD')} ${dayjs(new Date()).format('HH:mm:ss')}`, //current date - processdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), //current date - tenantid: tenantid, - orderheaderid: orderheaderid, - orderdetailid: val.orderdetailid, //// - productid: val.productid, ///// - userid: val.userid, - orderstatus: 'pending', - pending: `${dayjs(pendingtime).format('YYYY-MM-DD')} ${dayjs(pendingtime).format('HH:mm:ss')}`, // if pending - starttime: `${dayjs(starttime).format('YYYY-MM-DD')} ${dayjs(starttime).format('HH:mm:ss')}`, - endtime: `${dayjs(endtime).format('YYYY-MM-DD')} ${dayjs(endtime).format('HH:mm:ss')}`, - appuserid: parseInt(localStorage.getItem('appuserid')), //loginuserid - shiftid: val.shiftid, - userrate: val.userrate, - productrate: val.productrate - }); + "processdate": dayjs().format('YYYY-MM-DD HH:mm:ss'), //current date + "tenantid": tenantid, + "orderheaderid": orderheaderid, + "orderdetailid": val.orderdetailid,//// + "productid": val.productid,///// + "userid": val.userid, + "orderstatus": "pending", + "pending": `${dayjs(pendingtime).format('YYYY-MM-DD')} ${dayjs(pendingtime).format('HH:mm:ss')}`, // if pending + "starttime": `${dayjs(starttime).format('YYYY-MM-DD')} ${dayjs(starttime).format('HH:mm:ss')}`, + "endtime": `${dayjs(endtime).format('YYYY-MM-DD')} ${dayjs(endtime).format('HH:mm:ss')}`, + "appuserid": parseInt(localStorage.getItem("appuserid")), //loginuserid + "shiftid": val.shiftid, + "userrate": val.userrate, + "productrate": val.productrate + }) staffarr.map((val) => { arr1.push({ - notificationid: 0, - notificationdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), - Title: 'Staff Asigned', - message: `A Staff has been assigned to the order ${orderid}`, - configid: 2, - tenantid: tenantid, - orderheaderid: orderheaderid, - orderprocessid: 0, - shiftid: val.shiftid, - userid: val.userid, - orderid: orderid, - sound: 'ring', - click_action: 'FLUTTER_NOTIFICATION_CLICK', - firstname: val.firstname - }); - }); - }); - console.log('arr'); - console.log(arr); + "notificationid": 0, + "notificationdate": dayjs().format('YYYY-MM-DD HH:mm:ss'), + "Title": "Staff Asigned", + "message": `A Staff has been assigned to the order ${orderid}`, + "configid": 2, + "tenantid": tenantid, + "orderheaderid": orderheaderid, + "orderprocessid": 0, + "shiftid": val.shiftid, + "userid": val.userid, + "orderid": orderid, + "sound": "ring", + "click_action": "FLUTTER_NOTIFICATION_CLICK", + "firstname": val.firstname + }) - await axios - .post(`${process.env.REACT_APP_URL2}/orders/createorderprocess`, arr) + }) + + }) + console.log('arr') + console.log(arr) + + await axios.post(`${process.env.REACT_APP_URL2}/orders/createorderprocess`, arr) .then((res) => { - console.log(res); - if (res.data.message === 'Successfully created') { + console.log(res) + if (res.data.message === "Successfully created") { // if (orderheaderid && tenantid) { // fetchorderdetails(); // fetchorderaddons(); // fetchorderattires(); // } + enqueueSnackbar('Roles assigned successfully', { - variant: 'success', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, + variant: 'success', anchorOrigin: { vertical: 'top', horizontal: 'right' }, autoHideDuration: 2000 - }); + }) // fetchroleslist(productid, '', '', orderheaderid, arr1[0].shiftid); - // console.log(productid, '', '', orderheaderid, arr1[0].shiftid); + console.log(productid, '', '', orderheaderid, arr1[0].shiftid) + + + // arr1.map((val2) => { // notificationpush(val2,val2.Title); @@ -543,93 +690,173 @@ const Details = () => { fetchorderattires(); fetchassignedcount(); } + + }).catch((err) => { + console.log(err) }) - .catch((err) => { - console.log(err); - }); - console.log(arr); - }; + console.log(arr) + } const notificationpush = async (val) => { - let fcmtoken = val.userfcmtoken; + let fcmtoken = val.userfcmtoken let obj1 = { - notificationid: 0, - notificationdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), - Title: 'Staff Asigned', - message: `A Staff has been assigned to the order ${orderid}`, - configid: 2, - tenantid: tenantid, - orderheaderid: orderheaderid, - orderprocessid: val.orderprocessid, - shiftid: val.shiftid, - userid: val.userid, - orderid: orderid, - sound: 'ring', - click_action: 'FLUTTER_NOTIFICATION_CLICK' + "notificationid": 0, + "notificationdate": dayjs().format('YYYY-MM-DD HH:mm:ss'), + "Title": "Staff Asigned", + "message": `A Staff has been assigned to the order ${orderid}`, + "configid": 2, + "tenantid": tenantid, + "orderheaderid": orderheaderid, + "orderprocessid": val.orderprocessid, + "shiftid": val.shiftid, + "userid": val.userid, + "orderid": orderid, + "sound": "ring", + "click_action": "FLUTTER_NOTIFICATION_CLICK", // "firstname": val.firstname + }; - console.log(obj1, fcmtoken); - await axios - .post( - `${process.env.REACT_APP_URL2}/utils/notification/send`, - { - priority: 'high', - registration_ids: [fcmtoken], - data: obj1, - notification: { - body: `A Staff has been assigned to ${orderid}`, - title: 'Legendary', - sound: 'ring', - content_available: true - } - }, - { - headers: { - Authorization: `Bearer ${process.env.REACT_APP_STAFF_TOKEN}` - } + console.log(obj1, fcmtoken) + await axios.post(`${process.env.REACT_APP_URL2}/utils/notification/send`, + { + "priority": "high", + "registration_ids": [fcmtoken], + "data": obj1, + "notification": { + "body": `A Staff has been assigned to ${orderid}`, + "title": "Legendary", + "sound": "ring", + "content_available": true } - ) + } + , { + headers: { + 'Authorization': `Bearer ${process.env.REACT_APP_STAFF_TOKEN}` + } + } + ) .then((res) => { - console.log(res); + console.log(res) // if(res.data.status){ enqueueSnackbar('Notification sent successfully', { - variant: 'success', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, + variant: 'success', anchorOrigin: { vertical: 'top', horizontal: 'right' }, autoHideDuration: 2000 - }); + }) // } }) .catch((err) => { - console.log(err); - }); - }; + console.log(err) + }) + + } + + const fetchassignedstaffs = async () => { + + // console.log(obj1) + await axios.get(`${process.env.REACT_APP_URL2}/orders/getnotificationusers?orderheaderid=${orderheaderid}`) + .then((res) => { + if (res.data.status) { + let arr1 = []; + let data2; + let tokenarr = [] + console.log(res) + + res.data.details.map((val) => { + arr1.push({ + "notificationid": 0, + "notificationdate": dayjs().format('YYYY-MM-DD HH:mm:ss'), + "Title": "Staff Asigned", + "message": `${val.staffname} has been assigned to the order ${orderid}`, + "configid": 2, + "tenantid": tenantid, + "orderheaderid": orderheaderid, + "orderprocessid": val.orderprocessid, + "shiftid": val.shiftid, + "userid": val.userid, + "orderid": orderid, + "success": 0, + "orderstatus": 'assigned', + "processing": dayjs().format('YYYY-MM-DD HH:mm:ss'), + "notifytype": 1, + "notifyreason": 'Staff Assigned' + // "sound": "ring", + // "click_action": "FLUTTER_NOTIFICATION_CLICK", + // "firstname": val.firstname + + }); + tokenarr.push(val.userfcmtoken); + + }) - const sendgroupnotification = async (obj1) => { - console.log(obj1); - await axios - .post(`${process.env.REACT_APP_URL2}/utils/notification/sendall`, obj1, { - headers: { - Authorization: `Bearer ${process.env.REACT_APP_STAFF_TOKEN}` + data2 = { + "Title": "Staff Asigned", + "message": `A Staff has been assigned to the order ${orderid}`, + "tenantid": tenantid, + "orderheaderid": orderheaderid, + "orderid": orderid, + "configid": 2, + // "click_action": "FLUTTER_NOTIFICATION_CLICK" + } + let fcmmodel = { + "priority": "high", + "registration_ids": tokenarr, + "data": data2, + "notification": { + "body": `An order has been assigned ${orderid}`, + "title": "Legendary", + "sound": "ring", + "content_available": true, + "click_action": "FLUTTER_NOTIFICATION_CLICK" + } + } + let grpnotifyobj = { + "notifications": arr1, + "fcmmodel": fcmmodel + } + console.log("grpnotifyobj") + console.log(grpnotifyobj) + sendgroupnotification(grpnotifyobj) + // notificationpush(obj2, val.userfcmtoken); + // notificationpush(arr1,tokenarr); + // enqueueSnackbar('Notifications sent successfully', { + // variant: 'success', anchorOrigin: { vertical: 'top', horizontal: 'right' }, + // autoHideDuration: 2000 + // }) } }) + .catch((err) => { + console.log(err) + }) + + } + + const sendgroupnotification = async (obj1) => { + + console.log(obj1) + await axios.post(`${process.env.REACT_APP_URL2}/utils/notification/sendall`, obj1, { + headers: { + 'Authorization': `Bearer ${process.env.REACT_APP_STAFF_TOKEN}` + } + } + ) .then((res) => { - console.log(res); + console.log(res) if (res.data.status) { // updateorderstatus(); enqueueSnackbar('Notification sent successfully', { - variant: 'success', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, + variant: 'success', anchorOrigin: { vertical: 'top', horizontal: 'right' }, autoHideDuration: 2000 - }); + }) fetchorderaddons(); } }) .catch((err) => { - console.log(err); - }); - }; + console.log(err) + }) + + } // const updateorderstatus = async () => { @@ -658,15 +885,15 @@ const Details = () => { // } const fetchassignedcount = async () => { + // console.log(obj1) - await axios - .get(`${process.env.REACT_APP_URL2}/orders/getorderstatuscount?orderheaderid=${orderheaderid}`) + await axios.get(`${process.env.REACT_APP_URL2}/orders/getorderstatuscount?orderheaderid=${orderheaderid}`) .then((res) => { if (res.data.status) { // let arr1=[]; - console.log(res); - setAssignedpendingcount(res.data.pendingcount); - fetchorderaddons(); + console.log(res) + setAssignedpendingcount(res.data.pendingcount) + fetchorderaddons() // res.data.details.map((val) => { // let val2={ // "notificationid": 0, @@ -686,24 +913,25 @@ const Details = () => { // }; + // notificationpush(val2,val.userfcmtoken); + // }) } else { - setAssignedpendingcount(res.data.pendingcount); - fetchorderaddons(); + setAssignedpendingcount(res.data.pendingcount) + fetchorderaddons() } }) .catch((err) => { - console.log(err); - }); - }; + console.log(err) + }) + + } function AlertCustomerDelete({ // title, - open, - handleClose - }) { + open, handleClose }) { const [deletepassword, setDeletepassword] = useState(''); return ( @@ -711,63 +939,105 @@ const Details = () => { open={open} onClose={() => handleClose(false)} maxWidth="xs" - fullScreen={isMobile} - PaperProps={{ sx: { borderRadius: { xs: 0, sm: 3 } } }} + PaperProps={{ sx: { borderRadius: 3 } }} > - - - + + + - - - - - {/* - Are you sure you want to cancel this order? - */} - {invoiceeligible && ( - }> - Order is within 24Hrs time frame. The order will be invoiced with standard pricing as agreed. - {/* This is an warning alert. */} - - Terms & Condition link - - - )} - - Please type in the order number to confirm. + + + Cancel Order + + + Confirm to permanently cancel this order - { - console.log(e.target.value); - setDeletepassword(e.target.value); - }} - error={deletepassword !== orderid.slice(4)} - // error={true} - value={deletepassword} - /> + + + + + + {orderid.slice(4)} + - + {(invoiceeligible) && + }> + Order is within 24Hrs time frame. The order will be invoiced with standard pricing as agreed. + Terms & Condition link + + } + + + Please type in the order number to confirm. + + setDeletepassword(e.target.value)} + error={deletepassword !== orderid.slice(4)} + value={deletepassword} + placeholder={orderid.slice(4)} + /> + + + - @@ -781,68 +1051,120 @@ const Details = () => { - - - - - Assign Roles + // fullScreen + TransitionComponent={PopupTransition}> - + + + + + + + + + Assign Roles + + + {clientname} · {currentrole} + + - + + {orderid} + - + + - {/* */} - setTabstatus((e) => (e === 0 ? 1 : 0))} - variant="scrollable" - scrollButtons="auto" + setTabstatus((e) => (e === 0 ? 1 : 0))} + sx={{ + display: 'inline-flex', + alignItems: 'center', + gap: 0.75, + px: 1.25, + py: 0.625, + borderRadius: 999, + cursor: 'pointer', + bgcolor: BRAND, + color: '#fff', + fontWeight: 800, + fontSize: 12, + boxShadow: `0 6px 18px ${ring(BRAND)}` + }} > - {/* */} - - + + + + {currentrole || 'Role'} + - - - - + + {[ + { label: 'Required', value: currentshiftobj.shifts, color: BRAND }, + { label: 'Assigned', value: currentshiftobj.assigned, color: '#10b981' }, + { label: 'Remaining', value: currentshiftobj.remaining, color: '#ef4444' } + ].map((c) => ( + + {c.label} + + {c.value ?? 0} + + + ))} @@ -853,10 +1175,10 @@ const Details = () => { - + setTabstatus((e) => (e === 0) ? 1 : 0)} variant="scrollable" scrollButtons="auto" > @@ -876,130 +1198,27 @@ const Details = () => { */} + {/* */} {/* */} - {stafflist.length === 0 ? ( + {(stafflist.length === 0) ? <> - {loading ? ( + {(loading) ? <> + + + - ) : ( + : No Staffs Available - )} + + } - ) : isMobile ? ( - - {stafflist.map((val, i) => { - const isSelected = staffarr.find((res) => res.userid == val.userid) ? true : false; - return ( - - - - - - {val.firstname} - - {val.contactno} - - - - {val.orderdetailid !== orderdetailid ? ( - { - console.log(currentshiftobj); - if (currentshiftobj.remaining >= 0) { - if (e.target.checked && currentshiftobj.remaining != 0) { - let arr = staffarr; - arr.push({ - userid: val.userid, - orderdetailid, - productid, - shiftid: currentshiftobj.shiftid, - userrate: currentshiftobj.price, - productrate: val.rolecost, - firstname: val.firstname - }); - setStaffarr([...arr]); - let obj = currentshiftobj; - obj.assigned++; - obj.remaining = obj.shifts - obj.assigned; - setCurrentshiftobj({ ...obj }); - } else if ( - currentshiftobj.assigned != currentshiftobj.shifts || - (currentshiftobj.remaining === 0 && !e.target.checked) - ) { - let arr = staffarr; - let index = arr.findIndex((val1) => val1.userid === val.userid); - arr.splice(index, 1); - setStaffarr([...arr]); - let obj = currentshiftobj; - obj.assigned--; - obj.remaining = obj.shifts - obj.assigned; - setCurrentshiftobj({ ...obj }); - } - console.log(staffarr); - } - }} - /> - ) : ( - - - - { - console.log(val); - unassign(val); - }} - > - - - - - { - console.log(val); - notificationpush(val); - }} - > - - - - - )} - - - - - - {val.cateoryname} - - - - - - - - - - - {val.orderid && ( - - - - )} - - - ); - })} - - ) : ( + : + @@ -1019,238 +1238,223 @@ const Details = () => { City Action + {stafflist.map((val, i) => { - return ( - - res.userid == val.userid) ? '#f5f5f5' : '', - ':hover': { - backgroundColor: staffarr.find((res) => res.userid == val.userid) ? '#f5f5f5 !important' : '' - } - }} - > - - {i + 1} - - - - - {/* {row.tenantname.charAt(0)} */} - - - {val.firstname} - - {val.contactno} - - + + return + + + res.userid == val.userid)) ? '#f5f5f5' : '', ':hover': { + backgroundColor: (staffarr.find((res) => res.userid == val.userid)) ? '#f5f5f5 !important' : '' + } + }}> + + {i + 1} + + + + + {/* {row.tenantname.charAt(0)} */} + + + {val.firstname} + + {val.contactno} + - - - {/* + + + + {/* {val.contactno} some demo address */} - - - {val.cateoryname} - - - {/* {row.category} */} - - - {val.rolecost} - {/* */} - {/* {(row.gender === 1) && } */} - {/* {row.gender === 0 && } */} - {/* */} - {/* */} - {/* B+ */} - {/* {row.devicetype} */} - {val.experience} Years - - - - {val.city} - - {val.orderdetailid !== orderdetailid ? ( - <> - res.userid == val.userid) ? true : false} - onClick={(e) => { - console.log(currentshiftobj); - if (currentshiftobj.remaining >= 0) { - if (e.target.checked && currentshiftobj.remaining != 0) { - let arr = staffarr; - arr.push({ - userid: val.userid, - orderdetailid, - productid, - shiftid: currentshiftobj.shiftid, - userrate: currentshiftobj.price, - productrate: val.rolecost, - firstname: val.firstname - }); - setStaffarr([...arr]); - let obj = currentshiftobj; - // obj.shifts--; - // obj.assigned = arr.length; - obj.assigned++; - obj.remaining = obj.shifts - obj.assigned; - setCurrentshiftobj({ ...obj }); - } else if ( - currentshiftobj.assigned != currentshiftobj.shifts || - (currentshiftobj.remaining === 0 && !e.target.checked) - ) { - let arr = staffarr; - // let index = arr.indexOf(val.userid) - let index = arr.findIndex((val1) => val1.userid === val.userid); - arr.splice(index, 1); - setStaffarr([...arr]); - let obj = currentshiftobj; - // obj.shifts++; - // obj.assigned = arr.length; - obj.assigned--; - obj.remaining = obj.shifts - obj.assigned; - setCurrentshiftobj({ ...obj }); - } - // forceUpdate(); - console.log(staffarr); + + {val.cateoryname} + + {/* {row.category} */} + + + + {val.rolecost} + {/* */} + {/* {(row.gender === 1) && } */} + {/* {row.gender === 0 && } */} + {/* */} + {/* */} + {/* B+ */} + {/* {row.devicetype} */} + {val.experience} Years + + {val.city} + + {(val.orderdetailid !== orderdetailid) ? + <> + res.userid == val.userid)) ? true : false} + onClick={(e) => { + console.log(currentshiftobj) + if (currentshiftobj.remaining >= 0) { + + + if (e.target.checked && currentshiftobj.remaining != 0) { + let arr = staffarr; + arr.push({ + userid: val.userid, + orderdetailid, + productid, + shiftid: currentshiftobj.shiftid, + userrate: currentshiftobj.price, + productrate: val.rolecost, + firstname: val.firstname + }); + setStaffarr([...arr]) + let obj = currentshiftobj; + // obj.shifts--; + // obj.assigned = arr.length; + obj.assigned++; + obj.remaining = obj.shifts - obj.assigned; + setCurrentshiftobj({ ...obj }) + } else if (currentshiftobj.assigned != currentshiftobj.shifts || (currentshiftobj.remaining === 0 && (!e.target.checked))) { + let arr = staffarr; + // let index = arr.indexOf(val.userid) + let index = arr.findIndex((val1) => val1.userid === val.userid) + arr.splice(index, 1); + setStaffarr([...arr]); + let obj = currentshiftobj; + // obj.shifts++; + // obj.assigned = arr.length; + obj.assigned--; + obj.remaining = obj.shifts - obj.assigned; + setCurrentshiftobj({ ...obj }) } - }} - /> - - ) : ( - <> - + // forceUpdate(); + console.log(staffarr); + } + }} /> - {/* + : + + <> + + + {/* */} - - { - console.log(val); - unassign(val); - // sendunassignnotification(val) - }} - > - {/* */} - - - - - { - console.log(val); - // unassign(val) - notificationpush(val); - }} - > - {/* */} - - - - - )} - - - {val.orderid && ( - <> - - - )} - - - - ); - })} + + { + console.log(val) + unassign(val) + // sendunassignnotification(val) + }}> + {/* */} + + + + + { + console.log(val) + // unassign(val) + notificationpush(val) + }}> + {/* */} + + + + + } + + + + + {(val.orderid) && + + <> + + + + } + + + + + }) + }
    - )} + }
    - - {stafflist.length > 0 && ( + + {(stafflist.length > 0) && <> - - OK - - { - // dialogclose(); - setStaffarr([]); - let obj = currentshiftobj; - // obj.shifts = row.orderqty; - obj.remaining = obj.shifts; - obj.assigned = 0; + OK + { - setCurrentshiftobj(obj); - }} - > - clear - + // dialogclose(); + setStaffarr([]); + let obj = currentshiftobj; + // obj.shifts = row.orderqty; + obj.remaining = obj.shifts; + obj.assigned = 0; + + setCurrentshiftobj(obj); + }}>clear - )} - { - dialogclose(); - }} - > - Close - + } + { dialogclose() }}>Close {/*
    */} {/* */} - + - + + + {/* - + */} @@ -1259,184 +1463,201 @@ const Details = () => { Details */} - - - - - history.back()} - // onClick={()=>} + + + history.back()} + sx={{ + bgcolor: '#fff', + border: `1px solid ${DT.borderSubtle}`, + borderRadius: 999, + color: DT.textPrimary, + '&:hover': { bgcolor: tint(BRAND), borderColor: edge(BRAND), color: BRAND } + }} + > + + + + + + + - - - {/* Test me */} - - Details - - {/* */} - : orderid} - variant="combined" - color="warning" - size="small" - /> - {/* Date */} - {/* {orderdate} */} - : orderdate} - variant="combined" - color="primary" - size="small" - /> - - {orderstatus === 'pending' && } - {orderstatus === 'cancelled' && } - {orderstatus === 'completed' && } - {orderstatus === 'processing' && } - {orderstatus === 'assigned' && } - {orderstatus === 'confirmed' && } - - {orderstatus === 'active' && } - {orderstatus === 'closed' && } - - {orderstatus === 'modified' && } - + Order Details + + + + + {orderid === '' ? : orderid} + + + + {orderdate === '' ? : orderdate} + + - - - {/* {dayjs(startdate).$d.toString()} */} - {/* {startdate} */} - {/* {dayjs().$d.toString()} */} - - {(orderstatus === 'pending' || orderstatus === 'assigned' || orderstatus === 'confirmed' || orderstatus === 'modified') && ( - // && (dayjs(startdate).$d > dayjs().$d) - - } - onClick={(e) => { - e.stopPropagation(); - // if (dayjs(startdate).$d > dayjs().$d) { - if (dayjs(dayjs().format('MM-DD-YYYY')).isBefore(dayjs(dayjs(startdate).format('MM-DD-YYYY')))) { - navigate(`/editorder`, { - state: { - orderheaderid: orderheaderid, - tenantid: tenantid - } - }); - } else { - enqueueSnackbar( - 'Order cannot be edited.\n Order date is not valid at this time', - - { - variant: 'error', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, - autoHideDuration: 4000, - style: { whiteSpace: 'pre-line' } - } - ); - } - }} - > - Edit Order - - - )} - - {/* {(((orderstatus === 'pending') - || (orderstatus === 'modified')) - && assignedpendingcount === 0) && - <> - } - onClick={() => { - fetchassignedstaffs(); - }} - > - Notify Staff - - - } */} - {orderstatus !== 'cancelled' && orderstatus !== '' && orderstatus !== 'completed' && orderstatus !== 'closed' && ( - <> - { - console.log(dayjs(startdate).diff(dayjs(), 'm') / 60); - if (dayjs(startdate).diff(dayjs(), 'm') / 60 > 24) { - setInvoiceeligible(false); - setOpen(true); - } else { - setInvoiceeligible(true); - setOpen(true); - } - }} - sx={{ borderRadius: '40px', mt: { xs: 2, sm: 0 }, width: { xs: '100%', sm: 'auto' } }} - startIcon={} - > - Cancel Order - - - )} - {orderstatus === 'cancelled' && ( - <> - - - )} - {/* {(orderstatus === 'completed') && - - } */} - - - + + + {((orderstatus === 'pending') || + (orderstatus === 'assigned') || + (orderstatus === 'confirmed') || + (orderstatus === 'modified')) && ( + + } + onClick={(e) => { + e.stopPropagation(); + if (dayjs(dayjs().format('MM-DD-YYYY')).isBefore(dayjs(dayjs(startdate).format('MM-DD-YYYY')))) { + navigate(`/editorder`, { + state: { + orderheaderid: orderheaderid, + tenantid: tenantid + } + }); + } else { + enqueueSnackbar('Order cannot be edited.\n Order date is not valid at this time', { + variant: 'error', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 4000, + style: { whiteSpace: 'pre-line' } + }); + } + }} + > + Edit Order + + + )} + + {orderstatus !== 'cancelled' && orderstatus !== '' && orderstatus !== 'completed' && orderstatus !== 'closed' && ( + { + if ((dayjs(startdate).diff(dayjs(), 'm') / 60) > 24) { + setInvoiceeligible(false); + setOpen(true); + } else { + setInvoiceeligible(true); + setOpen(true); + } + }} + startIcon={} + > + Cancel Order + + )} + + {orderstatus === 'cancelled' && ( + + Cancelled on {cancelleddate} + + )} + + + + {/* Dialog window */} + - + {/*

    {(dayjs().isBefore(dayjs(startdate)))?'true':'false'}

    */} {/* */} @@ -1468,9 +1689,7 @@ const Details = () => { Client - - {clientname === '' ? : clientname} - + {(clientname === '') ? : clientname} {/* {eventlocation.map((val, i) => { return {val} @@ -1481,9 +1700,7 @@ const Details = () => { fafdf dafaf afdafafd */} - - {tenantaddress === '' ? : tenantaddress} - + {(tenantaddress === '') ? : tenantaddress} @@ -1494,12 +1711,10 @@ const Details = () => { Event - - {eventname === '' ? : eventname} - + {(eventname === '') ? : eventname} {/* {(eventlocation === '') ? : eventlocation} - + : venuetype} color="primary" variant="light" size="small" sx={{ width: 'max-content' }} /> */} @@ -1507,331 +1722,160 @@ const Details = () => {
    - {categoryarr.map((val5, j) => { - return ( - - - - - - Shift {j + 1} - - {''} - {val5.locationaddress} - - {val5.shiftstatus === 1 && } - - - {val5.ordercontacts.map((val11) => { - return ( - <> - - - {val11.contactname.charAt(0).toUpperCase()} - - - - ); - })} - + {categoryarr.map((val5, j) => { + + + + + + return < Fragment key={val5.locationaddress}> + + + + + + Shift {j + 1} + + + {''}{val5.locationaddress} + + {(val5.shiftstatus === 1) && + + } - {isMobile ? ( - - {val5.orderdetails.length === 0 && ( - - - - - - )} - {val5.orderdetails.map((row, i) => ( - - - - #{i + 1} - {row.productname} - - - - { - setStafflist([]); - setExpandopen(expandopen[0] === j && expandopen[1] === i ? ['', ''] : [j, i]); - fetchstafflist(row.orderdetailid); - }} - > - {expandopen[0] === j && expandopen[1] === i ? : } - - - {orderstatus === 'cancelled' && ( - - - - )} - {row.status === 1 && ( - - - - - - )} - {row.supplyqty > row.orderqty && ( - - - - - - )} - - - - - - {dayjs(row.starttime).format('MM/DD/YYYY')} - {dayjs(row.starttime).format('hh:mm A')} - - - - - {dayjs(row.endtime).format('MM/DD/YYYY')} - {dayjs(row.endtime).format('hh:mm A')} - - - - - - - - - - - - - - - {stafflist.length === 0 ? ( - loading ? ( - - - - ) : ( - - No Staffs has been Assigned - - ) - ) : ( - - {stafflist.map((val, si) => ( - - - - {val.staffname} - - - - - - {val.orderstatus === 'pending' && } - {val.orderstatus === 'cancelled' && ( - - )} - {val.orderstatus === 'completed' && ( - - )} - {val.orderstatus === 'processing' && ( - - )} - {val.orderstatus === 'assigned' && } - {val.orderstatus === 'confirmed' && ( - - )} - {val.orderstatus === 'active' && } - {val.orderstatus === 'closed' && } - - - - - - {dayjs(val.Starttime).format('MM/DD/YYYY')} - {dayjs(val.Starttime).format('hh:mm A')} - - - - - {dayjs(val.Endtime).format('MM/DD/YYYY')} - {dayjs(val.Endtime).format('hh:mm A')} - - - - - - - - - - - - - - - - - - - ))} - - )} - - - ))} - - ) : ( - - - - - # - Role - Start Date - End Date - Unpaid break - Count - Assigned - Attire - Price - {/* Tax */} - Amount - Action - - - - {val5.orderdetails.length === 0 && ( - <> - - - - - - - - - - - - - - - - - - {/* */} - - - - - - - - - - - - - - - - - - - - {/* + + {val5.ordercontacts.map((val11) => { + + return <> + + {val11.contactname.charAt(0).toUpperCase()} + + + }) + + } + + + + + +
    + + + # + Role + Start Date + End Date + Unpaid break + Count + Assigned + Attire + Price + {/* Tax */} + Amount + Action + + + + {(val5.orderdetails.length === 0) && + <> + + + + + + + + + + + + + + + + + + + + + + {/* */} + + + + + + + + + + + + + + + + + + + + + + + + + + {/* */} - - {/* */} - - )} - {/* */} - {val5.orderdetails.map((row, i) => ( - <> - - {i + 1} - {row.productname} - {/* {row.productname} + + {/* */} + + } + + {/* */} + {val5.orderdetails.map((row, i) => ( + <> + + {i + 1} + {row.productname} + {/* {row.productname} */} - - - {dayjs(row.starttime).format('MM/DD/YYYY')} - {dayjs(row.starttime).format('hh:mm A')} - - - - {' '} - - {dayjs(row.endtime).format('MM/DD/YYYY')} - {dayjs(row.endtime).format('hh:mm A')} - - - {row.unpaidbreak || 0} - - - - - - - - {/* < Grid container spacing={1}> + + + {dayjs(row.starttime).format('MM/DD/YYYY')} + {dayjs(row.starttime).format('hh:mm A')} + + + + {' '} + + {dayjs(row.endtime).format('MM/DD/YYYY')} + {dayjs(row.endtime).format('hh:mm A')} + + + {row.unpaidbreak || 0} + + + + + + + + + + {/* < Grid container spacing={1}> {(row.orderattires || []).map((val) => { return @@ -1843,30 +1887,42 @@ const Details = () => { } */} - - ${row.price} - {/* {row.taxamount} */} - ${row.landingamount} - - - - { - setStafflist([]); - setExpandopen(expandopen[0] === j && expandopen[1] === i ? ['', ''] : [j, i]); - // expanddatafetch(row.orderheaderid); + + ${row.price} + {/* {row.taxamount} */} + ${row.landingamount} + + + - // fetchroleslist(row.productid, '', '', val5.orderheaderid, row.shiftid); - fetchstafflist(row.orderdetailid); - }} - > - {expandopen[0] === j && expandopen[1] === i ? : } - - + - {/* {(orderstatus !== 'cancelled') && + { + setStafflist([]); + setExpandopen(((expandopen[0] === j) && (expandopen[1] === i)) ? ['', ''] : [j, i]) + // expanddatafetch(row.orderheaderid); + + // fetchroleslist(row.productid, '', '', val5.orderheaderid, row.shiftid); + fetchstafflist(row.orderdetailid) + + + } + } + > + {((expandopen[0] === j) && (expandopen[1] === i)) ? + + : + } + + + + + + {/* {(orderstatus !== 'cancelled') && <> { @@ -1907,102 +1963,111 @@ const Details = () => { setDialogopen(true); }} > - + } */} - {orderstatus === 'cancelled' && ( + {(orderstatus === 'cancelled') && + <> + + + } + + {(row.status === 1) && + + + + + + } + {(row.supplyqty > row.orderqty) && + + + + + + } + + + + + + + + + + + + {/* */} + + + {(stafflist.length === 0) ? <> - - - + {(loading) ? + <> + + + + + + + + : + <> + + No Staffs has been Assigned + + + } - )} + : - {row.status === 1 && ( - - - - - - )} - {row.supplyqty > row.orderqty && ( - - - - - - )} - - - - - - - {/* */} + +
    + + + # + Staff + Start Time + End Time + Pay Rate - - {stafflist.length === 0 ? ( - <> - {loading ? ( - <> - - - - - ) : ( - <> - - No Staffs has been Assigned - - - )} - - ) : ( - -
    - - - # - Staff - Start Time - End Time - Pay Rate + {/* Category */} + Clockin + Clockout - {/* Category */} - Clockin - Clockout + Hours Worked - Hours Worked - {/* Experience */} - {/* Level */} - {/* City */} - Status - - - - {stafflist.map((val, i) => { - return ( - - res.userid == val.userid)) ? '#f5f5f5' : '', ':hover': { - // backgroundColor: (staffarr.find((res) => res.userid == val.userid)) ? '#f5f5f5 !important' : '' - // } - } - } - > - - {i + 1} - - {/* + + {/* Experience */} + + {/* Level */} + {/* City */} + Status + + + + + {stafflist.map((val, i) => { + + return + + + res.userid == val.userid)) ? '#f5f5f5' : '', ':hover': { + // backgroundColor: (staffarr.find((res) => res.userid == val.userid)) ? '#f5f5f5 !important' : '' + // } + + }}> + + {i + 1} + + {/* { */} - - - {val.staffname} - - - - - - - - - {dayjs(val.Starttime).format('MM/DD/YYYY')} - - - {dayjs(val.Starttime).format('hh:mm A')} - - - - - - - {dayjs(val.Endtime).format('MM/DD/YYYY')} - - - {dayjs(val.Endtime).format('hh:mm A')} - - - - {val.rolecost} + + + + {val.staffname} + + + + + + + + + {dayjs(val.Starttime).format('MM/DD/YYYY')} + {dayjs(val.Starttime).format('hh:mm A')} + + + + + {dayjs(val.Endtime).format('MM/DD/YYYY')} + {dayjs(val.Endtime).format('hh:mm A')} + + + {val.rolecost} - {/* + {/* {val.cateoryname} @@ -2060,50 +2119,38 @@ const Details = () => { */} - - - {/* Clock In: */} - {/* Age */} - - - - - - - {/* Clock In: */} - {/* Age */} - - - - - {val.hoursworked} + + + {/* Clock In: */} + {/* Age */} + + - {/* + + + + + {/* Clock In: */} + {/* Age */} + + + + + + {val.hoursworked} + + + {/* + - + @@ -2130,82 +2177,98 @@ const Details = () => { */} - - - {val.orderstatus === 'pending' && ( - - )} - {val.orderstatus === 'cancelled' && ( - - )} - {val.orderstatus === 'completed' && ( - - )} - {val.orderstatus === 'processing' && ( - - )} - {val.orderstatus === 'assigned' && ( - - )} - {val.orderstatus === 'confirmed' && ( - - )} - {val.orderstatus === 'active' && ( - - )} - {val.orderstatus === 'closed' && ( - - )} - - - - - ); - })} - -
    -
    - )} -
    - - - - - ))} - - - - )} -
    -
    -
    - ); - })} + + + + + {(val.orderstatus === 'pending') && + + + } + {(val.orderstatus === 'cancelled') && + + + + } + {(val.orderstatus === 'completed') && + + + } + {(val.orderstatus === 'processing') && + + } + {(val.orderstatus === 'assigned') && + + } + {(val.orderstatus === 'confirmed') && + + } + + {(val.orderstatus === 'active') && + + } + {(val.orderstatus === 'closed') && + + } + + + + + + + }) + } + + + + } + + + + + + + + + + + + ))} + + + + + + + +
    + + }) + } - - + {/* */} {/* Order Addons */} - {(orderaddons || []).map((val) => { - return ( - + { + + + (orderaddons || []).map((val) => { + return - } variant="outlined" color="error" label={val.addon} /> + } variant='outlined' color="error" label={val.addon} /> - ); - })} + }) + } {/* } variant='outlined' color="error" label="Parking Provided" /> @@ -2228,11 +2291,11 @@ const Details = () => { {/* */} {/* */} - - + + Sub Total: - ${subtotal === '' ? : subtotal} + ${(subtotal === '') ? : subtotal} {/* Discount: @@ -2242,26 +2305,25 @@ const Details = () => { */} Tax: - {taxamount === '' ? : taxamount} + {(taxamount === '') ? : taxamount} Grand Total: - - {grandtotal === '' ? : `$${grandtotal}`} - + {(grandtotal === '') ? : `$${grandtotal}`} {/* */} - - + - Other Instructions: - {otherinstructions} + Other Instructions: + + + {otherinstructions} + {/* @@ -2269,11 +2331,12 @@ const Details = () => { */} - + {/* */} {/* */} - + + ); }; diff --git a/src/pages/nearle/orders/map.js b/src/pages/nearle/orders/map.js new file mode 100644 index 0000000..4a62f0a --- /dev/null +++ b/src/pages/nearle/orders/map.js @@ -0,0 +1,157 @@ +/* eslint-disable no-unused-vars */ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import TextField from '@mui/material/TextField'; +import Autocomplete from '@mui/material/Autocomplete'; +import LocationOnIcon from '@mui/icons-material/LocationOn'; +import Grid from '@mui/material/Grid'; +import Typography from '@mui/material/Typography'; +import parse from 'autosuggest-highlight/parse'; +import { debounce } from '@mui/material/utils'; + +// This key was created specifically for the demo in mui.com. +// You need to create a new one for your application. +const GOOGLE_MAPS_API_KEY ='AIzaSyCF4KatYCI3vqz1_H3kiHeyS3yCMfYToh8'; + +function loadScript(src, position, id) { + if (!position) { + return; + } + + const script = document.createElement('script'); + script.setAttribute('async', ''); + script.setAttribute('id', id); + script.src = src; + position.appendChild(script); +} + +const autocompleteService = { current: null }; + +export default function GoogleMaps() { + const [value, setValue] = React.useState(null); + const [inputValue, setInputValue] = React.useState(''); + const [options, setOptions] = React.useState([]); + const loaded = React.useRef(false); + + if (typeof window !== 'undefined' && !loaded.current) { + if (!document.querySelector('#google-maps')) { + loadScript( + `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API_KEY}&libraries=places`, + document.querySelector('head'), + 'google-maps', + ); + } + + loaded.current = true; + } + + const fetch = React.useMemo( + () => + debounce((request, callback) => { + autocompleteService.current.getPlacePredictions(request, callback); + }, 400), + [], + ); + + React.useEffect(() => { + let active = true; + + if (!autocompleteService.current && window.google) { + autocompleteService.current = + new window.google.maps.places.AutocompleteService(); + } + if (!autocompleteService.current) { + return undefined; + } + + if (inputValue === '') { + setOptions(value ? [value] : []); + return undefined; + } + + fetch({ input: inputValue }, (results) => { + if (active) { + let newOptions = []; + + if (value) { + newOptions = [value]; + } + + if (results) { + newOptions = [...newOptions, ...results]; + } + + setOptions(newOptions); + } + }); + + return () => { + active = false; + }; + }, [value, inputValue, fetch]); + + return ( + + typeof option === 'string' ? option : option.description + } + filterOptions={(x) => x} + options={options} + autoComplete + includeInputInList + filterSelectedOptions + value={value} + noOptionsText="No locations" + onChange={(event, newValue) => { + setOptions(newValue ? [newValue, ...options] : options); + setValue(newValue); + }} + onInputChange={(event, newInputValue) => { + setInputValue(newInputValue); + }} + renderInput={(params) => ( + + )} + renderOption={(props, option) => { + const matches = + option.structured_formatting.main_text_matched_substrings || []; + + const parts = parse( + option.structured_formatting.main_text, + matches.map((match) => [match.offset, match.offset + match.length]), + ); + + return ( +
  • + + + + + + {parts.map((part, index) => ( + + {part.text} + + ))} + + {option.structured_formatting.secondary_text} + + + +
  • + ); + }} + /> + ); +} diff --git a/src/pages/nearle/orders/miltiUploadBackup.js b/src/pages/nearle/orders/miltiUploadBackup.js new file mode 100644 index 0000000..490794f --- /dev/null +++ b/src/pages/nearle/orders/miltiUploadBackup.js @@ -0,0 +1,1610 @@ +/* eslint-disable no-unused-vars */ +import React from 'react'; +import Loader from 'components/Loader'; +import { useEffect, useState, Fragment, useRef } from 'react'; +import { useTheme } from '@mui/material/styles'; +import MainCard from 'components/MainCard'; +import axios from 'axios'; +import ClearIcon from '@mui/icons-material/Clear'; +import { SearchOutlined, CloseOutlined, ExclamationCircleOutlined, FileAddOutlined } from '@ant-design/icons'; +import { Empty } from 'antd'; +import MyLocationIcon from '@mui/icons-material/MyLocation'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import dayjs from 'dayjs'; +var utc = require('dayjs/plugin/utc'); +dayjs.extend(utc); +import { enqueueSnackbar } from 'notistack'; +import { useNavigate } from 'react-router'; +import Papa from 'papaparse'; +import { calculateDrivingDistance, calculateTotalCharge } from '../../../utils/distance'; +import * as XLSX from 'xlsx'; + +import { + FormControl, + InputAdornment, + Grid, + Typography, + Stack, + Box, + Button, + TextField, + Autocomplete, + Chip, + Divider, + Dialog, + DialogTitle, + DialogContent, + Checkbox, + DialogActions, + CircularProgress, + IconButton, + OutlinedInput, + FormGroup, + FormControlLabel, + Table, + TableContainer, + TableCell, + TableBody, + TableRow, + Paper, + TableHead, + FormLabel, + RadioGroup, + Radio, + Backdrop, + List, + ListItem, + ListItemText +} from '@mui/material'; +import CircularLoader from 'components/nearle_components/CircularLoader'; + +const MultipleOrders = () => { + const navigate = useNavigate(); + const theme = useTheme(); + const locationRef = useRef(null); + const tenantRef = useRef(null); + const userid = localStorage.getItem('userid'); + const [locations, setLocations] = useState([]); + const [tenantlist, setTenantlist] = useState([]); + const [loading, setLoading] = useState(false); + const [btnLoading, setBtnLoading] = useState(false); + const [appId, setAppId] = useState(0); + const [tenantLocations, setTenantlocations] = useState([]); + // const [tenantid, setTenantid] = useState(0); + const tenantid = localStorage.getItem('tenantid') || 0; + const [locationid, setLocationid] = useState(0); + const [basePrice, setBasePrice] = useState(0); + const [pricePerKm, setPricePerKm] = useState(0); + const [minKm, setMinKm] = useState(0); + const [pickCust, setPickCust] = useState(null); + const [dropCust, setDropCust] = useState([]); + const [isCustomerOpen, setIsCustomerOpen] = useState(false); + const [searchCustList, setSearchCustList] = useState(''); + const [customerlist, setCustomerlist] = useState([]); + const [startdate, setStartdate] = useState(dayjs().format('MM-DD-YYYY')); + const [timeslotarr, setTimeslotarr] = useState([]); + const [starttime, setStatrttime] = useState(); + const [endtime, setEndtime] = useState(); + const [selectedtime, setSelectedtime] = useState(''); + const [alertmessage, setAlertmessage] = useState(''); + const [otherinstructions, setOtherinstructions] = useState(''); + const [admintoken, setAdmintoken] = useState(); + const [totaldist, settotaldist] = useState(0); + const [totalAmt, settotalAmt] = useState(0); + const [totalQty, settotalQty] = useState(0); + const [totalCash, settotalCash] = useState(0); + const [users, setUsers] = useState([]); + const [uploadType, setUploadType] = useState(null); + const [tenantValue, setTenantValue] = useState(null); + const [locationValue, setLocationValue] = useState(null); + const [pickupSlotsList, setPickupSlotsList] = useState(null); + const [pickupSlot, setPickupSlot] = useState(null); + + useEffect(() => { + if (timeslotarr[0]) { + let arr = []; + timeslotarr.map((val) => { + if (dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0) { + arr.push(val); + } + }); + } + }, [timeslotarr]); + + // =============================================== || opentoast || =============================================== + const opentoast = (message, variant, time) => { + enqueueSnackbar(message, { + variant: variant, + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: time ? time : 1500 + }); + console.log(alertmessage); + }; + + // 🔹 Smart toast wrapper — prevents duplicate toasts for same message within 3 seconds + let toastCache = {}; + const OpenToast = (message, type = 'info', timeout = 10000) => { + const key = `${type}-${message}`; + if (toastCache[key]) return; // skip duplicates + opentoast(message, type, timeout); // your existing toast/snackbar + toastCache[key] = true; + setTimeout(() => delete toastCache[key], 3000); // reset after delay + }; + + // ==============================|| fetchAppLocations ||============================== // + + const fetchAppLocations = async () => { + setLoading(true); + + try { + const locationRes = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`); + console.log('fetchAppLocations', locationRes.data.details); + setLocations(locationRes.data.details); + } catch (err) { + console.log('locationRes', err); + OpenToast(err.message, 'error', 5000); + } finally { + setLoading(false); + } + }; + useEffect(() => { + fetchAppLocations(); + }, []); + + // ===================================================== || fetchtenantinfolist || ===================================================== + + const fetchtenantinfolist = async () => { + setLoading(true); + await axios + .get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${appId}&status=active`) + + .then((res) => { + console.log(res); + if (res.data.status) { + let arr = []; + res.data.details.map((val) => { + arr.push({ + ...val, + label: `${val.tenantname}` + }); + }); + setTenantlist(arr); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + useEffect(() => { + appId && fetchtenantinfolist(); + }, [appId]); + // ============================================= || fetchTenantPricing || ============================================= + + const fetchTenantPricing = async (id) => { + try { + const pricingResponse = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantpricing/?tenantid=${id}`); + console.log('pricingResponse', pricingResponse.data.details); + setBasePrice(pricingResponse.data.details.baseprice); + setPricePerKm(pricingResponse.data.details.priceperkm); + setMinKm(pricingResponse.data.details.minkm); + } catch (error) { + console.log('fetchTenantPricing error', error); + } + }; + // ============================================= || gettenantlocations (branches) || ============================================= + const gettenantlocations = async (id) => { + try { + const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${id}`); + console.log('gettenantlocations', res.data.details); + if (res.data.details.length == 1) { + setTenantlocations(res.data.details); + setPickCust(res.data.details[0]); + setLocationid(res.data.details[0].locationid); + setLocationValue(res.data.details[0].locationid); + setPickupSlotsList(res.data.details[0].slots); + } else { + setTenantlocations(res.data.details); + } + } catch (err) { + console.log('gettenantlocations', err); + } + }; + useEffect(() => { + gettenantlocations(tenantid); + }, [tenantid]); + // ========================================================= || clientdetails || ========================================================= + const clientdetails = async () => { + try { + let url = + searchCustList == '' + ? `${process.env.REACT_APP_URL}/customers/gettenantcustomers/?tenantid=${tenantid}&pageno=1&pagesize=30` + : `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tenantid}&keyword=${searchCustList}`; + await axios + .get(url) + .then((res) => { + if (res.data.status) { + console.log('clientdetails', res.data.details); + + setCustomerlist(res.data.details); + let arr = []; + res.data.details.map((val) => { + arr.push({ + label: `${val.firstname} | ${val.contactno}`, + ...val + }); + }); + } + }) + .catch((err) => { + console.log(err); + opentoast('server error', 'warning'); + }); + } catch (err) { + console.log(err); + } + }; + useEffect(() => { + if (tenantid) { + clientdetails(); + } + }, [searchCustList.length > 3, searchCustList == '', tenantid]); + + // ========================================================= || calculateTotal(dist , charge) || ========================================================= + const calculateTotal = () => { + let a1 = 0; + let a2 = 0; + let a3 = 0; + let a4 = 0; + dropCust.map((customer) => { + a1 += customer.distance; + a2 += customer.totalcharge; + a3 += customer.quantity; + a4 += customer.collectionamt; + }); + settotaldist(a1); + settotalAmt(a2); + settotalQty(a3); + settotalCash(a4); + }; + useEffect(() => { + calculateTotal(); + }, [dropCust]); + + // ========================================================= || handleCheckboxChange || ========================================================= + const handleCheckboxChange = async (event, customer) => { + setLoading(true); + if (event.target.checked) { + // If the checkbox is checked, calculate the distance and add the customer + try { + const obj = await calculateDistance(customer); + const { roundedDistance, totalcharge } = obj; + // Create a new customer object with the distance property + const updatedCustomer = { + ...customer, + distance: roundedDistance, + totalcharge: totalcharge + }; + + // Add the updated customer object to dropCust + setDropCust((prevDropCust) => [...prevDropCust, updatedCustomer]); + + // Log the rounded distance + // console.log(`Rounded Distance: ${roundedDistance} km`); + } catch (error) { + console.error('Failed to calculate distance:', error); + } finally { + setLoading(false); + } + } else { + // If the checkbox is unchecked, remove the customer from dropCust + setDropCust((prevDropCust) => { + return prevDropCust.filter((cust) => cust.customerid !== customer.customerid); + }); + setLoading(false); + } + }; + // ========================================================= || handleCheckboxChange1 || ========================================================= + // const handleCheckboxChange1 = async (customer) => { + // console.log('customer', customer); + // setLoading(true); + // try { + // const obj = await calculateDistance(customer); + // const { roundedDistance, totalcharge } = obj; + // // Create a new customer object with the distance property + // const updatedCustomer = { + // ...customer, + // distance: roundedDistance, + // totalcharge: totalcharge + // }; + + // // Add the updated customer object to dropCust + // setDropCust((prevDropCust) => [...prevDropCust, updatedCustomer]); + + // // Log the rounded distance + // console.log(`Rounded Distance: ${roundedDistance} km`); + // setLoading(false); + // } catch (error) { + // console.error('Failed to calculate distance:', error); + // } + // }; + const handleCheckboxChange1 = async (customer) => { + console.log('customer', customer); + + setLoading(true); + + try { + setDropCust((prevDropCust) => { + const isAlreadySelected = prevDropCust.some((c) => c.firstname === customer.firstname); + + // 🔴 REMOVE if already exists + if (isAlreadySelected) { + return prevDropCust.filter((c) => c.firstname !== customer.firstname); + } + + // 🟢 ADD if not exists (calculate distance) + return prevDropCust; + }); + + // Only calculate distance if customer is not already added + const alreadyExists = dropCust.some((c) => c.firstname === customer.firstname); + + if (!alreadyExists) { + const obj = await calculateDistance(customer); + const { roundedDistance, totalcharge } = obj; + + const updatedCustomer = { + ...customer, + distance: roundedDistance, + totalcharge + }; + + setDropCust((prevDropCust) => [...prevDropCust, updatedCustomer]); + + console.log(`Rounded Distance: ${roundedDistance} km`); + } + } catch (error) { + console.error('Failed to calculate distance:', error); + } finally { + setLoading(false); + } + }; + + // ========================================================= || calculateDistance || ========================================================= + + // 🔹 Main distance calculation function + const calculateDistance = async (customer) => { + try { + // --- Input validation --- + if (!customer || typeof customer !== 'object') { + throw new Error('Invalid customer data: expected an object.'); + } + + if (!pickCust || typeof pickCust !== 'object') { + throw new Error('Origin (pickCust) data missing or invalid.'); + } + + // --- Compute distance --- + const roundedDistance = await calculateDrivingDistance(pickCust, customer); + + // --- Calculate total charge --- + const totalcharge = calculateTotalCharge(roundedDistance, basePrice, pricePerKm, minKm); + return { roundedDistance, totalcharge }; + } catch (error) { + // --- Categorized smart error handling --- + console.log('on calculateDistance', error.message); + if (error.message.includes('Invalid coordinates') || error.message.includes('Invalid coordinate')) { + console.log('📍 Invalid coordinate format:', error.message); + OpenToast('Invalid coordinate format. Check location data.', 'warning', 3000); + } else if (error.message.includes('Origin') || error.message.includes('customer')) { + console.log('❌ Missing or invalid input data:', error.message); + OpenToast('Missing or invalid input data for distance calculation.', 'warning', 3000); + } else { + console.log('💥 Unexpected error calculating distance:', error); + OpenToast('Unexpected error during distance calculation.', 'error', 3000); + } + + throw error; // keeps your current flow intact + } + }; + + // ==================================================== || fetchTiming || ==================================================== + const fetchTiming = async () => { + setLoading(true); + await axios + .get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`) + .then((res) => { + console.log('fetchTiming', res); + const { opentime, closetime } = res.data.details[0]; + if (res.data.status) { + setStatrttime(`${dayjs().format('MM-DD-YYYY')} ${opentime}`); + setEndtime(`${dayjs().format('MM-DD-YYYY')} ${closetime}`); + console.log('starttime', `${dayjs().format('MM-DD-YYYY')} ${opentime}`); + console.log('endtime', `${dayjs().format('MM-DD-YYYY')} ${closetime} `); + let arr = []; + for ( + let i = `${dayjs().format('MM-DD-YYYY')} ${opentime}`, j = 0; + dayjs(`${dayjs().format('MM-DD-YYYY')} ${closetime} `).diff(i, 'm') >= 0; + j++, i = dayjs(i).add(30, 'm') + ) { + arr.push(i); + } + console.log('setTimeslotarr', arr); + setTimeslotarr(arr); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + useEffect(() => { + if (appId) { + fetchTiming(); + } + }, [appId]); + + const fetchAppAdminTokens = async () => { + setLoading(true); + await axios + .get(`${process.env.REACT_APP_URL}/utils/getapplocationconfig/?applocationid=${appId}`) + .then((res) => { + const userfcmtokemArray = res.data.details.applocationadmins.map((admin) => admin.userfcmtokem); // fcm => firebase cloud messaging + console.log('fetchAppAdminTokens', res); + console.log('userfcmtokemArray', userfcmtokemArray); + if (res.data.status) { + setAdmintoken(userfcmtokemArray); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + + useEffect(() => { + if (appId) { + fetchAppAdminTokens(); + } + }, [appId]); + + useEffect(() => { + console.log('pickCust', pickCust); + }, [pickCust]); + useEffect(() => { + console.log('dropCust', dropCust); + }, [dropCust]); + // // ==================================================== || fetchtenantinfo || ==================================================== + // const fetchtenantinfo = async () => { + // setLoading(true); + // console.log('tenantid', tenantid); + + // await axios + // .get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tenantid}`) + // .then((res) => { + // console.log('fetchtenantinfo', res); + // if (res.data.status) { + // setTenantid(res.data.details.tenantid); + // } + // setLoading(false); + // }) + // .catch((err) => { + // console.log(err); + // setLoading(false); + // }); + // }; + // useEffect(() => { + // if (tenantid) { + // fetchtenantinfo(); + // } + // }, [tenantid]); + // ================================================== || sendnotifications || ================================================== + const sendnotifications = async () => { + setLoading(true); + await axios + .post(`${process.env.REACT_APP_URL}/utils/sendnotifications`, { + priority: 'high', + registration_ids: admintoken, + data: { + accessid: process.env.REACT_APP_RIDER_ACCESS_ID + }, + notification: { + title: 'Nearle Merchant', + body: 'An Order has been placed successfully,kindly process the same', + sound: 'ring' + } + }) + .then((res) => { + console.log(res); + if (res.data.message == 'Success') { + enqueueSnackbar('Notification sent Successfully', { + variant: 'success', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + enqueueSnackbar(err.message, { + variant: 'error', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + setLoading(false); + }); + }; + + const cleanReceiverName = (name) => { + if (typeof name !== 'string') return name; + return name.replace(/^[\d.\s]+/, '').trim(); + }; + + const handleFileUpload = (event) => { + console.log('Normal upload started...'); + try { + const file = event.target.files?.[0]; + if (!file) { + opentoast('No file selected.', 'warning'); + return; + } + + const fileName = file.name.toLowerCase(); + const isCSV = fileName.endsWith('.csv'); + const isExcel = fileName.endsWith('.xls') || fileName.endsWith('.xlsx'); + + // Invalid file + if (!isCSV && !isExcel) { + opentoast('Invalid file type. Please upload a CSV or Excel file.', 'warning'); + return; + } + + // ---------------------- CSV ---------------------- + if (isCSV) { + Papa.parse(file, { + header: true, + dynamicTyping: true, + skipEmptyLines: true, + complete: (results) => { + const data = results.data || []; + if (data.length === 0) { + opentoast('CSV file is empty or invalid.', 'warning'); + setUsers([]); + return; + } + const cleanedData = data.map((row) => ({ + ...row, + firstname: cleanReceiverName(row.firstname) + })); + console.log('✅ Parsed CSV Data:', cleanedData); + setUsers(cleanedData); + opentoast('CSV file uploaded successfully, ✅', 'success', 3000); + opentoast(' Press Continue to add delivery customers', 'warning', 5000); + }, + error: (error) => { + console.error('❌ CSV Parse Error:', error.message); + opentoast(`CSV parsing failed: ${error.message}`, 'warning'); + } + }); + } + + // ---------------------- EXCEL (.xls / .xlsx) ---------------------- + if (isExcel) { + const reader = new FileReader(); + + reader.onload = (e) => { + try { + const data = e.target.result; + + // Use correct mode for binary Excel formats + const workbook = XLSX.read(data, { + type: 'binary', + cellDates: true, + cellNF: false, + cellText: false + }); + const firstSheet = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[firstSheet]; + const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: '' }); + + if (!jsonData || jsonData.length === 0) { + opentoast('Excel file is empty or invalid.', 'warning'); + setUsers([]); + return; + } + + const cleanedData = jsonData.map((row) => ({ + ...row, + firstname: cleanReceiverName(row.firstname) + })); + + console.log('✅ Parsed Excel Data:', cleanedData); + setUsers(cleanedData); + opentoast('Excel file uploaded successfully ✅, press continue', 'success', 3000); + } catch (err) { + console.error('❌ Excel Parse Error:', err); + opentoast(`Error reading Excel: ${err.message}`, 'warning'); + } + }; + + // ✅ Key fix: use readAsBinaryString for both .xls & .xlsx + reader.readAsBinaryString(file); + } + } catch (err) { + console.error('Unexpected error during file upload:', err.message); + opentoast(`Unexpected error: ${err.message}`, 'warning'); + } + }; + + // your header mapping + const headerMap = { + 'pickupdate(yyyy-mmm-dd)': 'date', + 'sendername*': 'locationname', + 'senderphone*': 'locationcontact', + 'senderaddress*': 'locationaddress', + 'receivername*': 'firstname', + 'receiverphone': 'contactno', + 'receiveralternatephone*': 'altcontactno', + receiverfulladdress: 'address', + receiverlatitude: 'latitude', + receiverlongitude: 'longitude', + 'itemdescription*': 'description', + Quantity: 'quantity', + ' Collect Cash': 'collectionamt' + }; + + // helper to normalize headers + const normalizeHeader = (header) => header?.toString().trim().toLowerCase().replace(/\s+/g, ''); + + const handleFileDirectUpload = (event) => { + try { + const file = event.target.files?.[0]; + if (!file) { + opentoast('No file selected.', 'warning'); + return; + } + + const fileName = file.name.toLowerCase(); + const isCSV = fileName.endsWith('.csv'); + const isExcel = fileName.endsWith('.xls') || fileName.endsWith('.xlsx'); + + if (!isCSV && !isExcel) { + opentoast('Invalid file type. Please upload a CSV or Excel file.', 'warning'); + return; + } + + const processData = (data, headers) => { + console.log('data', data); + const normalizedMap = {}; + for (const key in headerMap) { + normalizedMap[normalizeHeader(key)] = headerMap[key]; + } + console.log('normalizedMap', normalizedMap); + + const mappedData = data.map((row) => { + const newRow = {}; + for (const key in row) { + const cleanKey = normalizeHeader(key); + const newKey = normalizedMap[cleanKey] || cleanKey; + let value = row[key]; + if (newKey === 'firstname') value = cleanReceiverName(value); + + newRow[newKey] = value; + } + + return newRow; + }); + + const missingCols = Object.keys(headerMap) + .filter((clientCol) => clientCol.endsWith('*')) + .filter((clientCol) => !headers.includes(normalizeHeader(clientCol))); + + if (missingCols.length > 0) { + isExcel && opentoast(`Missing columns: ${missingCols.join(', ')}`, 'warning'); + } + + console.log('✅ Final Processed Data:', mappedData); + setUsers(mappedData); + opentoast('File uploaded and successfully ', 'success', 3000); + opentoast('Press Continue', 'warning', 3000); + }; + + // ============ CSV handler ============ + if (isCSV) { + Papa.parse(file, { + header: true, + dynamicTyping: true, + skipEmptyLines: true, + complete: (results) => { + if (!results.data?.length) { + opentoast('CSV file is empty or has no valid rows.', 'warning'); + setUsers([]); + return; + } + const headers = results.meta.fields.map(normalizeHeader); + processData(results.data, headers); + }, + error: (error) => { + console.error('❌ CSV Parsing Error:', error); + opentoast(`CSV parsing failed: ${error.message}`, 'warning'); + } + }); + } + + // ============ Excel handler ============ + if (isExcel) { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = e.target.result; + // Try reading as binary first + let workbook; + try { + workbook = XLSX.read(data, { type: 'binary' }); + } catch { + // fallback for modern XLSX files + const arrayBuffer = new Uint8Array(data); + workbook = XLSX.read(arrayBuffer, { type: 'array' }); + } + + const firstSheet = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[firstSheet]; + const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: '' }); + + if (!jsonData?.length) { + opentoast('Excel file is empty or invalid.', 'warning'); + setUsers([]); + return; + } + + const headers = Object.keys(jsonData[0]).map(normalizeHeader); + processData(jsonData, headers); + } catch (err) { + console.error('❌ Error processing Excel:', err); + opentoast(`Error reading Excel: ${err.message}`, 'warning'); + } + }; + + // Important: use readAsBinaryString for Excel + reader.readAsBinaryString(file); + } + } catch (err) { + console.error('Unexpected error during file upload:', err); + opentoast(`Unexpected error: ${err.message}`, 'warning'); + } + }; + + // =============================================== || createorders || =============================================== + const createorders = async () => { + // ===================== Build Payload ===================== + const arr = dropCust.map((customer) => ({ + applocationid: pickCust.applocationid, + configid: 9, + partnerid: pickCust.partnerid, + partneruserid: +userid, + paymentstatus: 1, + paymenttype: 42, + pickupaddress: pickCust.address || '', + pickupcity: pickCust.city || '', + pickupcontactno: pickCust.contactno || '', + pickupcustomer: pickCust.locationname || '', + pickuplandmark: pickCust.landmark || '', + pickuplat: pickCust.latitude, + pickuplocation: pickCust.suburb || '', + pickuplocationid: pickCust.locationid || 0, + pickuplong: pickCust.longitude, + tenantid: pickCust.tenantid, + + customerid: +customer?.customerid, + deliveryaddress: customer.address || '', + deliverycharge: +customer.totalcharge || 0, + deliverycity: customer.city || '', + deliverycontactno: customer.contactno?.toString() || '', + deliverycustomer: customer.firstname || '', + deliveryid: +customer.customerid, + deliverylandmark: customer.landmark || '', + deliverylat: customer.latitude?.toString() || '', + deliverylocation: customer.suburb || '', + deliverylocationid: customer.deliverylocationid || 0, + deliverylong: customer.longitude?.toString() || '', + deliverytime: `${dayjs(startdate).format('YYYY-MM-DD')} ${dayjs(selectedtime.$d).format('HH:mm:ss')}`, + deliverytype: 'B', + + itemcount: 1, + quantity: customer.quantity, + collectionamt: customer.collectionamt, + kms: customer.distance?.toString() || '0', + locationid: +pickCust.locationid, + moduleid: +pickCust.moduleid, + + orderamount: +customer.totalcharge || 0, + ordercharges: 0.0, + orderdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), + ordernotes: otherinstructions, + orderstatus: 'created', + ordervalue: +customer.totalcharge || 0, + pickupSlot + })); + + console.log('arr', arr); + + // ===================== Validation ===================== + if (!tenantid) { + opentoast('Choose Client', 'warning'); + return; + } + setLoading(true); + try { + const res = await axios.post(`${process.env.REACT_APP_URL}/orders/createorders`, arr); + if (res.data.status) { + opentoast('Order Created Successfully', 'success', 2000); + if (admintoken) { + sendnotifications(); + } + navigate('/nearle/orders'); + setLoading(false); + } else { + console.log(res.data); + console.error('Create order failed (API response):', res.data); + opentoast(res?.data?.message || 'Order creation failed. Please try again.', 'warning', 3000); + } + } catch (err) { + opentoast(err.message, 'error', 2000); + console.log('create orders', err.message); + console.error('Create order error:', { + message: err.message, + response: err.response, + request: err.request, + stack: err.stack + }); + + // Exact but short error for user + let toastMessage = 'Something went wrong. Please try again.'; + + if (err.response) { + // Server responded with error + toastMessage = err.response.data?.message || `Server error (${err.response.status})`; + } else if (err.request) { + // No response received + toastMessage = 'Network error. Check your internet connection.'; + } + opentoast(toastMessage, 'error'); + setLoading(false); + } finally { + setLoading(false); + setBtnLoading(false); + } + }; + + const [fileName, setFileName] = useState(''); + const removeFileExtension = (fileName) => { + return fileName.replace(/\.[^/.]+$/, ''); + }; + + const onFileChange = (event) => { + const file = event.target.files[0]; + if (!file) return; + const cleanedName = removeFileExtension(file.name); + setFileName((prev) => (prev ? `${prev}, ${cleanedName}` : cleanedName)); + if (tenantid === 916) { + handleFileDirectUpload(event); + } else { + handleFileDirectUpload(event); + // handleFileUpload(event); + } + }; + + const handleQuantityChange = (customerid, value) => { + setDropCust((prev) => prev.map((cust) => (cust.customerid === customerid ? { ...cust, quantity: Number(value) || 0 } : cust))); + }; + const handleCollectionAmtChange = (customerid, value) => { + setDropCust((prev) => prev.map((cust) => (cust.customerid === customerid ? { ...cust, collectionamt: Number(value) || 0 } : cust))); + }; + + return ( + <> + {loading && ( + <> + + {/* */} + + )} + { + theme.zIndex.drawer + 1 + }} + open={btnLoading} // when loader = true, backdrop covers the page + > + + + } + + + {/* ===================================================== ||Business Location || ===================================================== */} + + + Create Multiple Order + + + {tenantLocations?.length === 1 ? ( + + + + ) + }} + /> + ) : ( + `${option.locationname} (${option.suburb})`} + value={locationValue} + onOpen={(event) => { + if (!appId && !tenantid) { + event.preventDefault(); + + OpenToast('Please select Location and Tenant first!', 'warning', 3000); + + setTimeout(() => { + locationRef.current?.focus(); + }, 0); + } else if (!tenantid) { + event.preventDefault(); + + OpenToast('Please select Tenant first!', 'warning', 3000); + + setTimeout(() => { + tenantRef.current?.focus(); + }, 0); + } + }} + onChange={(event, value, reason) => { + if (reason === 'clear') { + setLocationid(0); + setLocationValue(null); + setPickCust(null); + } else { + setLocationid(value?.locationid || 0); + setLocationValue(value); + setPickCust(value); + setPickupSlotsList(value?.slots); + } + }} + renderInput={(params) => } + /> + )} + + + + + {/* ===================================================== || Pickup || ===================================================== */} + + + {locationid !== 0 && ( + + + + + + )} + {/* ================================================= || Time || ================================================= */} + + + + + + { + setStartdate(e); + let dateres11 = dayjs().diff(dayjs(`${dayjs(e).format('YYYY-MM-DD')}`), 'd'); + console.log('dateres11'); + console.log(dateres11); + setSelectedtime(''); + if (dateres11 <= 0) { + console.log('startdate', e); + setStartdate(e); + + let arr = []; + timeslotarr.map((val) => { + if (dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0) { + arr.push(val); + } + }); + } else { + setAlertmessage('choose Upcoming Date'); + opentoast('choose Upcoming Date', 'warning'); + setStartdate(NaN); + } + }} + value={dayjs(startdate)} + sx={{ width: 'auto', mt: 0 }} + disablePast + /> + + + {/* {timeslotarr.length > 0 && ( + + + + + + + Time + + + + + + { + setStartdate(e); + let dateres11 = dayjs().diff(dayjs(`${dayjs(e).format('YYYY-MM-DD')}`), 'd'); + console.log('dateres11'); + console.log(dateres11); + setSelectedtime(''); + if (dateres11 <= 0) { + console.log('startdate', e); + setStartdate(e); + + let arr = []; + timeslotarr.map((val) => { + if ( + dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0 + ) { + arr.push(val); + } + }); + } else { + setAlertmessage('choose Upcoming Date'); + opentoast('choose Upcoming Date', 'warning'); + setStartdate(NaN); + } + }} + value={dayjs(startdate)} + sx={{ width: 'auto', mt: 0 }} + disablePast + /> + + + + + + + {timeslotarr.map((val, index) => { + if ( + dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0 + ) { + return ( + + + { + console.log('selectedtime', val); + setSelectedtime(val); + }} + // onClick={() => { + // if (distance > appLocaRadius) { + // setOpen(true); + // } else if (showDistance) { + // console.log('selectedtime', val); + // setSelectedtime(val); + // } else { + // opentoast('Out of city limit', 'error'); + // } + // }} + /> + + + ); + } + })} + + + + + + )} */} + + + { + if (reason === 'clear') { + setSelectedtime(null); + setPickupSlot(null); + } else { + // Convert to AM/PM and merge with date + const formattedTime = dayjs(newValue.time, 'HH:mm').format('hh:mm A'); + setSelectedtime(formattedTime); + const finalDateTime = dayjs(`${startdate} ${formattedTime}`, 'MM-DD-YYYY hh:mm A').format('YYYY-MM-DD hh:mm A'); + setPickupSlot(finalDateTime); + } + }} + getOptionLabel={(option) => `${option.name} (${dayjs(option.time, 'HH:mm').format('hh:mm A')})`} + renderInput={(params) => } + /> + + + + + + {/* ===================================================== || Drop || ===================================================== */} + + + + + Drop ({dropCust?.length || 0}) + + + {/* ================= Upload CSV ================= */} + {uploadType === 0 && ( + <> + {fileName && ( + + + + {fileName} + + + )} + + + + + + )} + + {/* ================= Continue ================= */} + {users.length >= 1 && uploadType === 0 && ( + + )} + + {/* ================= Select Customers ================= */} + {uploadType === 1 && ( + + )} + + {/* ================= Upload Type ================= */} + + + Upload Type + { + if (!locationid) { + OpenToast('Please select Business Location!', 'warning', 3000); + return; + } + setUploadType(Number(e.target.value)); + setDropCust([]); + setUsers([]); + setFileName(''); + }} + > + } label="Excel / CSV" /> + } label="Selection" /> + + + + + + } + > + + + {dropCust?.length > 0 ? ( + <> + + + S.No + Customer + Address + Quantity + + + Cash Collect + + Kms + + Charge + Action + + + + + {dropCust?.map((customer, index) => ( + + {index + 1} + {customer.firstname} + {customer.address} + + {uploadType == 0 ? ( + {customer.quantity} + ) : ( + handleQuantityChange(customer.customerid, e.target.value)} + inputProps={{ min: 0 }} + /> + )} + + + {uploadType == 0 ? ( + ₹{Number(customer.collectionamt || 0).toFixed(2)} + ) : ( + { + if (e.target.value <= 0) { + handleCollectionAmtChange(customer.customerid, 0); + } else { + handleCollectionAmtChange(customer.customerid, e.target.value); + } + }} + inputProps={{ min: 0 }} + InputProps={{ + startAdornment: + }} + /> + )} + + + {customer.distance} + {`₹${customer?.totalcharge?.toFixed(2)}`} + + { + <> + handleCheckboxChange(event, customer)} + onClick={() => handleCheckboxChange1(customer)} + /> + + } + + + ))} + {dropCust?.length != 0 && ( + + Total + + + + {`${totalQty} `} + + + + {`${totalCash?.toFixed(2)} `} + + + {`${totaldist} `} + + + {`₹${totalAmt?.toFixed(2)}`} + + + + + )} + + + ) : ( + + {/* Header */} + + {' '} + + Important Instructions + + + + {/* Ordered List */} + + + Choose either Upload Type to upload CSV/Excel files, or + Selection Type to select from saved customers. + + + + Uploaded CSV or Excel files must follow the required format and contain the correct column names. + + + + Multiple files can be uploaded, but only one file at a time. + + + + Invalid or incorrectly formatted files will not be processed. + + + + )} +
    +
    +
    +
    + + + {/* ================================================= || Notes || ================================================= */} + + + + setOtherinstructions(e.target.value)} + /> + + + + + + + +
    + {/* ============================================= || saved address Dialog || ============================================= */} + { + setIsCustomerOpen(false); + }} + fullWidth + sx={{ minWidth: 'lg' }} + > + + + {`Select Drop Customers (${dropCust.length || 0})`} + + + setSearchCustList(e.target.value)} + sx={{ + '& .MuiOutlinedInput-input': { + p: '10.5px 0px 12px' + }, + bgcolor: 'white' + }} + startAdornment={ + + + + } + endAdornment={ + { + setSearchCustList(''); + }} + > + + + } + autoComplete="off" + /> + + + + + + + {customerlist?.length == 0 ? ( + + + + ) : ( + + {customerlist && + customerlist?.map((customer, index) => ( + + cust.customerid === customer.customerid)} // Set the checked state of the checkbox based on whether the customer is in `dropCust` + onChange={(event) => handleCheckboxChange(event, customer)} + /> + } + label={ +
    + + {`${customer.firstname} (${customer.contactno})`} + + + + {customer.address} + +
    + } + /> +
    + ))} +
    + )} +
    + + + + +
    + + ); +}; + +export default MultipleOrders; diff --git a/src/pages/nearle/orders/multiOrderBackup.js b/src/pages/nearle/orders/multiOrderBackup.js new file mode 100644 index 0000000..2ba9b65 --- /dev/null +++ b/src/pages/nearle/orders/multiOrderBackup.js @@ -0,0 +1,846 @@ +/* eslint-disable no-unused-vars */ +import React from 'react'; +import Loader from 'components/Loader'; +import { useEffect, useState, Fragment } from 'react'; +import { useTheme } from '@mui/material/styles'; +import MainCard from 'components/MainCard'; +import axios from 'axios'; +import ClearIcon from '@mui/icons-material/Clear'; +import { SearchOutlined, CloseOutlined } from '@ant-design/icons'; +import { Empty } from 'antd'; +import MyLocationIcon from '@mui/icons-material/MyLocation'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import dayjs from 'dayjs'; +var utc = require('dayjs/plugin/utc'); +dayjs.extend(utc); +import { enqueueSnackbar } from 'notistack'; +import { useNavigate } from 'react-router'; +import { calculateDrivingDistance, calculateTotalCharge } from '../../../utils/distance'; +import { GoogleMap, LoadScript, Marker } from '@react-google-maps/api'; + +import { + FormControl, + InputAdornment, + Grid, + Typography, + Stack, + Button, + TextField, + Autocomplete, + Divider, + Dialog, + DialogTitle, + DialogContent, + Checkbox, + DialogActions, + CircularProgress, + IconButton, + OutlinedInput, + FormGroup, + FormControlLabel, + Table, + TableContainer, + TableCell, + TableBody, + TableRow, + Paper, + TableHead, + Box +} from '@mui/material'; +import CircularLoader from 'components/nearle_components/CircularLoader'; +// import RidersPinPointOSM from './RidersPinPointOSM'; +import RidersPinPoint from './ridersPinPoint'; + +const MultipleOrders = () => { + const navigate = useNavigate(); + const theme = useTheme(); + const [loading, setLoading] = useState(false); + const [btnLoading, setBtnLoading] = useState(false); + const [appId, setAppId] = useState(0); + + const [tenantLocations, setTenantlocations] = useState([]); + const userid = localStorage.getItem('userid'); + const tenId = localStorage.getItem('tenantid'); + const [tid, setTid] = useState(0); + const [isLocation, setIsLocation] = useState(false); + const [basePrice, setBasePrice] = useState(0); + const [pricePerKm, setPricePerKm] = useState(0); + const [minKm, setMinKm] = useState(0); + const [pickCust, setPickCust] = useState(null); + const [dropCust, setDropCust] = useState([]); + const [isCustomerOpen, setIsCustomerOpen] = useState(false); + const [searchCustList, setSearchCustList] = useState(''); + const [customerlist, setCustomerlist] = useState([]); + const [startdate, setStartdate] = useState(dayjs().format('MM-DD-YYYY')); + const [timeslotarr, setTimeslotarr] = useState([]); + const [starttime, setStatrttime] = useState(); + const [endtime, setEndtime] = useState(); + const [alertmessage, setAlertmessage] = useState(''); + const [otherinstructions, setOtherinstructions] = useState(''); + const [admintoken, setAdmintoken] = useState(); + const [totaldist, settotaldist] = useState(0); + const [totalAmt, settotalAmt] = useState(0); + const [isLoading, setIsLoading] = useState(false); + const [showMap, setShowMap] = useState(false); + + useEffect(() => { + dropCust && console.log('dropCust', dropCust); + }, [dropCust]); + + // =============================================== || opentoast || =============================================== + const opentoast = (message, variant, time) => { + enqueueSnackbar(message, { + variant: variant, + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: time ? time : 1500 + }); + console.log(alertmessage); + }; + // ==============================|| fetchAppLocations ||============================== // + const fetchAppLocations = async () => { + try { + const locationRes = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`); + console.log('fetchAppLocations', locationRes.data.details); + } catch (err) { + console.log('locationRes', err); + } + }; + useEffect(() => { + fetchAppLocations(); + }, []); + + // ============================================= || fetchTenantPricing || ============================================= + + const fetchTenantPricing = async (id) => { + try { + const pricingResponse = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantpricing/?tenantid=${tenId}`); + console.log('pricingResponse', pricingResponse.data.details); + setBasePrice(pricingResponse.data.details.baseprice); + setPricePerKm(pricingResponse.data.details.priceperkm); + setMinKm(pricingResponse.data.details.minkm); + } catch (error) { + console.log('fetchTenantPricing error', error); + } + }; + useEffect(() => { + fetchTenantPricing(); + }, []); + // ============================================= || gettenantlocations (branches) || ============================================= + const gettenantlocations = async (id) => { + try { + const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${id}`); + console.log('gettenantlocations', res.data.details); + if (res.data.details.length == 1) { + setIsLocation(true); + setTenantlocations(res.data.details); + setPickCust(res.data.details[0]); + } else { + setTenantlocations(res.data.details); + } + } catch (err) { + console.log('gettenantlocations', err); + } + }; + useEffect(() => { + gettenantlocations(tenId); + }, []); + // ========================================================= || clientdetails || ========================================================= + const clientdetails = async () => { + try { + let url = + searchCustList == '' + ? `${process.env.REACT_APP_URL}/customers/gettenantcustomers/?tenantid=${tenId}&pageno=1&pagesize=10` + : `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tenId}&keyword=${searchCustList}`; + await axios + .get(url) + .then((res) => { + if (res.data.status) { + console.log('clientdetails', res.data.details); + + setCustomerlist(res.data.details); + let arr = []; + res.data.details.map((val) => { + arr.push({ + label: `${val.firstname} | ${val.contactno}`, + ...val + }); + }); + } + }) + .catch((err) => { + console.log(err); + opentoast('server error', 'warning'); + }); + } catch (err) { + console.log(err); + } + }; + useEffect(() => { + if (tenId) { + clientdetails(); + } + }, [searchCustList.length > 3, searchCustList == '', tenId]); + + // ========================================================= || calculateTotal(dist , charge) || ========================================================= + const calculateTotal = () => { + let a1 = 0; + let a2 = 0; + dropCust?.map((customer) => { + a1 += customer.distance; + a2 += customer.totalcharge; + }); + settotaldist(a1); + settotalAmt(a2); + }; + useEffect(() => { + dropCust && calculateTotal(); + }, [dropCust]); + + // ========================================================= || handleCheckboxChange || ========================================================= + const handleCheckboxChange = async (event, customer) => { + setIsLoading(true); + console.log('event', event.target.checked); + console.log('customer', customer); + if (event.target.checked) { + // If the checkbox is checked, calculate the distance and add the customer + try { + const obj = await calculateDistance(customer); + console.log('return of calculateDistance', obj); + + const { roundedDistance, totalcharge } = obj; + // Create a new customer object with the distance property + const updatedCustomer = { + ...customer, + distance: roundedDistance, + totalcharge: totalcharge + }; + + // Add the updated customer object to dropCust + setDropCust((prevDropCust) => [...prevDropCust, updatedCustomer]); + + // Log the rounded distance + console.log(`Rounded Distance: ${roundedDistance} km`); + } catch (error) { + console.error('Failed to calculate distance:', error); + } + setIsLoading(false); + } else { + // If the checkbox is unchecked, remove the customer from dropCust + setDropCust((prevDropCust) => { + return prevDropCust.filter((cust) => cust.customerid !== customer.customerid); + }); + setIsLoading(false); + } + }; + + // ========================================================= || calculateDistance || ========================================================= + const calculateDistance = async (customer) => { + console.log('Distance calculation starts'); + try { + const roundedDistance = await calculateDrivingDistance(pickCust, customer); + const totalcharge = calculateTotalCharge(roundedDistance, basePrice, pricePerKm, minKm); + return { roundedDistance, totalcharge }; + } catch (error) { + console.error('Error calculating distance:', error); + throw error; + } + }; + + // ==================================================== || fetchTiming || ==================================================== + const fetchTiming = async () => { + setLoading(true); + await axios + .get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`) + .then((res) => { + console.log('fetchTiming', res); + const { opentime, closetime, latitude, longitude, radius } = res.data.details[0]; + if (res.data.status) { + setStatrttime(`${dayjs().format('MM-DD-YYYY')} ${opentime}`); + setEndtime(`${dayjs().format('MM-DD-YYYY')} ${closetime}`); + console.log('starttime', `${dayjs().format('MM-DD-YYYY')} ${opentime}`); + console.log('endtime', `${dayjs().format('MM-DD-YYYY')} ${closetime} `); + let arr = []; + for ( + let i = `${dayjs().format('MM-DD-YYYY')} ${opentime}`, j = 0; + dayjs(`${dayjs().format('MM-DD-YYYY')} ${closetime} `).diff(i, 'm') >= 0; + j++, i = dayjs(i).add(30, 'm') + ) { + arr.push(i); + } + console.log('setTimeslotarr', arr); + setTimeslotarr(arr); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + useEffect(() => { + if (appId) { + fetchTiming(); + } + }, [starttime, endtime, appId]); + + const fetchAppAdminTokens = async () => { + setLoading(true); + await axios + .get(`${process.env.REACT_APP_URL}/utils/getapplocationconfig/?applocationid=${appId}`) + .then((res) => { + const userfcmtokemArray = res.data.details.applocationadmins.map((admin) => admin.userfcmtokem); // fcm => firebase cloud messaging + console.log('fetchAppAdminTokens', res); + console.log('userfcmtokemArray', userfcmtokemArray); + if (res.data.status) { + setAdmintoken(userfcmtokemArray); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + + useEffect(() => { + if (starttime && endtime) { + fetchAppAdminTokens(); + } + }, [starttime, endtime]); + + useEffect(() => { + console.log('pickCust', pickCust); + }, [pickCust]); + + // ==================================================== || fetchtenantinfo || ==================================================== + const fetchtenantinfo = async () => { + setLoading(true); + console.log('tid', tid); + + await axios + .get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`) + .then((res) => { + console.log('fetchtenantinfo', res); + if (res.data.status) { + fetchAppAdminTokens(); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + useEffect(() => { + if (tid) { + fetchtenantinfo(); + } + }, [tid]); + // ================================================== || sendnotifications || ================================================== + const sendnotifications = async () => { + setLoading(true); + await axios + .post(`${process.env.REACT_APP_URL}/utils/sendnotifications`, { + priority: 'high', + registration_ids: admintoken, + data: { + accessid: process.env.REACT_APP_RIDER_ACCESS_ID + }, + notification: { + title: 'Nearle Merchant', + body: 'An Order has been placed successfully,kindly process the same', + sound: 'ring' + } + }) + .then((res) => { + console.log(res); + if (res.data.message == 'Success') { + enqueueSnackbar('Notification sent Successfully', { + variant: 'success', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + enqueueSnackbar(err.message, { + variant: 'error', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + setLoading(false); + }); + }; + // =============================================== || creategrouporders || =============================================== + const creategrouporders = async () => { + const arr = dropCust?.map((customer) => ({ + applocationid: pickCust.applocationid, + cancellled: '', + // categoryid: +tenant.categoryid, + configid: 9, + customerid: customer.customerid, + deliveryaddress: customer.address || '', + deliverycharge: +customer.totalcharge || 0, + deliverycity: customer.city || '', + deliverycontactno: customer.contactno || '', + deliverycustomer: customer.firstname || '', + deliveryid: +customer.customerid, + deliverylandmark: customer.landmark || '', + deliverylat: customer.latitude, + deliverylocation: customer.suburb || '', + deliverylocationid: customer.deliverylocationid || 0, + deliverylong: customer.longitude, + // deliverytime: `${dayjs(startdate).format('YYYY-MM-DD HH:mm:ss')} `, + deliverytime: dayjs().format('YYYY-MM-DD HH:mm:ss'), + deliverytype: 'B', + delivered: '', + itemcount: 1, + kms: customer.distance.toString() || 0, + locationid: +pickCust.locationid, + moduleid: +pickCust.moduleid, + orderamount: +customer.totalcharge || 0, + ordercharges: 0.0, + orderdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), + orderheaderid: 0, + orderid: '', // + ordernotes: otherinstructions, + orderstatus: 'created', + ordervalue: +customer.totalcharge || 0, + partnerid: pickCust.partnerid, + partneruserid: +userid, + paymentstatus: 1, + paymenttype: 42, + pending: '', + pickupaddress: pickCust.address || '', + pickupcity: pickCust.locationcity || '', + pickupcontactno: pickCust.contactno || '', + pickupcustomer: pickCust.locationname || '', + pickuplandmark: pickCust.landmark || '', + pickuplat: pickCust.latitude, + pickuplocation: pickCust.suburb || '', + pickuplocationid: pickCust.locationid || 0, + pickuplong: pickCust.longitude, + processing: '', + ready: '', + remarks: '', + taxamount: 0.0, + tenantid: pickCust.tenantid, + tenantuserid: 0 + })); + console.log('arr', arr); + + if (!tenId) { + opentoast('Choose Client ', 'warning'); + } else { + setLoading(true); + + await axios + .post(`${process.env.REACT_APP_URL}/orders/createorders`, arr) + .then((res) => { + if (res.data.status) { + enqueueSnackbar('Order Created Successfully', { + variant: 'success', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + if (admintoken) { + // notifyadmin(admintoken); + sendnotifications(); + } + navigate('/nearle/orders'); + } else { + opentoast(res.data.message, 'warning'); + } + setLoading(false); + console.log(res); + }) + .catch((err) => { + console.log(err); + // opentoast(err.data.message, 'warning'); + setLoading(false); + }); + } + console.log(arr); + }; + + return ( + <> + {loading && } + {/* */} + + + + + Multiple Orders + + + + + + {/* Business Location */} + + {tenantLocations?.length === 1 ? ( + + + + ) + }} + /> + ) : ( + `${option.locationname} (${option.suburb})`} + onChange={(event, value, reason) => { + if (value) { + setTid(value.tenantid); + setIsLocation(true); + setPickCust(value); + } + if (reason === 'clear') setIsLocation(false); + }} + renderInput={(params) => } + /> + )} + + + {/* Date Picker */} + + + { + let diff = dayjs().diff(dayjs(dayjs(e).format('YYYY-MM-DD')), 'd'); + + if (diff <= 0) { + setStartdate(e); + + let arr = []; + timeslotarr.forEach((val) => { + if (dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0) { + arr.push(val); + } + }); + + if (arr[0]) { + setOrderarr([ + { + sno: 1, + address: '', + customerid: '', + deliverytime: dayjs(arr[0]), + deliverylocationid: '', + clientname: '', + contactno: '', + latitude: '', + longitude: '' + } + ]); + } else { + setOrderarr([]); + } + } else { + opentoast('choose Upcoming Date', 'warning'); + setStartdate(NaN); + } + }} + /> + + + + + + + {/* ===================================================== || Pickup || ===================================================== */} + {pickCust && ( + + + + + Pickup Location + Address + + + + + {pickCust?.locationname} + {pickCust?.address} + + +
    +
    + )} + + {/* ===================================================== || Drop || ===================================================== */} + + { + if (!isLocation) { + opentoast('Select Business Location', 'warning'); + } else { + setIsCustomerOpen(true); + setSearchCustList(''); + } + }} + > + Select Customers + + } + > + + + + + S.No + Customer + Address + Kms + Charge + Action + + + + {!dropCust && ( + + + + + + )} + {dropCust?.map((customer, index) => ( + + {index + 1} + {customer.firstname} + {customer.address} + {customer.distance} + {`₹${customer.totalcharge}.00`} + + { + handleCheckboxChange(event, customer)} + /> + } + + + ))} + {dropCust?.length != 0 && ( + + + Total + + + + + {`${totaldist} `} + + + {`₹${totalAmt}.00`} + + + + )} + +
    +
    +
    + + {/* ================================================= || Riders Map || ================================================= */} + + {/* {showMap && dropCust.length >= 1 && } */} + + {/* ================================================= || Notes || ================================================= */} + {dropCust && ( + + + + setOtherinstructions(e.target.value)} + /> + + + + + + + )} + + {/* ============================================= || saved address Dialog || ============================================= */} + { + setIsCustomerOpen(false); + }} + fullWidth + sx={{ minWidth: 'lg' }} + > + {isLoading && } + + + {`Select Drop Customers (${dropCust?.length || 0})`} + + + setSearchCustList(e.target.value)} + sx={{ + '& .MuiOutlinedInput-input': { + p: '10.5px 0px 12px' + }, + bgcolor: 'white' + }} + startAdornment={ + + + + } + endAdornment={ + { + setSearchCustList(''); + }} + > + + + } + autoComplete="off" + /> + + + + + + + {customerlist.length == 0 ? ( + + + + ) : ( + + {customerlist && + customerlist.map((customer, index) => ( + + cust.customerid === customer.customerid)} // Set the checked state of the checkbox based on whether the customer is in `dropCust` + onChange={(event) => handleCheckboxChange(event, customer)} + /> + } + label={ +
    + + {`${customer.firstname} (${customer.contactno})`} + + + + {customer.address} + +
    + } + /> +
    + ))} +
    + )} +
    + + + + +
    + + ); +}; + +export default MultipleOrders; diff --git a/src/pages/nearle/orders/multipleOrders.js b/src/pages/nearle/orders/multipleOrders.js index 834226f..a1f99f3 100644 --- a/src/pages/nearle/orders/multipleOrders.js +++ b/src/pages/nearle/orders/multipleOrders.js @@ -1,4 +1,5 @@ -import React, { useEffect, useState, useRef, Fragment } from 'react'; +/* eslint-disable no-unused-vars */ +import React, { useEffect, useState, useRef } from 'react'; import axios from 'axios'; import Papa from 'papaparse'; import * as XLSX from 'xlsx'; @@ -55,9 +56,7 @@ import { CalendarOutlined, ClockCircleOutlined, FileTextOutlined, - InboxOutlined, - LockOutlined, - CheckCircleFilled + InboxOutlined } from '@ant-design/icons'; import { Empty } from 'antd'; import { FaUser, FaTruck, FaUsers, FaPaperPlane, FaRoute, FaMoneyBillWave, FaBoxes, FaReceipt } from 'react-icons/fa'; @@ -66,47 +65,31 @@ import { MdOutlineCloudUpload } from 'react-icons/md'; import Loader from 'components/Loader'; import CircularLoader from 'components/CircularLoader'; -import AnimateButton from 'components/@extended/AnimateButton'; -import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; import './OrdersRedesign.css'; var utc = require('dayjs/plugin/utc'); dayjs.extend(utc); -// ============================== small style tokens ============================== -const cellHeaderSx = { - fontSize: 11.5, - fontWeight: 700, - color: '#475569', - py: 0.75, - px: 1 -}; - -const cellBodySx = { - fontSize: 12, - py: 0.6, - px: 1 -}; +const cellHeaderSx = { fontSize: 11.5, fontWeight: 700, color: '#475569', py: 0.75, px: 1 }; +const cellBodySx = { fontSize: 12, py: 0.6, px: 1 }; const MultipleOrders = () => { const navigate = useNavigate(); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); const locationRef = useRef(null); - const tenantRef = useRef(null); const userid = localStorage.getItem('userid'); + // tenantid is fixed for this app — comes from the logged-in session + const tenantid = localStorage.getItem('tenantid'); // ============================== state ============================== const [locations, setLocations] = useState([]); - const [tenantlist, setTenantlist] = useState([]); const [tenantLocations, setTenantlocations] = useState([]); const [loading, setLoading] = useState(false); const [btnLoading, setBtnLoading] = useState(false); const [appId, setAppId] = useState(0); - const [tenantid, setTenantid] = useState(0); const [locationid, setLocationid] = useState(0); - const [tenantValue, setTenantValue] = useState(null); const [locationValue, setLocationValue] = useState(null); const [basePrice, setBasePrice] = useState(0); @@ -135,18 +118,16 @@ const MultipleOrders = () => { const [uploadType, setUploadType] = useState(null); const [users, setUsers] = useState([]); const [fileName, setFileName] = useState(''); + const [slotDropdownOpen, setSlotDropdownOpen] = useState(false); - // Stable dedup cache for OpenToast. Was a `let` inside the component body, - // which got recreated on every render and broke the dedup. A ref persists - // across renders without triggering re-renders itself. const toastCacheRef = useRef({}); // ============================== toast ============================== const opentoast = (message, variant, time) => { enqueueSnackbar(message, { - variant: variant, + variant, anchorOrigin: { vertical: 'top', horizontal: 'right' }, - autoHideDuration: time ? time : 1500 + autoHideDuration: time || 1500 }); }; @@ -158,15 +139,10 @@ const MultipleOrders = () => { setTimeout(() => delete toastCacheRef.current[key], 3000); }; - // ============================== effects: reset chains ============================== + // ============================== reset chains ============================== useEffect(() => { - // appId change → clear downstream selections (tenant, location, customers) - setTenantid(0); - setTenantValue(null); setLocationid(0); setLocationValue(null); - setTenantlocations([]); - setPickCust(null); setDropCust([]); setUsers([]); setUploadType(null); @@ -176,22 +152,12 @@ const MultipleOrders = () => { setSelectedtime(''); }, [appId]); - useEffect(() => { - // tenantid change → clear location/customers (keep appId) - setLocationid(0); - setLocationValue(null); - setDropCust([]); - setUsers([]); - setUploadType(null); - setFileName(''); - }, [tenantid]); - // ============================== fetchAppLocations ============================== const fetchAppLocations = async () => { setLoading(true); try { - const locationRes = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`); - setLocations(locationRes.data.details || []); + const res = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`); + setLocations(res.data.details || []); } catch (err) { OpenToast(err.message, 'error', 5000); } finally { @@ -199,29 +165,7 @@ const MultipleOrders = () => { } }; - useEffect(() => { - fetchAppLocations(); - }, []); - - // ============================== fetchtenantinfolist ============================== - const fetchtenantinfolist = async () => { - setLoading(true); - try { - const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${appId}&status=active`); - if (res.data.status) { - const arr = (res.data.details || []).map((val) => ({ ...val, label: `${val.tenantname}` })); - setTenantlist(arr); - } - } catch (err) { - OpenToast('Failed to load clients', 'warning', 3000); - } finally { - setLoading(false); - } - }; - - useEffect(() => { - if (appId) fetchtenantinfolist(); - }, [appId]); + useEffect(() => { fetchAppLocations(); }, []); // ============================== fetchTenantPricing ============================== const fetchTenantPricing = async (id) => { @@ -231,11 +175,13 @@ const MultipleOrders = () => { setBasePrice(d.baseprice || 0); setPricePerKm(d.priceperkm || 0); setMinKm(d.minkm || 0); - } catch (error) { - console.log('fetchTenantPricing error', error); + } catch (err) { + console.log('fetchTenantPricing', err); } }; + useEffect(() => { if (tenantid) fetchTenantPricing(tenantid); }, []); + // ============================== gettenantlocations ============================== const gettenantlocations = async (id) => { try { @@ -255,24 +201,23 @@ const MultipleOrders = () => { } }; + useEffect(() => { if (tenantid) gettenantlocations(tenantid); }, []); + // ============================== clientdetails ============================== const clientdetails = async () => { try { - const url = - searchCustList === '' - ? `${process.env.REACT_APP_URL}/customers/gettenantcustomers/?tenantid=${tenantid}&pageno=1&pagesize=30` - : `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tenantid}&keyword=${searchCustList}`; + const url = searchCustList === '' + ? `${process.env.REACT_APP_URL}/customers/gettenantcustomers/?tenantid=${tenantid}&pageno=1&pagesize=30` + : `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tenantid}&keyword=${searchCustList}`; const res = await axios.get(url); if (res.data.status) setCustomerlist(res.data.details || []); } catch (err) { - console.log(err); opentoast('server error', 'warning'); } }; useEffect(() => { if (!tenantid) return; - // Light debounce so we don't fire on every keystroke. const t = setTimeout(() => { if (searchCustList === '' || searchCustList.length > 2) clientdetails(); }, 250); @@ -281,10 +226,7 @@ const MultipleOrders = () => { // ============================== totals ============================== useEffect(() => { - let a1 = 0; - let a2 = 0; - let a3 = 0; - let a4 = 0; + let a1 = 0, a2 = 0, a3 = 0, a4 = 0; dropCust.forEach((c) => { a1 += Number(c.distance) || 0; a2 += Number(c.totalcharge) || 0; @@ -297,6 +239,7 @@ const MultipleOrders = () => { settotalCash(a4); }, [dropCust]); + // ============================== distance (Google) ============================== // ============================== distance (OSRM/Haversine) ============================== const calculateDistance = async (customer) => { try { @@ -316,7 +259,7 @@ const MultipleOrders = () => { } }; - // ============================== handleCheckboxChange (dialog: add/remove on tick) ============================== + // ============================== checkbox handlers ============================== const handleCheckboxChange = async (event, customer) => { setLoading(true); try { @@ -333,31 +276,18 @@ const MultipleOrders = () => { } }; - // Toggle handler used by: - // 1. CSV "Continue" bulk add (matches by firstname since uploaded rows - // don't carry a stable customerid). - // 2. Per-row remove (CloseOutlined) in the drop table. - // Uses a single functional updater + locally-captured already-selected flag - // so we don't read stale `dropCust` after the state change. const handleCheckboxChange1 = async (customer) => { - // Compute "already selected" from the latest state synchronously. let wasSelected = false; setDropCust((prev) => { wasSelected = prev.some((c) => c.firstname === customer.firstname); - if (wasSelected) { - return prev.filter((c) => c.firstname !== customer.firstname); - } + if (wasSelected) return prev.filter((c) => c.firstname !== customer.firstname); return prev; }); - - if (wasSelected) return; // remove path is done - - // Add path — compute distance, then append. + if (wasSelected) return; setLoading(true); try { const { roundedDistance, totalcharge } = await calculateDistance(customer); setDropCust((prev) => { - // Guard against parallel adds for the same row. if (prev.some((c) => c.firstname === customer.firstname)) return prev; return [...prev, { ...customer, distance: roundedDistance, totalcharge }]; }); @@ -381,9 +311,7 @@ const MultipleOrders = () => { } }; - useEffect(() => { - if (appId) fetchAppAdminTokens(); - }, [appId]); + useEffect(() => { if (appId) fetchAppAdminTokens(); }, [appId]); // ============================== sendnotifications ============================== const sendnotifications = async () => { @@ -392,15 +320,9 @@ const MultipleOrders = () => { priority: 'high', registration_ids: admintoken, data: { accessid: process.env.REACT_APP_RIDER_ACCESS_ID }, - notification: { - title: 'Nearle Merchant', - body: 'An Order has been placed successfully, kindly process the same', - sound: 'ring' - } + notification: { title: 'Nearle Merchant', body: 'An Order has been placed successfully, kindly process the same', sound: 'ring' } }); - if (res.data.message === 'Success') { - opentoast('Notification sent Successfully', 'success', 1000); - } + if (res.data.message === 'Success') opentoast('Notification sent Successfully', 'success', 1000); } catch (err) { opentoast(err.message, 'error', 1000); } @@ -423,31 +345,21 @@ const MultipleOrders = () => { receiverlongitude: 'longitude', 'itemdescription*': 'description', Quantity: 'quantity', - ' Collect Cash': 'collectionamt', - customerDeliveryTime: 'customerdeliverytime', - kitchenPickupTime: 'kitchenpickuptime' + ' Collect Cash': 'collectionamt' }; const handleFileDirectUpload = (event) => { try { const file = event.target.files?.[0]; - if (!file) { - opentoast('No file selected.', 'warning'); - return; - } + if (!file) { opentoast('No file selected.', 'warning'); return; } const fileNameLower = file.name.toLowerCase(); const isCSV = fileNameLower.endsWith('.csv'); const isExcel = fileNameLower.endsWith('.xls') || fileNameLower.endsWith('.xlsx'); - - if (!isCSV && !isExcel) { - opentoast('Invalid file type. Please upload a CSV or Excel file.', 'warning'); - return; - } + if (!isCSV && !isExcel) { opentoast('Invalid file type. Please upload a CSV or Excel file.', 'warning'); return; } const processData = (data, headers) => { const normalizedMap = {}; for (const key in headerMap) normalizedMap[normalizeHeader(key)] = headerMap[key]; - const mappedData = data.map((row) => { const newRow = {}; for (const key in row) { @@ -459,17 +371,11 @@ const MultipleOrders = () => { } return newRow; }); - const requiredCols = Object.keys(headerMap).filter((k) => k.trim().endsWith('*')); - const missingRequired = requiredCols.filter( - (clientCol) => !headers.includes(normalizeHeader(clientCol)) - ); + const missingRequired = requiredCols.filter((clientCol) => !headers.includes(normalizeHeader(clientCol))); if (missingRequired.length > 0) { - opentoast(`Missing required columns: ${missingRequired.join(', ')}`, 'warning', 3000); - setUsers([]); - return; + opentoast(`Missing columns: ${missingRequired.join(', ')}`, 'warning', 3000); } - setUsers(mappedData); opentoast('File uploaded successfully', 'success', 2000); opentoast('Press Continue to add as drop customers', 'info', 2500); @@ -477,38 +383,22 @@ const MultipleOrders = () => { if (isCSV) { Papa.parse(file, { - header: true, - dynamicTyping: true, - skipEmptyLines: true, + header: true, dynamicTyping: true, skipEmptyLines: true, complete: (results) => { - if (!results.data?.length) { - opentoast('CSV file is empty or has no valid rows.', 'warning'); - setUsers([]); - return; - } - const headers = results.meta.fields.map(normalizeHeader); - processData(results.data, headers); + if (!results.data?.length) { opentoast('CSV file is empty or has no valid rows.', 'warning'); setUsers([]); return; } + processData(results.data, results.meta.fields.map(normalizeHeader)); }, error: (error) => opentoast(`CSV parsing failed: ${error.message}`, 'warning') }); } - if (isExcel) { const reader = new FileReader(); reader.onload = (e) => { try { - const data = e.target.result; - const workbook = XLSX.read(data, { type: 'binary' }); - const firstSheet = workbook.SheetNames[0]; - const worksheet = workbook.Sheets[firstSheet]; - const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: '' }); - if (!jsonData?.length) { - opentoast('Excel file is empty or invalid.', 'warning'); - setUsers([]); - return; - } - const headers = Object.keys(jsonData[0]).map(normalizeHeader); - processData(jsonData, headers); + const workbook = XLSX.read(e.target.result, { type: 'binary' }); + const jsonData = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]], { defval: '' }); + if (!jsonData?.length) { opentoast('Excel file is empty or invalid.', 'warning'); setUsers([]); return; } + processData(jsonData, Object.keys(jsonData[0]).map(normalizeHeader)); } catch (err) { opentoast(`Error reading Excel: ${err.message}`, 'warning'); } @@ -537,44 +427,27 @@ const MultipleOrders = () => { setDropCust((prev) => prev.map((c) => (c.customerid === customerid ? { ...c, collectionamt: Number(value) || 0 } : c))); }; - // ============================== createorders ============================== + // ============================== buildDeliveryTime ============================== const buildDeliveryTime = () => { - // Prefer the parsed pickupSlot (already merged date + slot's time). - // Fall back to startdate + selectedtime when present. if (pickupSlot) { const parsed = dayjs(pickupSlot, ['YYYY-MM-DD hh:mm A', 'YYYY-MM-DD HH:mm:ss']); if (parsed.isValid()) return parsed.format('YYYY-MM-DD HH:mm:ss'); } if (startdate && selectedtime) { - const parsed = dayjs(`${dayjs(startdate).format('YYYY-MM-DD')} ${selectedtime}`, [ - 'YYYY-MM-DD hh:mm A', - 'YYYY-MM-DD HH:mm:ss' - ]); + const parsed = dayjs(`${dayjs(startdate).format('YYYY-MM-DD')} ${selectedtime}`, ['YYYY-MM-DD hh:mm A', 'YYYY-MM-DD HH:mm:ss']); if (parsed.isValid()) return parsed.format('YYYY-MM-DD HH:mm:ss'); } return dayjs().format('YYYY-MM-DD HH:mm:ss'); }; + // ============================== createorders ============================== const createorders = async () => { - if (!tenantid) { - opentoast('Choose Client', 'warning'); - return; - } - if (!pickCust) { - opentoast('Pickup location required', 'warning'); - return; - } - if (!pickupSlot) { - opentoast('Select a pickup slot', 'warning'); - return; - } - if (!dropCust.length) { - opentoast('Add at least one drop customer', 'warning'); - return; - } + if (!tenantid) { opentoast('Client not found. Please re-login.', 'warning'); return; } + if (!pickCust) { opentoast('Pickup location required', 'warning'); return; } + if (!pickupSlot) { opentoast('Select a pickup slot', 'warning'); return; } + if (!dropCust.length) { opentoast('Add at least one drop customer', 'warning'); return; } const deliverytime = buildDeliveryTime(); - const arr = dropCust.map((customer) => ({ applocationid: pickCust.applocationid, configid: 9, @@ -592,7 +465,6 @@ const MultipleOrders = () => { pickuplocationid: pickCust.locationid || 0, pickuplong: pickCust.longitude, tenantid: pickCust.tenantid, - customerid: +customer?.customerid, deliveryaddress: customer.address || '', deliverycharge: +customer.totalcharge || 0, @@ -607,14 +479,12 @@ const MultipleOrders = () => { deliverylong: customer.longitude?.toString() || '', deliverytime, deliverytype: 'B', - itemcount: 1, quantity: customer.quantity, collectionamt: customer.collectionamt, kms: customer.distance?.toString() || '0', locationid: +pickCust.locationid, moduleid: +pickCust.moduleid, - orderamount: +customer.totalcharge || 0, ordercharges: 0.0, orderdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), @@ -636,13 +506,8 @@ const MultipleOrders = () => { opentoast(res?.data?.message || 'Order creation failed. Please try again.', 'warning', 3000); } } catch (err) { - let toastMessage = 'Something went wrong. Please try again.'; - if (err.response) { - toastMessage = err.response.data?.message || `Server error (${err.response.status})`; - } else if (err.request) { - toastMessage = 'Network error. Check your internet connection.'; - } - opentoast(toastMessage, 'error', 3000); + const msg = err.response ? err.response.data?.message || `Server error (${err.response.status})` : err.request ? 'Network error. Check your internet connection.' : 'Something went wrong.'; + opentoast(msg, 'error', 3000); } finally { setLoading(false); setBtnLoading(false); @@ -650,21 +515,8 @@ const MultipleOrders = () => { }; // ============================== derived ============================== - const stepsComplete = { - location: !!appId, - client: !!tenantid, - business: !!locationid, - schedule: !!pickupSlot, - drops: dropCust.length > 0 - }; - const canSubmit = - stepsComplete.location && stepsComplete.client && stepsComplete.business && stepsComplete.schedule && stepsComplete.drops; - - // ============================== preview helpers ============================== - // The right pane shows three exclusive views: - // 1. dropCust.length > 0 → "Drop List" with calculated charges + remove - // 2. users.length > 0 → raw "File Preview" of parsed rows, awaiting Continue - // 3. else → empty state + const canSubmit = !!locationid && !!pickupSlot && dropCust.length > 0; + const prereqOk = !!locationid; const previewMode = dropCust.length > 0 ? 'drops' : users.length > 0 ? 'preview' : 'empty'; // ============================== render ============================== @@ -675,7 +527,6 @@ const MultipleOrders = () => { - {/* Outer viewport-locked shell — page never scrolls; panes scroll internally. */} { gap: 1 }} > - {/* Thin title bar */} - + {/* Title bar */} + Create Multiple Orders @@ -703,9 +548,7 @@ const MultipleOrders = () => { size="small" label={`${dropCust.length} drop${dropCust.length === 1 ? '' : 's'}`} sx={{ - height: 20, - fontSize: 10.5, - fontWeight: 700, + height: 20, fontSize: 10.5, fontWeight: 700, bgcolor: dropCust.length ? 'rgba(24,144,255,0.10)' : '#f1f5f9', color: dropCust.length ? '#1890ff' : '#94a3b8', border: `1px solid ${dropCust.length ? 'rgba(24,144,255,0.25)' : '#e2e8f0'}` @@ -717,155 +560,57 @@ const MultipleOrders = () => { - {/* ============================== 50 / 50 workspace ============================== */} - - {/* ============================== LEFT 50% : Input fields ============================== */} + {/* 50 / 50 workspace */} + + + {/* LEFT — input fields */} - {/* Card: Setup (Location / Client / Business) */} + {/* Card: Schedule & Pickup */} - + - Setup + Schedule & Pickup - - `${option.locationname}`} - onChange={(event, value, reason) => { - if (reason === 'clear') setAppId(0); - else if (value) setAppId(value.applocationid); - }} - renderInput={(params) => ( - - - {params.InputProps.startAdornment} - - ) - }} - /> - )} - /> - - - option?.tenantname || ''} - isOptionEqualToValue={(option, value) => option.tenantid === value.tenantid} - onOpen={(event) => { - if (!appId) { - event.preventDefault(); - OpenToast('Please select Location first!', 'warning', 3000); - setTimeout(() => locationRef.current?.focus(), 0); - } - }} - onChange={(e, val, reason) => { - if (reason === 'clear') { - setTenantid(0); - setTenantValue(null); - } else if (val) { - setTenantid(val.tenantid); - setTenantValue(val); - fetchTenantPricing(val.tenantid); - gettenantlocations(val.tenantid); - } - }} - renderInput={(params) => ( - - - {params.InputProps.startAdornment} - - ) - }} - /> - )} - /> - - - {tenantLocations.length === 1 ? ( + + {tenantLocations?.length === 1 ? ( + + + ) }} + sx={{ + '& .MuiOutlinedInput-root': { borderRadius: '10px', height: '36px', alignItems: 'center' }, + '& .MuiOutlinedInput-input': { padding: '0 14px !important' } + }} /> ) : ( - option?.locationname ? `${option.locationname} (${option.suburb || ''})` : '' - } - onOpen={(event) => { - if (!appId && !tenantid) { - event.preventDefault(); - OpenToast('Please select Location and Client first!', 'warning', 3000); - setTimeout(() => locationRef.current?.focus(), 0); - } else if (!tenantid) { - event.preventDefault(); - OpenToast('Please select Client first!', 'warning', 3000); - setTimeout(() => tenantRef.current?.focus(), 0); - } - }} + getOptionLabel={(option) => option ? `${option.locationname} (${option.suburb || option.locationsuburb || ''})` : ''} + value={locationValue} onChange={(event, value, reason) => { if (reason === 'clear' || !value) { setLocationid(0); @@ -876,200 +621,126 @@ const MultipleOrders = () => { setLocationid(value.locationid || 0); setLocationValue(value); setPickCust(value); - setPickupSlotsList(value?.slots); + setPickupSlotsList(value?.slots || null); } }} renderInput={(params) => ( - - {params.InputProps.startAdornment} - - ) - }} /> )} + sx={{ + '& .MuiOutlinedInput-root': { borderRadius: '10px', height: '36px', alignItems: 'center', padding: '0 9px !important' }, + '& .MuiAutocomplete-input': { padding: '0 !important' } + }} /> )} + + + { + if (!e || !dayjs(e).isValid()) { setStartdate(dayjs().format('MM-DD-YYYY')); return; } + const diffDays = dayjs().diff(dayjs(dayjs(e).format('YYYY-MM-DD')), 'd'); + if (diffDays <= 0) { + setStartdate(dayjs(e).format('MM-DD-YYYY')); + setSelectedtime(''); + setPickupSlot(null); + } else { + opentoast('Choose an upcoming date', 'warning'); + setStartdate(dayjs().format('MM-DD-YYYY')); + } + }} + disablePast + slotProps={{ + textField: { + size: 'small', fullWidth: true, InputLabelProps: { shrink: true }, + InputProps: { + startAdornment: ( + + + + ) + }, + sx: { '& .MuiOutlinedInput-root': { borderRadius: '10px', height: '36px' } } + } + }} + /> + + + + { + if (!locationid) { + OpenToast('Please select a Business Location first.', 'warning', 3000); + return; + } + setSlotDropdownOpen(true); + }} + onClose={() => setSlotDropdownOpen(false)} + sx={{ + '& .MuiOutlinedInput-root': { borderRadius: '10px', height: '36px', alignItems: 'center', padding: '0 9px !important' }, + '& .MuiAutocomplete-input': { padding: '0 !important' } + }} + onChange={(e, newValue, reason) => { + if (reason === 'clear' || !newValue) { setSelectedtime(null); setPickupSlot(null); return; } + if (!newValue.time) { OpenToast('This slot has no time configured.', 'warning', 3000); return; } + const formattedTime = dayjs(newValue.time, 'HH:mm').format('hh:mm A'); + setSelectedtime(formattedTime); + const finalDateTime = dayjs(`${startdate} ${formattedTime}`, 'MM-DD-YYYY hh:mm A').format('YYYY-MM-DD hh:mm A'); + setPickupSlot(finalDateTime); + }} + getOptionLabel={(option) => option ? `${option.name} (${dayjs(option.time, 'HH:mm').format('hh:mm A')})` : ''} + renderInput={(params) => ( + + + + ) + }} + /> + )} + /> + + + {pickCust ? ( + + + + + + + {pickCust.locationname || '—'} + + + {pickCust.address || '—'} + + + + ) : ( + + Pickup auto-fills once a Business Location is selected. + + )} + - {/* Card: Schedule + Pickup display */} - - - - Schedule & Pickup - - - - - - { - if (!e || !dayjs(e).isValid()) { - setStartdate(dayjs().format('MM-DD-YYYY')); - return; - } - const diffDays = dayjs().diff(dayjs(`${dayjs(e).format('YYYY-MM-DD')}`), 'd'); - if (diffDays <= 0) { - setStartdate(dayjs(e).format('MM-DD-YYYY')); - setSelectedtime(''); - setPickupSlot(null); - } else { - opentoast('Choose an upcoming date', 'warning'); - setStartdate(dayjs().format('MM-DD-YYYY')); - } - }} - disablePast - slotProps={{ - textField: { - size: 'small', - fullWidth: true, - InputLabelProps: { shrink: true }, - InputProps: { - startAdornment: ( - - - - ) - }, - sx: { - '& .MuiOutlinedInput-root': { - borderRadius: '10px', - height: '36px', - paddingLeft: '10px' - } - } - } - }} - /> - - - - { - if (reason === 'clear' || !newValue) { - setSelectedtime(null); - setPickupSlot(null); - return; - } - if (!newValue.time) { - OpenToast('This slot has no time configured.', 'warning', 3000); - return; - } - const formattedTime = dayjs(newValue.time, 'HH:mm').format('hh:mm A'); - setSelectedtime(formattedTime); - const finalDateTime = dayjs( - `${startdate} ${formattedTime}`, - 'MM-DD-YYYY hh:mm A' - ).format('YYYY-MM-DD hh:mm A'); - setPickupSlot(finalDateTime); - }} - getOptionLabel={(option) => - option ? `${option.name} (${dayjs(option.time, 'HH:mm').format('hh:mm A')})` : '' - } - renderInput={(params) => ( - - - {params.InputProps.startAdornment} - - ) - }} - /> - )} - /> - - - {/* Pickup display (compact, single row) */} - - {pickCust ? ( - - - - - - - {pickCust.locationname || '—'} - - - {pickCust.address || '—'} - - - - ) : ( - - Pickup auto-fills once a Business Location is selected. - - )} - - - - - - {/* Card: Notes */} @@ -1083,26 +754,13 @@ const MultipleOrders = () => { Special Dispatch Notes setOtherinstructions(e.target.value)} sx={{ - '& .MuiOutlinedInput-root': { - borderRadius: '10px', - padding: '0 10px', - alignItems: 'center', - fontSize: '12px', - background: '#ffffff', - height: '32px' - }, - '& .MuiOutlinedInput-input': { - padding: '0 !important', - fontSize: '12px !important', - lineHeight: '32px' - } + '& .MuiOutlinedInput-root': { borderRadius: '10px', height: '36px', alignItems: 'center', fontSize: '12px', background: '#ffffff' }, + '& .MuiOutlinedInput-input': { padding: '0 14px !important', fontSize: '12px' } }} /> @@ -1112,106 +770,27 @@ const MultipleOrders = () => { {/* Card: Summary + Submit */} - - Bulk Summary - - - Live totals - + Bulk Summary + Live totals {(() => { const metric = ({ icon: Icon, label, value, accent, active }) => ( - - + + - - {label} - - - {value} - + {label} + {value} ); return ( - - {metric({ - icon: FaRoute, - label: 'Distance', - value: totaldist ? `${totaldist} km` : '—', - accent: '#1890ff', - active: !!totaldist - })} - - - {metric({ - icon: FaBoxes, - label: 'Quantity', - value: totalQty || 0, - accent: '#16a34a', - active: !!totalQty - })} - - - {metric({ - icon: FaMoneyBillWave, - label: 'Cash Collect', - value: `₹${Number(totalCash).toFixed(2)}`, - accent: '#d97706', - active: !!totalCash - })} - - - {metric({ - icon: FaTruck, - label: 'Deliveries', - value: dropCust.length, - accent: '#65387a', - active: dropCust.length > 0 - })} - + {metric({ icon: FaRoute, label: 'Distance', value: totaldist ? `${totaldist} km` : '—', accent: '#1890ff', active: !!totaldist })} + {metric({ icon: FaBoxes, label: 'Quantity', value: totalQty || 0, accent: '#16a34a', active: !!totalQty })} + {metric({ icon: FaMoneyBillWave, label: 'Cash Collect', value: `₹${Number(totalCash).toFixed(2)}`, accent: '#d97706', active: !!totalCash })} + {metric({ icon: FaTruck, label: 'Deliveries', value: dropCust.length, accent: '#65387a', active: dropCust.length > 0 })} ); })()} @@ -1222,162 +801,65 @@ const MultipleOrders = () => {
    Total Charge
    -
    - ₹{Number(totalAmt).toFixed(2)} -
    +
    ₹{Number(totalAmt).toFixed(2)}
    )} - {/* ============================== RIGHT 50% : File / Drop Preview ============================== */} - - - {/* Preview header (sticky inside card) */} - + {/* RIGHT — file / drop preview */} + + + {/* Preview header */} + - {previewMode === 'drops' - ? `Drop List` - : previewMode === 'preview' - ? `File Preview` - : `Preview`} + {previewMode === 'drops' ? 'Drop List' : previewMode === 'preview' ? 'File Preview' : 'Preview'} {previewMode === 'drops' && ( - + )} {previewMode === 'preview' && ( - } - sx={{ - height: 22, - fontWeight: 700, - fontSize: 11, - bgcolor: 'rgba(245,158,11,0.12)', - color: '#b45309', - border: '1px solid rgba(245,158,11,0.30)', - '& .MuiChip-icon': { color: '#b45309' } - }} + } + sx={{ height: 22, fontWeight: 700, fontSize: 11, bgcolor: 'rgba(245,158,11,0.12)', color: '#b45309', border: '1px solid rgba(245,158,11,0.30)', '& .MuiChip-icon': { color: '#b45309' } }} /> )} {previewMode !== 'empty' && (() => { - const prereqOk = appId && tenantid && locationid; const handleHeaderPick = (val) => { - if (!prereqOk) { - OpenToast('Please select Location, Client, and Business Location first.', 'warning', 3000); - return; - } + if (!prereqOk) { OpenToast('Please select a Business Location first.', 'warning', 3000); return; } setUploadType(val); - if (val === 0) { - document.getElementById('upload-file')?.click(); - } else if (val === 1) { - setIsCustomerOpen(true); - setSearchCustList(''); - } + if (val === 0) document.getElementById('upload-file')?.click(); + else if (val === 1) { setIsCustomerOpen(true); setSearchCustList(''); } }; return ( - - @@ -1386,65 +868,24 @@ const MultipleOrders = () => { {fileName && ( - + - - {fileName} - + {fileName} )} {previewMode === 'preview' && users.length >= 1 && ( - + - - Process & Calculate Distances - - - Click continue to import spreadsheet rows and calculate drop charges. - + Process & Calculate Distances + Click continue to import spreadsheet rows and calculate drop charges. - @@ -1452,157 +893,38 @@ const MultipleOrders = () => { {/* Scrollable preview body */} - {previewMode === 'drops' && isMobile && ( - - {dropCust.map((customer, index) => ( - - - - {index + 1}. {customer.firstname} - - - {customer.address} - - - - handleCheckboxChange1(customer)} - sx={{ color: '#ef4444', p: 0.5, flexShrink: 0 }} - > - - - - - } - > - - - {uploadType === 0 ? ( - - {customer.quantity ?? '—'} - - ) : ( - handleQuantityChange(customer.customerid, e.target.value)} - inputProps={{ min: 0 }} - fullWidth - sx={{ '& .MuiOutlinedInput-root': { borderRadius: '8px', height: 34 } }} - /> - )} - - - {uploadType === 0 ? ( - - {`₹${Number(customer.collectionamt || 0).toFixed(2)}`} - - ) : ( - { - const v = Number(e.target.value); - handleCollectionAmtChange(customer.customerid, v > 0 ? v : 0); - }} - inputProps={{ min: 0 }} - InputProps={{ - startAdornment: - }} - fullWidth - sx={{ '& .MuiOutlinedInput-root': { borderRadius: '8px', height: 34 } }} - /> - )} - - - - - ₹{Number(customer?.totalcharge || 0).toFixed(2)} - - - - - ))} - - - - - - - - ₹{Number(totalAmt).toFixed(2)} - - - - - - )} - {previewMode === 'drops' && !isMobile && ( - + {/* Drops — table */} + {previewMode === 'drops' && ( + # Customer Address - - Qty - - - Cash - + Qty + Cash Km - - Charge - - - {' '} - + Charge + {dropCust.map((customer, index) => ( {index + 1} - - {customer.firstname} - + {customer.firstname} - + {customer.address} - {uploadType === 0 ? ( - customer.quantity - ) : ( - handleQuantityChange(customer.customerid, e.target.value)} inputProps={{ min: 0 }} sx={{ width: 64, '& .MuiOutlinedInput-root': { borderRadius: '8px', height: 30 } }} @@ -1610,37 +932,20 @@ const MultipleOrders = () => { )} - {uploadType === 0 ? ( - `₹${Number(customer.collectionamt || 0).toFixed(2)}` - ) : ( - { - const v = Number(e.target.value); - handleCollectionAmtChange(customer.customerid, v > 0 ? v : 0); - }} + {uploadType === 0 ? `₹${Number(customer.collectionamt || 0).toFixed(2)}` : ( + { const v = Number(e.target.value); handleCollectionAmtChange(customer.customerid, v > 0 ? v : 0); }} inputProps={{ min: 0 }} - InputProps={{ - startAdornment: - }} + InputProps={{ startAdornment: }} sx={{ width: 90, '& .MuiOutlinedInput-root': { borderRadius: '8px', height: 30 } }} /> )} {customer.distance} - - ₹{Number(customer?.totalcharge || 0).toFixed(2)} - + ₹{Number(customer?.totalcharge || 0).toFixed(2)} - handleCheckboxChange1(customer)} - sx={{ color: '#ef4444', p: 0.5 }} - > + handleCheckboxChange1(customer)} sx={{ color: '#ef4444', p: 0.5 }}> @@ -1650,16 +955,10 @@ const MultipleOrders = () => { Total - - {totalQty} - - - ₹{Number(totalCash).toFixed(2)} - + {totalQty} + ₹{Number(totalCash).toFixed(2)} {totaldist} - - ₹{Number(totalAmt).toFixed(2)} - + ₹{Number(totalAmt).toFixed(2)} @@ -1667,41 +966,9 @@ const MultipleOrders = () => { )} - {previewMode === 'preview' && isMobile && ( - - {users.map((u, i) => ( - - - {i + 1}. {u.firstname || '—'} - - - {u.address || '—'} - - - } - > - - - - - - - ))} - - )} - - {previewMode === 'preview' && !isMobile && ( - + {/* File preview — table */} + {previewMode === 'preview' && ( +
    @@ -1709,44 +976,25 @@ const MultipleOrders = () => { Name Contact Address - - Qty - - - Cash - + Qty + Cash {users.map((u, i) => ( {i + 1} - - {u.firstname || '—'} - + {u.firstname || '—'} {u.contactno || '—'} - + {u.address || '—'} - - {u.quantity ?? '—'} - - - {u.collectionamt != null ? `₹${Number(u.collectionamt).toFixed(2)}` : '—'} - + {u.quantity ?? '—'} + {u.collectionamt != null ? `₹${Number(u.collectionamt).toFixed(2)}` : '—'} ))} @@ -1754,141 +1002,44 @@ const MultipleOrders = () => { )} + {/* Empty state */} {previewMode === 'empty' && (() => { - const prereqOk = appId && tenantid && locationid; const handleEmptyPick = (val) => { - if (!prereqOk) { - OpenToast('Please select Location, Client, and Business Location first.', 'warning', 3000); - return; - } + if (!prereqOk) { OpenToast('Please select a Business Location first.', 'warning', 3000); return; } setUploadType(val); - setDropCust([]); - setUsers([]); - setFileName(''); - if (val === 0) { - document.getElementById('upload-file')?.click(); - } else if (val === 1) { - setIsCustomerOpen(true); - setSearchCustList(''); - } + setDropCust([]); setUsers([]); setFileName(''); + if (val === 0) document.getElementById('upload-file')?.click(); + else if (val === 1) { setIsCustomerOpen(true); setSearchCustList(''); } }; - - const tile = ({ value, icon: Icon, title, sub, accent }) => { - return ( - handleEmptyPick(value)} - onKeyDown={(e) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - handleEmptyPick(value); - } - }} - sx={{ - flex: 1, - minWidth: 0, - display: 'flex', - alignItems: 'center', - textAlign: 'left', - gap: 1.25, - px: 1.5, - py: 1.25, - borderRadius: '10px', - border: `1.5px solid #eef2f6`, - bgcolor: '#fff', - opacity: prereqOk ? 1 : 0.6, - cursor: prereqOk ? 'pointer' : 'not-allowed', - transition: 'all 0.18s ease', - '&:hover': prereqOk - ? { - borderColor: accent, - boxShadow: `0 4px 12px -4px ${accent}40`, - transform: 'translateY(-1px)' - } - : undefined - }} - > - - - - - - {title} - - - {sub} - - - - ); - }; - - return ( - ( + handleEmptyPick(value)} + onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleEmptyPick(value); } }} + sx={{ flex: 1, minWidth: 0, display: 'flex', alignItems: 'center', textAlign: 'left', gap: 1.25, px: 1.5, py: 1.25, borderRadius: '10px', border: '1.5px solid #eef2f6', bgcolor: '#fff', opacity: prereqOk ? 1 : 0.6, cursor: prereqOk ? 'pointer' : 'not-allowed', transition: 'all 0.18s ease', '&:hover': prereqOk ? { borderColor: accent, boxShadow: `0 4px 12px -4px ${accent}40`, transform: 'translateY(-1px)' } : undefined }} > - + + + + + {title} + {sub} + + + ); + return ( + + - - Choose a Drop Source to begin - + Choose a Drop Source to begin - Select a source from below or the left panel to import or pick your delivery customers. + Select a source below to import or pick your delivery customers. - - {tile({ - value: 0, - icon: MdOutlineCloudUpload, - title: 'Excel / CSV', - sub: 'Bulk upload a sheet', - accent: '#1890ff' - })} - {tile({ - value: 1, - icon: FaUsers, - title: 'Selection', - sub: 'Pick saved customers', - accent: '#65387a' - })} + {tile({ value: 0, icon: MdOutlineCloudUpload, title: 'Excel / CSV', sub: 'Bulk upload a sheet', accent: '#1890ff' })} + {tile({ value: 1, icon: FaUsers, title: 'Selection', sub: 'Pick saved customers', accent: '#65387a' })} ); @@ -1899,26 +1050,13 @@ const MultipleOrders = () => { - + - {/* ============================== Saved customers dialog ============================== */} + {/* Saved customers dialog */} setIsCustomerOpen(false)} - fullWidth - fullScreen={isMobile} - sx={{ - '& .MuiDialog-paper': { - borderRadius: { xs: 0, sm: '16px' }, - overflow: 'hidden' - } - }} + open={isCustomerOpen} onClose={() => setIsCustomerOpen(false)} + fullWidth fullScreen={isMobile} + sx={{ '& .MuiDialog-paper': { borderRadius: { xs: 0, sm: '16px' }, overflow: 'hidden' } }} > @@ -1927,30 +1065,11 @@ const MultipleOrders = () => { setSearchCustList(e.target.value)} - sx={{ - bgcolor: 'white', - borderRadius: '10px', - '& .MuiOutlinedInput-input': { p: '10px 14px' } - }} - startAdornment={ - - - - } - endAdornment={ - setSearchCustList('')} - > - - - } + autoFocus fullWidth id="input-search-header" placeholder="Search by name, contact, or address…" + value={searchCustList} onChange={(e) => setSearchCustList(e.target.value)} + sx={{ bgcolor: 'white', borderRadius: '10px', '& .MuiOutlinedInput-input': { p: '10px 14px' } }} + startAdornment={} + endAdornment={ setSearchCustList('')}>} autoComplete="off" /> @@ -1969,27 +1088,14 @@ const MultipleOrders = () => { return ( handleCheckboxChange(event, customer)} - /> - } + sx={{ m: 0, py: 1, px: 1.25, borderRadius: '10px', '&:hover': { bgcolor: 'rgba(24,144,255,0.04)' } }} + control={ handleCheckboxChange(event, customer)} />} label={ {customer.firstname} ({customer.contactno}) - - {customer.address} - + {customer.address} } /> @@ -2002,15 +1108,9 @@ const MultipleOrders = () => { diff --git a/src/pages/nearle/orders/multipleorders_copy.js b/src/pages/nearle/orders/multipleorders_copy.js index 6d67847..dcd5e9b 100644 --- a/src/pages/nearle/orders/multipleorders_copy.js +++ b/src/pages/nearle/orders/multipleorders_copy.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ import React from 'react'; import Loader from 'components/Loader'; import { useEffect, useState, useRef, Fragment } from 'react'; @@ -506,7 +507,7 @@ const MultipleOrders = () => { accessid: process.env.REACT_APP_RIDER_ACCESS_ID }, notification: { - title: 'Nearle Merchant', + title: 'Doormile Merchant', body: 'An Order has been placed successfully,kindly process the same', sound: 'ring' } diff --git a/src/pages/nearle/orders/newcreateOrder.js b/src/pages/nearle/orders/newcreateOrder.js index e137ad3..d1726cc 100644 --- a/src/pages/nearle/orders/newcreateOrder.js +++ b/src/pages/nearle/orders/newcreateOrder.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ import * as React from 'react'; import { useEffect, useState, useRef, Fragment } from 'react'; import { @@ -786,7 +787,7 @@ const Createorder1 = () => { // notifyadmin(admintoken); sendnotifications(); } - navigate('/nearle/orders'); + navigate('/doormile/orders'); } else { opentoast('Error in creating orders', 'warning'); } @@ -837,7 +838,7 @@ const Createorder1 = () => { accessid: process.env.REACT_APP_RIDER_ACCESS_ID }, notification: { - title: 'Nearle Merchant', + title: 'Doormile Merchant', body: 'An Order has been placed successfully,kindly process the same', sound: 'ring' } diff --git a/src/pages/nearle/orders/optimisedOrderPreview.js b/src/pages/nearle/orders/optimisedOrderPreview.js index a80efe4..2071f6f 100644 --- a/src/pages/nearle/orders/optimisedOrderPreview.js +++ b/src/pages/nearle/orders/optimisedOrderPreview.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ import { Autocomplete, Button, @@ -200,7 +201,7 @@ const OptimisedOrderPreview = () => { onSettled: () => { setTimeout(() => { setIsLoading(false); - navigate('/nearle/deliveries'); + navigate('/doormile/deliveries'); }, 2000); } }); @@ -279,7 +280,7 @@ const OptimisedOrderPreview = () => { navigate('/nearle/orders')} + onClick={() => navigate('/doormile/orders')} sx={{ bgcolor: 'action.hover', '&:hover': { bgcolor: 'action.selected' } @@ -419,7 +420,7 @@ const OptimisedOrderPreview = () => { {orders.map((val, i) => { const typeAccent = - val.ordertype === 'Economy' ? '#10b981' : val.ordertype === 'Risky' ? '#ef4444' : '#662582'; + val.ordertype === 'Economy' ? '#10b981' : val.ordertype === 'Risky' ? '#ef4444' : '#C01227'; return ( @@ -719,7 +720,7 @@ const OptimisedOrderPreview = () => { color="secondary" startIcon={} onClick={() => { - navigate('/nearle/orders'); + navigate('/doormile/orders'); }} > Back diff --git a/src/pages/nearle/orders/orders.js b/src/pages/nearle/orders/orders.js index 9aaab12..7be13bf 100644 --- a/src/pages/nearle/orders/orders.js +++ b/src/pages/nearle/orders/orders.js @@ -1,73 +1,20 @@ +/* eslint-disable no-unused-vars */ import * as React from 'react'; +import { enqueueSnackbar } from 'notistack'; import { useState, useEffect, Fragment, useRef } from 'react'; -import { DashboardOutlined, DeleteFilled, DeleteOutlined, ShoppingCartOutlined } from '@ant-design/icons'; -import { Empty } from 'antd'; import dayjs from 'dayjs'; -import { useQuery, useMutation, useInfiniteQuery } from '@tanstack/react-query'; -import HoverSocialCard from 'components/cards/statistics/HoverSocialCard'; -import { useTheme } from '@mui/material/styles'; -import { CloseOutlined } from '@ant-design/icons'; -import { PopupTransition } from 'components/@extended/Transitions'; -import CircularLoader from 'components/CircularLoader'; -import Loader from 'components/Loader'; -import { KeyboardArrowDownOutlined, KeyboardArrowUpOutlined } from '@mui/icons-material'; - -import { PiMapPinLineDuotone } from 'react-icons/pi'; -import { - MdOutlineDateRange, - MdPersonOff, - MdEventBusy, - MdLocalShipping, - MdHourglassEmpty, - MdCheckCircle, - MdCancel, - MdOutlineReceiptLong, - MdOutlinePendingActions, - MdOutlineCheckCircle, - MdOutlineCancel, - MdMyLocation, - MdGroups, - MdPlace, - MdStraighten, - MdCurrencyRupee, - MdInventory2, - MdHistoryToggleOff, - MdCalendarMonth -} from 'react-icons/md'; -import { VscArchive } from 'react-icons/vsc'; -import DateFilterDialog from 'components/DateFilterDialog'; -import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar'; -import TitleCard from 'components/nearle_components/TitleCard'; -import MainCard from 'components/MainCard'; -import { OpenToast } from 'components/third-party/OpenToast'; -import { OrdersTableSkeleton } from './OrdersTableSkeleton'; -import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete'; -import PageHeader from 'components/nearle_components/PageHeader'; -import StatCard from 'components/nearle_components/StatCard'; -import AiImage from '../../../assets/images/aiImage.png'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import EnergySavingsLeafIcon from '@mui/icons-material/EnergySavingsLeaf'; -import DirectionsBikeOutlinedIcon from '@mui/icons-material/DirectionsBikeOutlined'; -import WarningIcon from '@mui/icons-material/Warning'; -import BoltIcon from '@mui/icons-material/Bolt'; -import { ArrowRightAltOutlined } from '@mui/icons-material'; -import { HiOutlineArrowLeft } from 'react-icons/hi'; - -import { IoReload } from 'react-icons/io5'; - var utc = require('dayjs/plugin/utc'); dayjs.extend(utc); +import axios from 'axios'; import { Avatar, + Box, Button, Grid, - Tabs, - Tab, IconButton, + Paper, Stack, - TextField, - Chip, Typography, Table, TableCell, @@ -76,60 +23,60 @@ import { Dialog, TableRow, DialogContent, - DialogTitle, Tooltip, - DialogActions, - Autocomplete, - TableContainer, - Checkbox, - useMediaQuery, + Skeleton, + CircularProgress, + InputBase, Backdrop, SpeedDial, SpeedDialIcon, SpeedDialAction, Badge, - Divider, - AccordionDetails, - AccordionSummary, - Accordion, - Paper, - Box + TableContainer, + Checkbox } from '@mui/material'; - import { - fetchOrders, - fetchorderscount, + MdAccessTime, + MdCancel, + MdCheckCircle, + MdClose, + MdCurrencyRupee, + MdDeleteOutline, + MdHistoryToggleOff, + MdHourglassEmpty, + MdInventory2, + MdLocalShipping, + MdSearch, + MdStraighten, + MdCalendarMonth, + MdReceiptLong, + MdClear, + MdNotes +} from 'react-icons/md'; +import { DeleteOutlined } from '@ant-design/icons'; +import Loader from 'components/Loader'; +import DateFilterDialog from 'components/DateFilterDialog'; +import CircularLoader from 'components/CircularLoader'; +import AiImage from '../../../assets/images/aiImage.png'; +import { useQuery, useMutation, useInfiniteQuery } from '@tanstack/react-query'; +import { useNavigate, useLocation } from 'react-router-dom'; +import { fetchPercentageData, - createOptimisationDeliveries, - // notifyRider, - cancelOrder, - getTenants, - gettenantlocations, - cancelMultipleOrder, createAutomationDeliveries, - fetchPaymentType, - fetchRidersList, - finalCreatedeliveries, - notifyRider, - getallriders + cancelMultipleOrder, + getallriders, + fetchorderscount } from '../../api/api'; -import LoaderWithImage from 'components/nearle_components/LoaderWithImage'; -import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; -import { useNavigate } from 'react-router'; -import CSVExport from 'components/third-party/ReactTable'; -import Dispatch from '../dispatch/Dispatch'; -// import usePreventReload from 'hooks/usePreventReload'; // ============================================================================ -// Design tokens — shared with deliveries / tenants / customers / pricing / -// invoice / orders-details / riders-summary pages. +// Design tokens — shared with the rest of the redesigned operator pages. // ============================================================================ const DT = { radiusPill: 999, - radiusCard: 14, - shadowSoft: '0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.05)', - shadowMd: '0 1px 3px rgba(15, 23, 42, 0.06)', - shadowPop: '0 12px 32px rgba(15, 23, 42, 0.12)', + radiusCard: 16, + shadowSoft: '0 14px 40px rgba(15, 23, 42, 0.10)', + shadowMd: '0 8px 24px rgba(15, 23, 42, 0.08)', + shadowPop: '0 18px 50px rgba(15, 23, 42, 0.18)', textPrimary: '#0f172a', textSecondary: '#64748b', textMuted: '#94a3b8', @@ -139,311 +86,177 @@ const DT = { surfaceAlt: '#f8fafc' }; const dtA = (c, suffix) => `${c}${suffix}`; -const dtTint = (c) => dtA(c, '08'); -const dtSoft = (c) => dtA(c, '18'); -const dtRing = (c) => dtA(c, '26'); -const dtEdge = (c) => dtA(c, '55'); +const tint = (c) => dtA(c, '08'); +const soft = (c) => dtA(c, '18'); +const ring = (c) => dtA(c, '26'); +const edge = (c) => dtA(c, '55'); -const BRAND = '#662582'; +const dtTint = tint; +const dtSoft = soft; +const dtRing = ring; +const dtEdge = edge; -const SoftPaper = (props) => ( - -); +const BRAND = '#C01227'; +const BRAND_LIGHT = '#D25463'; -const DTAccentAvatar = ({ color, selected, size = 24, children }) => ( - - {children} - -); -const pillFieldSx = () => ({ - '& .MuiOutlinedInput-root': { - borderRadius: '10px', - bgcolor: '#ffffff', - fontWeight: 600, - '& fieldset': { borderColor: '#e2e8f0', borderWidth: 1 }, - '&:hover fieldset': { borderColor: '#cbd5e1' }, - '&.Mui-focused': { boxShadow: `0 0 0 3px ${dtRing('#662582')}` }, - '&.Mui-focused fieldset': { borderColor: '#662582', borderWidth: 1.5 } - } -}); - -// Order row status meta — semantic palette for the per-row status badge. +// Semantic per-row status palette — colors per brand standard: +// green=delivered, amber=pending, blue=created/processing, red=cancelled, +// dark-red=failed, purple=on-hold. const ROW_STATUS_META = { - created: { label: 'Created', color: '#0ea5e9', icon: MdLocalShipping }, - pending: { label: 'Pending', color: '#f59e0b', icon: MdHourglassEmpty }, - processing:{ label: 'Processing',color: '#0ea5e9', icon: MdHistoryToggleOff }, - modified: { label: 'Modified', color: '#06b6d4', icon: MdHistoryToggleOff }, - confirmed: { label: 'Confirmed', color: '#10b981', icon: MdCheckCircle }, - accepted: { label: 'Accepted', color: '#6366f1', icon: MdCheckCircle }, - delivered: { label: 'Delivered', color: '#10b981', icon: MdCheckCircle }, - cancelled: { label: 'Cancelled', color: '#ef4444', icon: MdCancel } + created: { label: 'Created', color: '#3b82f6', icon: MdLocalShipping }, + pending: { label: 'Pending', color: '#f59e0b', icon: MdHourglassEmpty }, + processing: { label: 'Processing', color: '#3b82f6', icon: MdAccessTime }, + modified: { label: 'Confirmed', color: '#10b981', icon: MdCheckCircle }, + confirmed: { label: 'Confirmed', color: '#10b981', icon: MdCheckCircle }, + ready: { label: 'Accepted', color: '#6366f1', icon: MdCheckCircle }, + active: { label: 'Picked', color: '#8b5cf6', icon: MdLocalShipping }, + onhold: { label: 'On Hold', color: '#8b5cf6', icon: MdHistoryToggleOff }, + closed: { label: 'Closed', color: '#06b6d4', icon: MdCheckCircle }, + delivered: { label: 'Delivered', color: '#10b981', icon: MdCheckCircle }, + failed: { label: 'Failed', color: '#991b1b', icon: MdCancel }, + cancelled: { label: 'Cancelled', color: '#ef4444', icon: MdCancel } }; -// Tab pill meta — controls the 4 top-level status tabs. +// Top-level pill tabs. const ORDERS_STATUS_TABS = [ - { idx: 0, status: 'created', label: 'Created', color: BRAND, icon: MdLocalShipping, countKey: 'created' }, - { idx: 1, status: 'pending', label: 'Pending', color: '#f59e0b', icon: MdHourglassEmpty, countKey: 'pending' }, - { idx: 2, status: 'delivered', label: 'Delivered', color: '#10b981', icon: MdCheckCircle, countKey: 'delivered' }, - { idx: 3, status: 'cancelled', label: 'Cancelled', color: '#ef4444', icon: MdCancel, countKey: 'cancelled' } + { idx: 0, status: 'created', label: 'Created', color: BRAND, icon: MdLocalShipping, countKey: 'created' } ]; -const formatOrderDate = (row) => { - const dateCandidates = [row.pickupslot, row.deliverydate, row.orderdate]; - for (const candidate of dateCandidates) { - if (candidate && dayjs(candidate).isValid()) { - return `${dayjs(candidate).utc().format('DD/MM/YYYY')} ${dayjs(candidate).format('hh:mm A')}`; - } +const StatusBadge = ({ status }) => { + const meta = ROW_STATUS_META[String(status || '').toLowerCase()] || { + label: status || '—', + color: DT.textMuted, + icon: MdHistoryToggleOff + }; + const Icon = meta.icon; + return ( + + {meta.label} + + ); +}; + +const MetricCell = ({ value, color, icon, isMoney = false }) => { + const n = Number(value); + const display = isMoney ? `₹${Number.isFinite(n) ? n.toFixed(2) : '0.00'}` : Number.isFinite(n) ? n : value || 0; + const isZero = !Number.isFinite(n) || n === 0; + if (isZero) { + return ( + + {display} + + ); } - return '—'; + return ( + + {icon} + {display} + + ); }; const Orders = () => { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('md')); const navigate = useNavigate(); + const location = useLocation(); + const tid = localStorage.getItem('tenantid'); const loadMoreRef = useRef(); const containerRef = useRef(); - const locationRef = useRef(null); - const tenantRef = useRef(null); - const [appId, setAppId] = useState(0); - const [locaName, setLocoName] = useState('All'); - const [assignLoading, setAssignLoading] = useState(false); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [tabvalue, setTabvalue] = useState(0); + const [tabstatus, setTabstatus] = useState('Created'); + const [currentStatus, setCurrentStatus] = useState('created'); + const [cancelOpen, setCancelOpen] = useState(false); + const [orderheaderid, setOrderheaderid] = useState(''); + const locationId = 0; + const locoName = 'All Locations'; + const [dateOpen, setDateOpen] = useState(false); + const [datestatus, setDatestatus] = useState('Today'); const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD')); const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD')); - const [datestatus, setDatestatus] = useState('Today'); - const [open, setOpen] = useState(false); - const [tabvalue, setTabvalue] = useState(0); - const [currentStatus, setCurrentStatus] = useState('created'); - const [cancelDialog, setCancelDialog] = useState(false); - const [assignDialog, setAssignDialog] = useState(false); - const [multiDeleteDialog, setMultiDeleteDialog] = useState(false); - const [rider, setRider] = useState(null); - const [payment, setPayment] = useState(null); - const [orderheaderid, setOrderheaderid] = useState(''); - const [page, setPage] = useState(0); - const [productCollapse, setProductCollapse] = useState(null); - const [tenantid, setTenantid] = useState(0); - const [locationid, setLocationid] = useState(0); const [searchword, setSearchword] = useState(''); const [debouncedSearch, setDebouncedSearch] = useState(''); + + // Floating button and Dialog states + const [speedDialOpen, setSpeedDialOpen] = useState(false); + const [multiDeleteDialog, setMultiDeleteDialog] = useState(false); const [createloader, setCreateloader] = useState(false); const [isLoading, setIsLoading] = useState(false); - const [tenantValue, setTenantValue] = useState(null); - const [locationValue, setLocationValue] = useState(null); - const [speedDialOpen, setSpeedDialOpen] = useState(false); - const [aiDialog, setAiDialog] = useState(false); - const [normalAssignDialog, setNormalAssignDialog] = useState(false); - const [dispatchPreviewData, setDispatchPreviewData] = useState(null); const [deliverylist, setDeliverylist] = useState([]); - const [deliveryDetails, setDeliveryDetails] = useState([]); - const [zoneData, setZoneData] = useState(null); - const [metaData, setMetaData] = useState(null); - const [aiMode] = useState(0); - const [csvExportData, setCsvExportData] = useState([]); - const [finaldeliveryList, setFinalDeliveryList] = useState([]); - const aiModeRef = useRef(0); - // Caches the inputs of the most recent AI Assign call so we can forward them - // to the /nearle/dispatch/preview page via navigate state (the page re-uses - // them for its Re-Assign button). - const aiMutationContextRef = useRef(null); - const rowsPerPage = 100; - - const transportOptions = [ - { label: 'Bike', value: 1 }, - { label: 'Auto', value: 2 } - ]; - const [selectedMode, setSelectedMode] = useState(null); - // Riders the operator has marked as absent for today — passed to the AI - // assign API so its allocation skips them. Optional: the AI run still - // works with an empty list. - const [absentRiders, setAbsentRiders] = useState([]); - - // when orders are selected to show popup to stop unwanted reload - - // usePreventReload(shouldBlock); - - const tuningTypes = [ - { - tuneid: 1, - type: 'Balanced', - value: 'balanced' - }, - { - tuneid: 2, - type: 'Aggressive Speed', - value: 'aggressive_speed' - }, - { - tuneid: 3, - type: 'Fuel Saver', - value: 'fuel_saver' - }, - { - tuneid: 4, - type: 'Zone Strict', - value: 'zone_strict' - } - ]; - - // to clear the tenant and location autocomplete useEffect(() => { - setTenantid(0); - setTenantValue(null); - setLocationid(0); - setLocationValue(null); - }, [appId]); - // to clear the location autocomplete - useEffect(() => { - setLocationid(0); - setLocationValue(null); - }, [tenantid]); + const handler = setTimeout(() => { + setDebouncedSearch(searchword); + }, 400); + return () => clearTimeout(handler); + }, [searchword]); - // for csv file - useEffect(() => { - const filtered = finaldeliveryList.map((item) => ({ - zone_name: item.zone_name, - ordernotes: item.ordernotes, - rider: item.rider, - step: item.step, - ordertype: item.ordertype, - orderamount: item.orderamount, - riderkms: item.riderkms, - cumulativekms: item.cumulativekms, - baseprice: item.baseprice, - minkm: item.minkm, - priceperkm: item.priceperkm, - kms: item.kms, - actualkms: item.actualkms, - rider_charge: item.rider_charge, - deliveryamt: item.deliveryamt, - deliverycharges: item.deliverycharges, - profit: item.profit - })); - - setCsvExportData(filtered); - - console.log('csvExportData', filtered); - }, [finaldeliveryList]); - - // whenever thr delivery details get updated, delivery amt calculated and added - useEffect(() => { - if (!deliveryDetails?.length) return; - const updateDeliveryAmtList = deliveryDetails.map((list) => { - const cumulativeKms = Number(list.cumulativekms || 0); - const minKm = Number(list.minkm || 0); - const basePrice = Number(list.baseprice || 0); - const pricePerKm = Number(list.priceperkm || 0); - - if (cumulativeKms <= minKm) { - return { - ...list, - deliveryamt: basePrice - }; - } - return { - ...list, - deliveryamt: (cumulativeKms - minKm) * pricePerKm + basePrice - }; - }); - - setFinalDeliveryList(updateDeliveryAmtList); - console.log('finaldeliveryList', updateDeliveryAmtList); - }, [deliveryDetails]); - - // ==============================|| fetchPercentageData ||============================== // + const handleChangetab = (e, i) => { + setSearchword(''); + setRowsPerPage(10); + setTabvalue(i); + const tab = ORDERS_STATUS_TABS[i]; + setTabstatus(tab.label); + setCurrentStatus(tab.status); + setPage(0); + }; + // React Queries const { data: percentageData, isLoading: fetchpercentageIsLoading, - isError: fetchpercentageIsError, - error: fetchpercentageError, refetch: percentagedataRefetch } = useQuery({ - queryKey: ['percentageData', appId, startdate, enddate, tenantid, locationid], + queryKey: ['percentageData', locationId, startdate, enddate, tid, locationId], queryFn: fetchPercentageData, - enabled: true + enabled: true, + refetchInterval: 15000 }); - - // ==============================|| getTenants ||============================== // - - const { - data: tenantlist, - isLoading: fetchtenantsIsLoading, - isError: fetchtenantsIsError, - error: fetchtenantsError - } = useQuery({ - queryKey: ['tenantlist', appId], - queryFn: () => getTenants(appId), // Ensure appId is passed - enabled: appId !== 0 // Ensures query runs only when appId is valid - }); - - // ==============================|| gettenantlocations ||============================== // - - const { - data: locationlist, - isLoading: fetchlocationsIsLoading, - isError: fetchlocationsIsError, - error: fetchlocationsError - } = useQuery({ - queryKey: ['gettenantlocations', tenantid], - queryFn: () => gettenantlocations(tenantid), // Ensure appId is passed - enabled: tenantid !== 0 // Ensures query runs only when appId is valid - }); - // ==============================|| fetchPaymentType ||============================== // - - const { - data: paymentModes = [], - isLoading: paymentModesLoading - } = useQuery({ - queryKey: ['paymentmodes'], - queryFn: fetchPaymentType - }); - // ==============================|| fetchRidersList ||============================== // - - const { - data: ridersList = [], - isLoading: ridersListLoading - } = useQuery({ - queryKey: ['ridersList', appId], // Unique key for caching & re-fetching - queryFn: fetchRidersList, - enabled: appId !== 0 // Ensures query runs only when appId is valid - }); - // ==============================|| fetchorderscount ||============================== // - const { data: ordersCountData, - isLoading: fetchorderscountIsLoading, - isError: fetchorderscountIsError, - error: fetchorderscountError, refetch: orderscountRefetch } = useQuery({ - queryKey: ['ordersCount', appId, startdate, enddate, currentStatus, tenantid, locationid], + queryKey: ['ordersCount', locationId, startdate, enddate, currentStatus, tid, locationId], queryFn: fetchorderscount, refetchOnMount: true, - refetchOnWindowFocus: true + refetchOnWindowFocus: true, + refetchInterval: 15000 }); - // ==============================|| getallriders ||============================== // const { data: autoRiders @@ -454,27 +267,56 @@ const Orders = () => { refetchOnWindowFocus: true }); - // ==============================|| getorders ||============================== // + const cancelorder = async () => { + await axios + .put(`${process.env.REACT_APP_URL}/orders/updateorder`, { + orderheaderid: orderheaderid, + orderstatus: 'cancelled', + cancelled: dayjs().format('YYYY-MM-DD HH:mm:ss') + }) + .then((res) => { + if (res.data.status) { + enqueueSnackbar('Order Cancelled Successfully', { + variant: 'success', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 2000 + }); + refetchOrders(); + orderscountRefetch(); + percentagedataRefetch(); + setCancelOpen(false); + } + }) + .catch((err) => { + console.log(err); + }); + }; + + const fetchOrders = async ({ pageParam = 1 }) => { + const res = await axios.get( + `${process.env.REACT_APP_URL}/orders/tenant/getorders/?tenantid=${tid}&locationid=${locationId}&status=${currentStatus}&fromdate=${startdate}&todate=${enddate}&pageno=${pageParam}&pagesize=${rowsPerPage}&keyword=${debouncedSearch}` + ); + return { + data: res.data.details, + nextPage: res.data.details.length === rowsPerPage ? pageParam + 1 : undefined + }; + }; + const { - data: orderData, - isLoading: fetchOrdersIsLoading, - isError: fetchOrdersIsError, - error: fetchOrdersError, + data: rowdata, fetchNextPage, + isLoading: isLoadingGetOrders, hasNextPage, isFetchingNextPage, - refetch: ordersRefetch + refetch: refetchOrders } = useInfiniteQuery({ - queryKey: ['fetchOrders', appId, currentStatus, debouncedSearch, startdate, enddate, rowsPerPage, tenantid, locationid], + queryKey: [tabstatus, startdate, enddate, page, rowsPerPage, debouncedSearch, locationId], queryFn: fetchOrders, - getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined, - refetchOnWindowFocus: true, - refetchOnMount: true, // 👈 ensures API triggers on page navigation - refetchOnReconnect: true, - staleTime: 0 + getNextPageParam: (lastPage) => lastPage.nextPage, + refetchInterval: 15000 }); - const rows = orderData?.pages.flatMap((page) => page.rows) || []; + const rows = rowdata ? rowdata.pages.flatMap((p) => p.data) : []; useEffect(() => { if (!hasNextPage) return; @@ -485,9 +327,9 @@ const Orders = () => { } }, { - root: document.querySelector('.MuiTableContainer-root'), // 👈 or explicitly TableContainer - rootMargin: '0px', - threshold: 1.0 + root: null, + rootMargin: '0px 0px 400px 0px', + threshold: 0 } ); if (loadMoreRef.current) observer.observe(loadMoreRef.current); @@ -505,166 +347,60 @@ const Orders = () => { } }; - // ======================================================= || createDeliveryMutation || ======================================================= + // ==============================|| Mutations ||============================== // const createDeliveryMutation = useMutation({ - // mutationFn: createDeliveries, // Using the separate function - // 0 -> manual rider assign 1 -> ai rider assign - mutationFn: aiModeRef.current == 0 ? createOptimisationDeliveries : createAutomationDeliveries, - onSuccess: (data, variables = []) => { - console.log('data', data); - console.log('varialbles', variables); - OpenToast('Orders Optimised Successfully', 'success', 2000); + mutationFn: createAutomationDeliveries, + onSuccess: (data, variables) => { + enqueueSnackbar('Orders Optimised Successfully', { variant: 'success', autoHideDuration: 2000, anchorOrigin: { vertical: 'top', horizontal: 'right' } }); orderscountRefetch(); - ordersRefetch(); - // notifyRiderMutation.mutate(variables?.riderToken); // Call notifyRider after success - setAssignLoading(false); + refetchOrders(); setCreateloader(false); - if (aiModeRef.current == 0) { - setDeliveryDetails(data?.details); - setZoneData(data?.zones); - setMetaData(data?.meta); - setNormalAssignDialog(true); - // navigate('/nearle/orders/preview', { - // state: { - // zoneSummary: data?.zone_analysis, - // deliverylist: data?.details, - // zoneData: data?.zones, - // metaData: data?.meta, - // riderToken: rider.userfcmtoken, - // appId, - // aiMode: aiModeRef.current, - // reassignOrders - // } - // }); - } else { - setDeliveryDetails(data?.details); - setZoneData(data?.zones); - setMetaData(data?.meta); - setDispatchPreviewData(data); - // Route the AI Assign result to the dedicated Preview page (Dispatch - // view + Reconcile tab + Change Rider). The previous in-page dialog - // (aiDialog) is left intact but no longer opened — the page replaces - // it. All inputs needed for Re-Assign / Assign Orders are forwarded. - const ctx = aiMutationContextRef.current || {}; - navigate('/nearle/dispatch/preview', { - state: { - dispatchPreviewData: data, - aiMode: ctx.aiMode ?? aiModeRef.current, - selectedMode: ctx.selectedMode || selectedMode, - deliveryData: ctx.deliveryData || [], - autoRiders: ctx.autoRiders || autoRiders || [], - absentRidersPayload: ctx.absentRidersPayload || [], - rider: ctx.rider || rider, - appId: ctx.appId ?? appId, - tenantId: ctx.tenantid ?? tenantid, - startdate - } - }); - } + navigate('/nearle/dispatch/preview', { + state: { + dispatchPreviewData: data, + aiMode: 1, + selectedMode: { value: 1 }, + deliveryData: variables?.deliveries || [], + appId: locationId, + startdate: startdate, + tenantId: tid, + autoRiders: autoRiders || [] + } + }); }, onError: (error) => { - OpenToast(error.message, 'error', 4000); + enqueueSnackbar(error.message, { variant: 'error', autoHideDuration: 4000 }); setCreateloader(false); - setAssignLoading(false); }, onSettled: () => { - assignDialogclose(); - setAssignLoading(false); setCreateloader(false); setIsLoading(false); } }); - // ======================================================= || handleManualCreateDelivery || ======================================================= - - const handleManualCreateDelivery = () => { - if (!finaldeliveryList?.length) { - OpenToast('No deliveries selected', 'error', 3000); - return; - } - createManualDeliveryMutation.mutate({ - deliveries: finaldeliveryList - }); - }; - - // ======================================================= || createManualDeliveryMutation || ======================================================= - const createManualDeliveryMutation = useMutation({ - mutationFn: finalCreatedeliveries, - onMutate: () => { - setIsLoading(true); - }, - onSuccess: (data) => { - console.log('create data', data); - OpenToast('Delivery Created Successfully', 'success', 2000); - navigate('/nearle/deliveries'); // ✅ no delay - // if (rider?.userfcmtoken) { - notifyRiderMutation.mutate(rider?.userfcmtoken); - // } - }, - - onError: (error) => { - OpenToast(error.message, 'error', 4000); - }, - - onSettled: () => { - setIsLoading(false); - } - }); - - // ==============================|| cancelOrder ||============================== // - - const cancelOrderMutation = useMutation({ - mutationFn: cancelOrder, - onSuccess: (data) => { - if (data.status) { - setCancelDialog(false); - OpenToast('Order Cancelled Successfully', 'success', 2000); - orderscountRefetch(); - percentagedataRefetch(); - } - }, - onError: (error) => { - OpenToast(error.message, 'error', 2000); - } - }); - - // ==============================|| cancelMultipleOrder ||============================== // const cancelMultipleOrderMutation = useMutation({ mutationFn: cancelMultipleOrder, onSuccess: (data) => { if (data.status) { setMultiDeleteDialog(false); - OpenToast('Order Cancelled Successfully', 'success', 2000); - ordersRefetch(); + enqueueSnackbar('Orders Cancelled Successfully', { variant: 'success', autoHideDuration: 2000 }); + refetchOrders(); orderscountRefetch(); percentagedataRefetch(); setDeliverylist([]); } }, onError: (error) => { - OpenToast(error.message, 'error', 2000); + enqueueSnackbar(error.message, { variant: 'error', autoHideDuration: 4000 }); } }); - // ======================================================= || notifyRiderMutation || ======================================================= - - const notifyRiderMutation = useMutation({ - mutationFn: notifyRider, // Using the separate function - onSuccess: () => { - OpenToast('Notification sent Successfully', 'success', 2000); - }, - onError: (error) => { - OpenToast(error.message, 'error', 2000); - } - }); - - // ======================================================= || handleCreateDelivery || ======================================================= - - const handleCreateDelivery = async (tune) => { + const handleCreateDelivery = async () => { + if (rows.length === 0) return; setIsLoading(true); - setAssignLoading(true); - const deliveryData = deliverylist.map((val) => ({ + setCreateloader(true); + const deliveryData = rows.map((val) => ({ ...val, deliveryid: 0, deliverydate: dayjs(val.deliverydate).utc().format('YYYY-MM-DD HH:mm:ss'), @@ -680,496 +416,138 @@ const Orders = () => { pickuplocation: val.pickupsuburb, deliverylocation: val.deliverysuburb })); - console.log('deliveryData', deliveryData); - // Normalize the operator's "Absent Riders" picks into the {userid, username} - // shape the AI assign API expects. The API returns firstname/lastname, so - // we join them into a single `username` string (with a fallback to the - // raw `username` field when names are missing). - const absentRidersPayload = (absentRiders || []).map((r) => ({ - userid: r.userid, - username: - `${r.firstname || ''} ${r.lastname || ''}`.trim() || - r.username || - `Rider ${r.userid}` - })); - // Remember the inputs so the Preview page can re-run Re-Assign with the - // same payload without us having to re-derive it from current Orders state. - aiMutationContextRef.current = { - deliveryData, - absentRidersPayload, - autoRiders, - selectedMode, - aiMode: aiModeRef.current, - rider, - appId, - tenantid - }; - - if (aiModeRef.current == 0) { - // manual assign - createDeliveryMutation.mutate({ - deliveries: deliveryData - }); - } else { - if (selectedMode && selectedMode?.value == 1) { - // bike auto assign - createDeliveryMutation.mutate({ - // mode 1 -> bike , 2 -> auto - deliveries: selectedMode.value == 1 ? deliveryData : { body: deliveryData }, - hypertuning_params: tune || null, - selectedMode, - absent_riders: absentRidersPayload - // reshuffle: 'joshi' - }); - } else { - createDeliveryMutation.mutate({ - // Auto auti assign - data: { - orders: deliveryData, - riders: autoRiders, - config: { - pay_type: 'hourly', // options: "hourly", "daily", or "orders" - base_pay: 300.0, - strategy: 'multi_trip' - }, - absent_riders: absentRidersPayload - }, - selectedMode - }); - } - } - }; - // ======================================================= || createFinalDeliveryMutation || ======================================================= - - const createFinalDeliveryMutation = useMutation({ - mutationFn: finalCreatedeliveries, // for optimised delivery create - onSuccess: (data, variables) => { - console.log('data', data); - console.log('varialbles', variables); - // notifyRiderMutation.mutate(rider.userfcmtoken); // Call notifyRider after success - OpenToast('Delivery Created Successfully', 'success', 2000); - setIsLoading(false); - navigate('/nearle/deliveries'); - }, - onError: (error) => { - OpenToast(error.message, 'error', 4000); - }, - onSettled: () => { - setIsLoading(false); - } - }); - - // ======================================================= || handleFinalCreateDelivery || ======================================================= - - const handleFinalCreateDelivery = async () => { - // console.log('Final delivery list:', finaldeliveryList); - - setIsLoading(true); - createFinalDeliveryMutation.mutate({ - deliveries: finaldeliveryList + createDeliveryMutation.mutate({ + deliveries: deliveryData, + selectedMode: { value: 1 }, + hypertuning_params: 'balanced', + absent_riders: [] }); }; - const handleChangetab = (e, i) => { - setPage(0); - setTabvalue(i); - if (i === 0) { - setCurrentStatus('created'); - } - if (i === 1) { - setCurrentStatus('pending'); - } - if (i === 2) { - setCurrentStatus('delivered'); - } - if (i === 3) { - setCurrentStatus('cancelled'); - } - setSearchword(''); - }; - - const assignDialogclose = () => { - setAssignDialog(false); - setRider(null); - setPayment(null); - setSelectedMode(null); - setAbsentRiders([]); - }; - - const errorMessage = fetchpercentageIsError - ? `Error fetching percentages: ${fetchpercentageError?.message}` - : fetchorderscountIsError - ? `Error fetching orders Count: ${fetchorderscountError?.message}` - : fetchOrdersIsError - ? `Error fetching orders : ${fetchOrdersError?.message}` - : fetchtenantsIsError - ? `Error Fetching on Tenants:${fetchtenantsError?.message} ` - : fetchlocationsIsError - ? `Error Fetching on Tenants Locations:${fetchlocationsError?.message} ` - : null; - - useEffect(() => { - if (errorMessage) { - console.log(errorMessage); - OpenToast(errorMessage, 'error', 3000); - } - }, [errorMessage]); + // KPI tile definitions. + const kpiCards = [ + { key: 'created', label: 'Created Orders', color: BRAND, icon: MdLocalShipping, value: percentageData?.created, percentage: percentageData?.percentage1 }, + { key: 'pending', label: 'Pending Orders', color: '#f59e0b', icon: MdHourglassEmpty, value: percentageData?.uncoveredOrders, percentage: percentageData?.percentage2 } + ]; return ( - {(fetchpercentageIsLoading || - fetchorderscountIsLoading || - fetchOrdersIsLoading || - fetchtenantsIsLoading || - fetchlocationsIsLoading || - createloader) && ( - <> - - - - )} - { - theme.zIndex.drawer + 1 - }} - open={createloader || fetchOrdersIsLoading} // when loader = true, backdrop covers the page - > - - - } - {} - {/* // ==============================|| SpeedDial ||============================== // */} - {deliverylist.length > 0 && ( - - - - } - open={speedDialOpen} - onOpen={() => setSpeedDialOpen(true)} - onClose={() => setSpeedDialOpen(false)} + {(fetchpercentageIsLoading || isLoadingGetOrders || isLoading || createloader) && ( + <> + + + + )} + + {rows.length > 0 && currentStatus === 'created' && ( + )} - {/* ============================================= || Header || ============================================= */} - } - placeholder="Select Zone" - paperComponent={SoftPaper} - sx={{ width: { xs: '100%', sm: 280 }, zIndex: 100 }} - /> - } - /> - {/* ============================================= || KPI Cards || ============================================= */} - - {[ - { key: 'created', label: 'Created Orders', color: '#662582', icon: MdOutlineReceiptLong, value: percentageData?.created, percentage: percentageData?.percentage1 }, - { key: 'pending', label: 'Pending Orders', color: '#f59e0b', icon: MdOutlinePendingActions, value: percentageData?.uncoveredOrders, percentage: percentageData?.percentage2 }, - { key: 'delivered', label: 'Delivered Orders', color: '#10b981', icon: MdOutlineCheckCircle, value: percentageData?.coveredOrders, percentage: percentageData?.percentage3 }, - { key: 'cancelled', label: 'Cancelled Orders', color: '#ef4444', icon: MdOutlineCancel, value: percentageData?.cancelled, percentage: percentageData?.percentage4 } - ].map((item) => { - const Icon = item.icon; - return ( - - } - color={item.color} - loading={fetchpercentageIsLoading} - caption={item.percentage != null ? `${item.percentage}% of total` : undefined} - /> - - ); - })} - - - {/* ============================================= || Filter Bar (tenant, location, date) || ============================================= */} + {/* ============================================= || Header (compact) || ============================================= */} - - - option?.tenantname || ''} - PaperComponent={SoftPaper} - onOpen={(event) => { - if (!appId) { - event.preventDefault(); - OpenToast('Please select location first!', 'warning', 3000); - setTimeout(() => { - locationRef.current?.focus(); - }, 0); - } + + + { - if (reason === 'clear') { - setTenantid(0); - setTenantValue(null); - setLocationid(0); - setLocationValue(null); - } else { - setTenantid(val?.tenantid || 0); - setTenantValue(val); - setLocationid(0); - setLocationValue(null); - } - }} - renderInput={(params) => ( - - - - - - ) - }} - /> - )} - /> - - - - (option ? `${option.locationname} (${option.suburb})` : '')} - value={locationValue} - PaperComponent={SoftPaper} - onOpen={(event) => { - if (!appId && !tenantid) { - event.preventDefault(); - OpenToast('Please select a your Location and Tenant first!', 'warning', 3000); - setTimeout(() => { - locationRef.current?.focus(); - }, 0); - } else if (!tenantid) { - event.preventDefault(); - OpenToast('Please select Tenant first!', 'warning', 3000); - setTimeout(() => { - tenantRef.current?.focus(); - }, 0); - } - }} - onChange={(e, val, reason) => { - if (reason === 'clear') { - setLocationid(0); - setLocationValue(null); - } else { - setLocationid(val.locationid || 0); - setLocationValue(val); - } - }} - renderInput={(params) => ( - - - - - - ) - }} - /> - )} - /> - - - - - - setOpen(true)} - sx={{ - display: 'inline-flex', - alignItems: 'center', - gap: 0.75, - px: 1.5, - py: 0.875, - borderRadius: '10px', - cursor: 'pointer', - bgcolor: '#ffffff', - border: '1px solid #e2e8f0', - color: '#475569', - fontWeight: 600, - fontSize: 12, - transition: 'border-color 0.15s, box-shadow 0.15s', - '&:hover': { borderColor: '#cbd5e1' }, - '& svg': { color: '#94a3b8' } - }} - > - - {dayjs(startdate).format('DD/MM/YY')} – {dayjs(enddate).format('DD/MM/YY')} - - - + + + + - {datestatus} - + Orders + + + + + Live · {locoName} · {datestatus} + + - - - {/* ============================================= || filter Dialog | ============================================= */} - setOpen(false)} - onSelect={(range) => { - setStartdate(range.startDate); - setEnddate(range.endDate); - setDatestatus(range.label); - console.log('Selected Date Range:', range); - }} - /> + + + - {/* ============================================= || Status Tabs + Search || ============================================= */} + + + + + {/* ============================================= || Status Tabs + Search (compact) || ============================================= */} { py: 0.5, flexShrink: 0, cursor: 'pointer', - borderRadius: DT.radiusField + 'px', - border: `1px solid ${active ? t.color : DT.borderSubtle}`, - bgcolor: active ? t.color : DT.surface, - color: active ? '#fff' : DT.textSecondary, - fontWeight: 600, - boxShadow: 'none', - transition: 'background-color 0.15s, border-color 0.15s, color 0.15s', + borderRadius: 999, + border: `1.5px solid ${active ? t.color : edge(t.color)}`, + bgcolor: active ? t.color : tint(t.color), + color: active ? '#fff' : t.color, + fontWeight: 700, + boxShadow: active ? `0 6px 18px ${ring(t.color)}` : 'none', + transition: 'all 0.18s', '&:hover': { - borderColor: active ? t.color : DT.borderHover, - bgcolor: active ? t.color : DT.surfaceAlt + borderColor: t.color, + boxShadow: active ? `0 6px 18px ${ring(t.color)}` : `0 0 0 3px ${ring(t.color)}` } }} > - + - + {t.label} - {count} + {count ?? 0} ); })} - - - - + - {/* ============================================= || Table || ============================================= */} + + {/* ============================================= || Table (dense, sticky header) || ============================================= */} { }} > - {isMobile ? ( - /* ===================== MOBILE: card list ===================== */ - - {rows?.length === 0 && !fetchOrdersIsLoading && ( - - - - - - No {currentStatus} orders - - - {searchword ? 'Try a different keyword.' : 'Adjust the filters above to load orders.'} - - - )} - {fetchOrdersIsLoading && ( - - - - Loading orders… - - - )} - {rows?.map((row, index) => { - const isItemSelected = !!deliverylist.find((res) => res.orderheaderid === row.orderheaderid); - const handleCheckbox = (e) => { - if (!appId) { - OpenToast('Please select a location first!', 'warning', 2000); - locationRef.current?.focus(); - return; - } - if (e.target.checked) { - setDeliverylist((prev) => [...prev, { ...row, sno: prev.length + 1 }]); - } else { - setDeliverylist((prev) => - prev.filter((item) => item.orderheaderid !== row.orderheaderid).map((item, i) => ({ ...item, sno: i + 1 })) - ); - } - }; - const meta = ROW_STATUS_META[String(row.orderstatus || '').toLowerCase()] || { - label: row.orderstatus || '—', - color: BRAND, - icon: MdHistoryToggleOff - }; - const StatusIcon = meta.icon; - const chipSx = (c) => ({ - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - px: 1, - py: 0.25, - borderRadius: 999, - bgcolor: dtTint(c), - color: c, - fontWeight: 700, - fontSize: 11, - border: `1px solid ${dtEdge(c)}`, - whiteSpace: 'nowrap' - }); - return ( - - - - {currentStatus === 'created' && ( - - )} - - {String(page * rowsPerPage + index + 1).padStart(2, '0')} - - - {meta.label} - - - {row.orderstatus === 'created' && ( - - {row.deliverytype === 'C' && ( - { - if (productCollapse?.orderid === row.orderid) { - setProductCollapse(null); - } else { - setProductCollapse(row); - } - }} - sx={{ - borderRadius: 999, - bgcolor: dtTint('#06b6d4'), - color: '#06b6d4', - border: `1px solid ${dtEdge('#06b6d4')}`, - '&:hover': { bgcolor: dtSoft('#06b6d4') } - }} - > - {productCollapse?.orderid === row.orderid ? ( - - ) : ( - - )} - - )} - { - setCancelDialog(true); - setOrderheaderid(row.orderheaderid); - }} - sx={{ - borderRadius: 999, - bgcolor: dtTint('#ef4444'), - color: '#ef4444', - border: `1px solid ${dtEdge('#ef4444')}`, - '&:hover': { bgcolor: dtSoft('#ef4444') }, - '&.Mui-disabled': { color: theme.palette.secondary.main } - }} - > - - - - )} - - - - {row.tenantname} - - - {[row.tenantsuburb, row.applocation].filter(Boolean).join(' · ') || '—'} - - - - } - > - - - - {`${row.locationname}-(${row.locationsuburb})`} - - - {row.orderid} · {formatOrderDate(row)} - - - - - {row.pickupcustomer || '—'} - - - {row.pickupcontactno} - - - {row.pickupsuburb || row.pickupaddress} - - - - - {row.deliverycustomer || '—'} - - - {row.deliverycontactno} - - - {row.deliverysuburb || row.deliveryaddress} - - - - - {row.kms || 0} km - - - - {row.collectionamt ? `₹ ${row.collectionamt.toFixed(2)}` : '—'} - - - - - {row.deliverycharge ? `₹ ${row.deliverycharge.toFixed(2)}` : '—'} - - - {row.ordernotes && ( - - - {row.ordernotes} - - - )} - - - ); - })} - {rows?.length != 0 && ( -
    - {isFetchingNextPage || hasNextPage ? ( - - ) : ( - - No more orders - - )} -
    - )} -
    - ) : ( -
    +
    - {currentStatus == 'created' && ( - - 0 && deliverylist.length !== rows?.length} - checked={deliverylist.length === rows?.length} - onChange={(e) => { - if (!appId) { - OpenToast('Please select a location first!', 'warning', 2000); - locationRef.current?.focus(); // Auto-focus on the Autocomplete input - return; - } - if (e.target.checked) { - const newList = rows?.map((row, index) => ({ - ...row, - sno: index + 1 - })); - setDeliverylist(newList); - } else { - setDeliverylist([]); - } - }} - /> - - )} # - Tenant - Location + Order Location Pickup Drop - QTY + Qty COD - KMS + Kms Charges Notes Status - {currentStatus == 'created' && Actions} + {currentStatus === 'created' && Actions} + - {rows?.length === 0 && !fetchOrdersIsLoading && ( + {(isLoadingGetOrders || createloader) && + rows.length === 0 && + Array.from({ length: 10 }).map((_, idx) => ( + + {Array.from({ length: currentStatus === 'created' ? 12 : 11 }).map((__, ci) => ( + + + + ))} + + ))} + + {!isLoadingGetOrders && rows.length === 0 && ( - - - - + + + + - + No {currentStatus} orders - - {searchword ? 'Try a different keyword.' : 'Adjust the filters above to load orders.'} + + {searchword ? 'Try a different keyword or clear the search.' : 'Adjust the location, status, or date range above.'} + {searchword && ( + + )} )} - {fetchOrdersIsLoading && } - {rows?.map((row, index) => { + + {rows.map((row, index) => { const isItemSelected = !!deliverylist.find((res) => res.orderheaderid === row.orderheaderid); const handleCheckbox = (e) => { - if (!appId) { - OpenToast('Please select a location first!', 'warning', 2000); - locationRef.current?.focus(); - return; - } if (e.target.checked) { setDeliverylist((prev) => [...prev, { ...row, sno: prev.length + 1 }]); } else { @@ -1638,1285 +778,253 @@ const Orders = () => { return ( - {currentStatus === 'created' && ( - - - - )} - - {/* Serial Number */} - {page * rowsPerPage + index + 1} - - - {/* Tenant Info */} - - - - {row.tenantname} - - - {row.tenantsuburb} -
    -
    - - {row.applocation} - -
    -
    - - {/* Order Info */} - - {/* */} - - {`${row.locationname}-(${row.locationsuburb})`} + + {page * rowsPerPage + index + 1} - {/* */} - - + + + + + {row.locationname} + {row.locationsuburb && ` - ${row.locationsuburb}`} + + + {row.orderid} - - - {formatOrderDate(row)} - - + + + {(() => { + const dateObj = row.pickupslot && dayjs(row.pickupslot).isValid() + ? dayjs(row.pickupslot) + : dayjs(row.deliverydate || row.orderdate); + return ( + <> + + {dateObj.format('hh:mm A')} + + + · {dateObj.format('DD MMM YY')} + + + ); + })()} + - {/* Pickup Info */} - + - + {row.pickupcustomer} - {row.pickupcontactno} + + {row.pickupcontactno} + - {`${row.pickupsuburb || row.pickupaddress.slice(0, 12) + '.....'}`} + + {row.pickupsuburb || (row.pickupaddress ? `${row.pickupaddress.slice(0, 20)}…` : '—')} + - {/* Delivery Info */} - + - + {row.deliverycustomer} - {row.deliverycontactno} - - {`${row.deliverysuburb || row.deliveryaddress.slice(0, 12) + '.....'}`} + + {row.deliverycontactno} + + + + {row.deliverysuburb || + (row.deliveryaddress?.length > 20 ? `${row.deliveryaddress.slice(0, 20)}…` : row.deliveryaddress || '—')} + - {/* Quantity */} - {row.quantity ? ( - - {row.quantity} - - ) : ( - - {row.quantity} - - )} + } /> - {/* cod */} + - {row.collectionamt ? ( - - ₹{row.collectionamt.toFixed(2)} - - ) : ( - - ₹{row.collectionamt.toFixed(2)} - - )} + } isMoney /> - {/* kms */} + - - {row.kms} - + } /> - {/* Charges */} + - {row.deliverycharge ? ( - - ₹{row.deliverycharge.toFixed(2)} - - ) : ( - - ₹{row.deliverycharge.toFixed(2)} - - )} + } isMoney /> - {/* Notes */} - - - {row.ordernotes} - - - - {/* Order Status */} - - {(() => { - const meta = ROW_STATUS_META[String(row.orderstatus || '').toLowerCase()] || { - label: row.orderstatus || '—', - color: BRAND, - icon: MdHistoryToggleOff - }; - const StatusIcon = meta.icon; - return ( - + {row.ordernotes ? ( + + + - {meta.label} - - ); - })()} + {row.ordernotes} + + + ) : ( + + — + + )} - {/* Actions */} - {row.orderstatus === 'created' && ( - - - {row.deliverytype === 'C' && ( - - { - if (productCollapse?.orderid === row.orderid) { - setProductCollapse(null); - } else { - setProductCollapse(row); - } - }} - > - {productCollapse?.orderid === row.orderid ? : } - - - )} + + + - + {currentStatus === 'created' && ( + + {row.orderstatus === 'created' && ( + { - setCancelDialog(true); + size="small" + onClick={(e) => { + e.stopPropagation(); setOrderheaderid(row.orderheaderid); + setCancelOpen(true); + }} + sx={{ + bgcolor: tint('#ef4444'), + border: `1px solid ${edge('#ef4444')}`, + color: '#ef4444', + borderRadius: 999, + p: 0.75, + '&:hover': { + bgcolor: soft('#ef4444'), + borderColor: '#ef4444' + } }} > - + - + )} )}
    ); })} - {rows?.length != 0 && ( - - -
    - {isFetchingNextPage ? ( - - ) : hasNextPage ? ( - + + {rows.length !== 0 && ( + + + + {isFetchingNextPage || hasNextPage ? ( + <> + + + Loading more orders… + + ) : ( - - No more orders + + {rows.length} order{rows.length === 1 ? '' : 's'} · End of list )} -
    +
    )}
    - )}
    - {/* ============================================= || Orders Preview Dialog | ============================================= */} - - { - theme.zIndex.modal + 1 - }} - open={createloader} - > - - - } - - - Preview Orders } variant="contained" /> - - - - - - - - - - # - - - Tenant{' '} - - - order Location - - - Pickup{' '} - - - Delivery - - - Notes - - - Charges - - - KMS - - - - - {deliverylist.map((val, i) => { - return ( - - - - {val.sno} - - - - - {val.tenantname} - - - {val.tenantsuburb} -
    -
    - - {val.applocation} - -
    -
    - - - - {`${val.locationname}-(${val.locationsuburb})`} - - - - - {val.orderid} - - - - - - {dayjs(val.orderdate).utc().format('DD/MM/YYYY')} - - - {dayjs(val.orderdate).utc().format('hh:mm A')} - - - - - - - {dayjs(val.deliverydate).utc().format('DD/MM/YYYY')} - - - - {dayjs(val.deliverydate).utc().format('hh:mm A')} - - - - - - - - {val.pickupcustomer} - {val.pickupcontactno} - - {val.pickupsuburb || val.pickupaddress.slice(0, 20)} - - - - - - - - {val.deliverycustomer} - {val.deliverycontactno} - - {val.deliverysuburb || val.deliveryaddress.slice(0, 20)} - - - - - {val.ordernotes} - ₹ {val.deliverycharge.toFixed(2)} - {val.kms} -
    -
    - ); - })} -
    -
    -
    -
    -
    - - - - - - {aiModeRef.current == 1 && ( - option.label} - value={selectedMode} - onChange={(event, newValue) => { - setSelectedMode(newValue); - console.log('Selected value:', newValue?.value); // 1 or 2 - }} - renderInput={(params) => ( - - )} - isOptionEqualToValue={(option, value) => option.value === value.value} - /> - )} - - {/* Absent Riders multi-select — optional. Operator picks the - riders who are off today; the AI assign API receives their - {userid, username} under `absent_riders` and skips them - during allocation. Leaving it empty is valid: the AI run - proceeds against every rider in the location. */} - {aiModeRef.current == 1 && ( - setAbsentRiders(newValue)} - getOptionLabel={(option) => { - const name = `${option.firstname || ''} ${option.lastname || ''}`.trim(); - return name || `Rider #${option.userid}`; - }} - isOptionEqualToValue={(option, value) => option.userid === value.userid} - PaperComponent={(paperProps) => ( - - )} - ListboxProps={{ sx: { py: 0, maxHeight: 340 } }} - noOptionsText={ - - - - No riders to show for this location - - - } - renderTags={(value, getTagProps) => - value.map((option, index) => { - const name = - `${option.firstname || ''} ${option.lastname || ''}`.trim() || - `Rider #${option.userid}`; - const initials = - (name.match(/\b\w/g) || []).slice(0, 2).join('').toUpperCase() || '?'; - const tagProps = getTagProps({ index }); - return ( - - {initials} - - } - label={name} - sx={{ - height: 26, - bgcolor: '#fff7ed', - color: '#9a3412', - border: '1px solid', - borderColor: '#fdba74', - fontWeight: 600, - fontSize: 12, - '& .MuiChip-label': { px: 0.75 }, - '& .MuiChip-deleteIcon': { - color: '#c2410c', - fontSize: 16, - '&:hover': { color: '#7c2d12' } - } - }} - /> - ); - }) - } - renderOption={(props, option, { selected }) => { - const name = `${option.firstname || ''} ${option.lastname || ''}`.trim(); - const initials = - (name.match(/\b\w/g) || []).slice(0, 2).join('').toUpperCase() || '?'; - return ( -
  • - - - {initials} - - - - {name || `Rider #${option.userid}`} - - - ID #{option.userid} - {option.contactno ? ` · ${option.contactno}` : ''} - - - {selected && ( - - )} -
  • - ); - }} - renderInput={(params) => ( - - - {absentRiders.length > 0 && ( - - )} - {params.InputProps.startAdornment} -
    - ) - }} - sx={{ - minWidth: 300, - maxWidth: 440, - '& .MuiOutlinedInput-root': { - borderRadius: 2, - bgcolor: '#fffbf5', - transition: 'border-color 0.15s, box-shadow 0.15s', - '& fieldset': { - borderColor: '#fdba74', - borderWidth: 1.5 - }, - '&:hover fieldset': { borderColor: '#fb923c' }, - '&.Mui-focused': { - boxShadow: '0 0 0 3px rgba(251, 146, 60, 0.18)' - }, - '&.Mui-focused fieldset': { - borderColor: '#ea580c', - borderWidth: 2 - } - }, - '& .MuiInputLabel-root': { - fontWeight: 700, - fontSize: 13, - color: '#9a3412', - '&.Mui-focused': { color: '#9a3412' } - } - }} - /> - )} - sx={{ - '& .MuiAutocomplete-tag': { my: 0.25 }, - '& .MuiAutocomplete-endAdornment': { right: 8 } - }} - /> - )} - - - -
    -
    -
    -
    - {/* ============================================= || Normal Assign Orders | ============================================= */} - - - - - - setNormalAssignDialog(false)} - sx={{ - backgroundColor: 'action.hover', - color: 'text.primary', - '&:hover': { - backgroundColor: 'action.selected' - } - }} - > - - - - - Assign Orders - - + {/* ============================================= || Cancel Order Dialog || ============================================= */} + setCancelOpen(false)} maxWidth="xs" PaperProps={{ sx: { borderRadius: 3 } }}> + + + + + + + Cancel Order + - - - - - {(paymentModesLoading || ridersListLoading || isLoading) && ( - <> - - - - )} - { - + + + + Are you sure you want to cancel this order? This action cannot be undone. + + + - - - - - - {/* ============================================= || AI optimisation Assign Orders | ============================================= */} - - { - theme.zIndex.modal + 1 - }} - open={isLoading} - > - - - } - - - - - navigate('/nearle/orders')} - sx={{ - bgcolor: 'action.hover', - '&:hover': { bgcolor: 'action.selected' } - }} - > - - - - - Assign Orders - - - - option.type} - sx={{ minWidth: 250, maxWidth: 600, flex: 1 }} - renderInput={(params) => } - onChange={(e, val, reason) => { - if (reason === 'clear') { - handleCreateDelivery(null); - } else { - console.log('val', val); - handleCreateDelivery(val.value); - } - }} - /> - {/* option.type} - renderOption={(props, option, { selected }) => { - const { id, ...optionProps } = props; - const SelectionIcon = selected ? CheckBoxIcon : CheckBoxOutlineBlankIcon; - - return ( -
  • - - {option.type} -
  • - ); - }} - style={{ width: 400 }} - renderInput={(params) => ( - - )} - /> */} - - -
    -
    -
    - - {dispatchPreviewData && } - - - - - - - -
    - {/* ========================================= || cancel order Dialog || ========================================= */} - handleClose()} maxWidth="xs"> - - - - - - - - - Are you sure you want to cancel this order? - - - - - - - - - - - {/* ========================================= || Multiple cancel order Dialog || ========================================= */} - setMultiDeleteDialog(false)} maxWidth="xs"> - - - - - - - - - Are you sure, you want to cancel multiple orders? - - - - - @@ -2924,6 +1032,78 @@ const Orders = () => { + + {/* ============================================= || Date Filter || ============================================= */} + setDateOpen(false)} + onApply={({ startDate, endDate, label }) => { + setStartdate(startDate); + setEnddate(endDate); + setDatestatus(label); + }} + /> + + {/* ============================================= || Cancel Multiple Orders Dialog || ============================================= */} + setMultiDeleteDialog(false)} maxWidth="xs" PaperProps={{ sx: { borderRadius: 3 } }}> + + + + + + + Cancel Selected Orders + + + + + + + Are you sure you want to cancel the {deliverylist.length} selected orders? This action cannot be undone. + + + + + + + + + ); }; diff --git a/src/pages/nearle/orders/ridersPinPoint.js b/src/pages/nearle/orders/ridersPinPoint.js new file mode 100644 index 0000000..47bd344 --- /dev/null +++ b/src/pages/nearle/orders/ridersPinPoint.js @@ -0,0 +1,54 @@ +/* eslint-disable no-unused-vars */ +import { LoadScriptNext, GoogleMap, Marker } from '@react-google-maps/api'; + +// distance function +function distance(lat1, lng1, lat2, lng2) { + const R = 6371; + const dLat = (lat2 - lat1) * (Math.PI / 180); + const dLng = (lng2 - lng1) * (Math.PI / 180); + + const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) * Math.sin(dLng / 2) ** 2; + + return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); +} + +const containerStyle = { + width: '100%', + height: '300px' +}; + +export default function RidersPinPoint({ pickCust, dropCust }) { + // Ensure valid lat/lng + const center = pickCust?.latitude && pickCust?.longitude ? { lat: Number(pickCust.latitude), lng: Number(pickCust.longitude) } : null; + + // If center missing, don't render map + if (!center) return null; + + const sortedRiders = dropCust + ?.map((r) => ({ + ...r, + distance: distance(center.lat, center.lng, Number(r.latitude), Number(r.longitude)) + })) + .sort((a, b) => a.distance - b.distance); + + return ( + + + + + {sortedRiders?.map((r, index) => ( + + ))} + + + ); +} diff --git a/src/pages/nearle/orders/rough.js b/src/pages/nearle/orders/rough.js new file mode 100644 index 0000000..e070112 --- /dev/null +++ b/src/pages/nearle/orders/rough.js @@ -0,0 +1,2560 @@ +/* eslint-disable no-unused-vars */ +import * as React from 'react'; +import { useEffect, useState, useRef, Fragment } from 'react'; +import { + FormControl, + InputAdornment, + Grid, + Typography, + Stack, + Box, + Button, + TextField, + Autocomplete, + CardActions, + Chip, + Avatar, + Divider, + DialogTitle, + DialogContent, + Checkbox, + DialogActions, + CircularProgress, + ButtonGroup, + FormLabel, + IconButton, + Drawer, + InputLabel, + Select, + MenuItem, + Switch, + CardHeader, + Card, + OutlinedInput +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import { Empty } from 'antd'; +import { FaPhoneAlt } from 'react-icons/fa'; +import { GiDoorHandle } from 'react-icons/gi'; +import { FaLandmarkDome } from 'react-icons/fa6'; +import ClearIcon from '@mui/icons-material/Clear'; +import { useNavigate } from 'react-router'; +import { MdLocationCity } from 'react-icons/md'; +import { TbMapPinCode } from 'react-icons/tb'; +import { FaLocationDot } from 'react-icons/fa6'; +import axios from 'axios'; +import { useTheme } from '@mui/material/styles'; +import Geocode from 'react-geocode'; +import Loader from 'components/Loader'; +import * as geolib from 'geolib'; +import MainCard from 'components/MainCard'; +import { FaUser } from 'react-icons/fa6'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import Dialog from '@mui/material/Dialog'; +import dayjs from 'dayjs'; +import { enqueueSnackbar } from 'notistack'; +var utc = require('dayjs/plugin/utc'); +dayjs.extend(utc); +import LocationOnIcon from '@mui/icons-material/LocationOn'; +import parse from 'autosuggest-highlight/parse'; +import { debounce } from '@mui/material/utils'; +import CardContent from 'themes/overrides/CardContent'; +import { PhoneAndroid } from '@mui/icons-material'; +import { SearchOutlined, CloseOutlined } from '@ant-design/icons'; +import PhoneInput from 'react-phone-number-input/input'; +import MyLocationIcon from '@mui/icons-material/MyLocation'; +import HighlightOffIcon from '@mui/icons-material/HighlightOff'; + +function loadScript(src, position, id) { + if (!position) { + return; + } + + const script = document.createElement('script'); + script.setAttribute('async', ''); + script.setAttribute('id', id); + script.src = src; + position.appendChild(script); +} + +const Createorder1 = () => { + Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY); + // ================================================= || GoogleMaps (Drawer) || ================================================= + const [value, setValue] = React.useState(null); + const [value1, setValue1] = React.useState(null); + const [inputValue, setInputValue] = React.useState(''); + const [inputValue1, setInputValue1] = React.useState(''); + const [inputValue2, setInputValue2] = React.useState(''); + const [inputValue3, setInputValue3] = React.useState(''); + const [options, setOptions] = React.useState([]); + const [options1, setOptions1] = React.useState([]); + const loaded = React.useRef(false); + const loaded1 = React.useRef(false); + const [mobilenumber, setMobilenumber] = useState(''); + const [emailaddress, setEmailaddress] = useState(''); + const [city, setCity] = useState(''); + const [city1, setCity1] = useState(''); + const [zipcode, setZipcode] = useState(''); + const [zipcode1, setZipcode1] = useState(''); + const [state, setState] = useState(''); + const [state1, setState1] = useState(''); + const [suburb, setSuburb] = useState(''); + const [suburb1, setSuburb1] = useState(''); + const [pickContactName, setPickContactName] = useState(''); + const [dropContactName, setDropContactName] = useState(''); + const [pickdoorno, setPickDoorno] = useState(''); + const [dropDoorno, setDropDoorno] = useState(''); + const [pickLandmark, setPickLandmark] = useState(''); + const [dropLandmark, setDropLandmark] = useState(''); + const [address, setAddress] = useState(''); + const [address1, setAddress1] = useState(''); + const [latlong, setLatlong] = useState({}); + const [latlong1, setLatlong1] = useState({}); + const autocompleteService = useRef(null); + const [tenanatLocoId, setTenanatLocoId] = useState(localStorage.getItem('locationid')); + const [isLocation, setIsLocation] = useState(false); + + // // // ====================================================== || address (pick)|| ====================================================== + // useEffect(() => { + // if (address) { + // try { + // Geocode.fromAddress(address).then( + // (response) => { + // if (response.status == 'OK') { + // const { lat, lng } = response.results[0].geometry.location; + // console.log({ lat, lng }); + // setLatlong({ + // lat, + // lng + // }); + // console.log(response); + // if (response.results[0].address_components) { + // let place = response.results[0]; + // let cityA, zipcodeA, stateA, suburbA; + // for (let i = 0; i < place.address_components.length; i++) { + // for (let j = 0; j < place.address_components[i].types.length; j++) { + // switch (place.address_components[i].types[j]) { + // case 'locality': + // cityA = place.address_components[i].long_name; + // break; + // case 'administrative_area_level_1': + // stateA = place.address_components[i].long_name; + // break; + // case 'postal_code': + // zipcodeA = place.address_components[i].long_name; + // break; + // case 'sublocality': + // suburbA = place.address_components[i].long_name; + // break; + // } + // } + // } + // setCity(cityA || ''); + // setState(stateA || ''); + // setZipcode(zipcodeA || ''); + // setSuburb(suburbA || ''); + // console.log({ lat, lng, cityA, stateA, zipcodeA, suburbA }); + // setPickCust({ + // ...pickCust + // // city: cityA, + // // state: stateA, + // // postcode: zipcodeA, + // // suburb: suburbA + // // latitude: lat, + // // longitude: lng + // }); + // // setStartPoint({ latitude: lat, longitude: lng }); + // } + // } + // }, + // (error) => { + // console.log(error); + // } + // ); + // } catch (err) { + // console.log(err); + // } + // } + // }, [address]); + // // // ====================================================== || address 1 (drop)|| ====================================================== + // useEffect(() => { + // if (address) { + // try { + // Geocode.fromAddress(address1).then( + // (response) => { + // if (response.status == 'OK') { + // const { lat, lng } = response.results[0].geometry.location; + + // setLatlong1({ + // lat, + // lng + // }); + // console.log(response); + // if (response.results[0].address_components) { + // let place = response.results[0]; + // let cityB, zipcodeB, stateB, suburbB; + // for (let i = 0; i < place.address_components.length; i++) { + // for (let j = 0; j < place.address_components[i].types.length; j++) { + // switch (place.address_components[i].types[j]) { + // case 'locality': + // cityB = place.address_components[i].long_name; + // break; + // case 'administrative_area_level_1': + // stateB = place.address_components[i].long_name; + // break; + // case 'postal_code': + // zipcodeB = place.address_components[i].long_name; + // break; + // case 'sublocality': + // suburbB = place.address_components[i].long_name; + // break; + // } + // } + // } + // setCity(cityB || ''); + // setState(stateB || ''); + // setZipcode(zipcodeB || ''); + // setSuburb(suburbB || ''); + // console.log({ lat, lng, cityB, stateB, zipcodeB, suburbB }); + // setDropCust({ + // ...dropCust + // // city: cityB, + // // state: stateB, + // // postcode: zipcodeB, + // // suburb: suburbB + // // latitude: lat, + // // longitude: lng + // }); + // // setEndPoint({ latitude: lat, longitude: lng }); + // } + // } + // }, + // (error) => { + // console.log(error); + // } + // ); + // } catch (err) { + // console.log(err); + // } + // } + // }, [address1]); + + if (typeof window !== 'undefined' && !loaded.current) { + if (!document.querySelector('#google-maps')) { + loadScript( + `https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}&libraries=places&location=10.3656,77.9690&radius=50000&components=country:IN&strictbounds=true`, + document.querySelector('head'), + 'google-maps' + ); + } + loaded.current = true; + } + + // const fetch = React.useMemo( + // () => + // debounce((request, callback) => { + // autocompleteService.current.getPlacePredictions(request, callback); + // }, 400), + // [] + // ); + // const fetch1 = React.useMemo( + // () => + // debounce((request, callback) => { + // autocompleteService.current.getPlacePredictions(request, callback); + // }, 400), + // [] + // ); + + // ====================================================== || options (pick)|| ====================================================== + + // React.useEffect(() => { + // let active = true; + // if (!autocompleteService.current && window.google) { + // autocompleteService.current = new window.google.maps.places.AutocompleteService(); + // } + // if (!autocompleteService.current) { + // return undefined; + // } + // if (inputValue === '') { + // setOptions(value ? [value] : []); + // return undefined; + // } + // fetch({ input: inputValue }, (results) => { + // if (active) { + // let newOptions = []; + + // if (value) { + // newOptions = [value]; + // } + + // if (results) { + // newOptions = [...newOptions, ...results]; + // } + + // setOptions(newOptions); + // } + // }); + + // return () => { + // active = false; + // }; + // }, [value, inputValue, fetch]); + + // // ====================================================== || options1 (drop)|| ====================================================== + // React.useEffect(() => { + // let active = true; + // if (!autocompleteService.current && window.google) { + // autocompleteService.current = new window.google.maps.places.AutocompleteService(); + // } + // if (!autocompleteService.current) { + // return undefined; + // } + // if (inputValue1 === '') { + // setOptions1(value1 ? [value1] : []); + // return undefined; + // } + // fetch1({ input: inputValue1 }, (results) => { + // if (active) { + // let newOptions = []; + + // if (value1) { + // newOptions = [value1]; + // } + + // if (results) { + // newOptions = [...newOptions, ...results]; + // } + + // setOptions1(newOptions); + // } + // }); + + // return () => { + // active = false; + // }; + // }, [value1, inputValue1, fetch1]); + + const appId = localStorage.getItem('applocationid'); + const navigate = useNavigate(); + const [open, setOpen] = useState({}); + const [open1, setOpen1] = useState(''); + const [open2, setOpen2] = useState(false); + const [open3, setOpen3] = useState(false); + const [open4, setOpen4] = useState(false); + const [shift, setShift] = useState(1); + const [clientlist, setClientlist] = useState([]); + const [clientdetail, setClientdetail] = useState([]); + const [eventname, setEventname] = useState(''); + const [startdate, setStartdate] = useState(dayjs().format('MM-DD-YYYY')); + const [enddate, setEnddate] = useState(dayjs().add(1, 'day').format('MM-DD-YYYY')); + // const [starttime, setStatrttime] = useState(`${dayjs().format('MM-DD-YYYY')} 08:00:00`); + const [starttime, setStatrttime] = useState(); + // const [endtime, setEndtime] = useState(`${dayjs().format('MM-DD-YYYY')} 20:00:00`); + const [endtime, setEndtime] = useState(); + const [timeslotarr, setTimeslotarr] = useState([]); + const [currentsno, setCurrentsno] = useState(''); + const [roleoptions, setRoleoptions] = useState([]); + const theme = useTheme(); + const [otherinstructions, setOtherinstructions] = useState(''); + const [attireslist, setAttireslist] = useState([]); + const [serviceaddonslist, setServiceaddonslist] = useState([]); + const [orderaddonobj, setOrderaddonobj] = useState([]); + const [stafflist, setStafflist] = useState([]); + const [loading2, setLoading2] = useState(false); + const [loading, setLoading] = useState(false); + const [shiftarr, setShiftarr] = useState([]); + const [shiftarr1, setShiftarr1] = useState([]); + const [orderarr, setOrderarr] = useState([]); + const [alertmessage, setAlertmessage] = useState(''); + const [tabstatus, setTabstatus] = useState(''); + const [tenantinfo, setTenantinfo] = useState({}); + const [searchword, setSearchword] = useState(''); + const [clientdetailarr, setClientdetailarr] = useState([]); + const [clientdetailbusinessarr, setClientdetailbusinessarr] = useState([]); + const [admintoken, setAdmintoken] = useState(); + const [tenantlocationlist, setTenantlocationlist] = useState([]); + const [tenant, setTenant] = useState({}); + const [clientinfo, setClientinfo] = useState({}); + const [selectedtime, setSelectedtime] = useState(''); + const [tenantlist, setTenantlist] = useState([]); + const [tenantid, setTenantid] = useState(); + const [tenantlocation, setTenantlocation] = useState(''); + const [pickupswitch, setPickupswitch] = useState(true); + const [deliverytype, setDeliverytype] = useState('B'); + const [dropswitch, setDropswitch] = useState(false); + const [startPoint, setStartPoint] = useState({ latitude: 0, longitude: 0 }); + const [endPoint, setEndPoint] = useState({ latitude: 0, longitude: 0 }); + const [showDistance, setShowDistance] = useState(false); + const [distance, setDistance] = useState(0); + const [basePrice, setBasePrice] = useState(0); + const [pricePerKm, setPricePerKm] = useState(0); + const [minKm, setMinKm] = useState(0); + const [totalCharge, setTotalCharge] = useState(0); + const [subCat, setSubCat] = useState([]); + const [subCatName, setSubCatName] = useState('Select '); + const [subCatId, setSubCatId] = useState(); + const [weight, setWeight] = useState(''); + const tid = localStorage.getItem('tenantid'); + const [selectedCatChip, setSelectedCatChip] = useState(null); + const [isCustomerOpen, setIsCustomerOpen] = useState(false); + const [searchCustList, setSearchCustList] = useState(''); + const [customerlist, setCustomerlist] = useState([]); + const [pickCust, setPickCust] = useState({}); + const [dropCust, setDropCust] = useState({}); + const [pickordrop, setpickordrop] = useState(0); // 1 ->pick 2 -> drop + const [addId1, setAddId1] = useState(0); + const [addId2, setAddId2] = useState(0); + const [tenantLocations, setTenantlocations] = useState([]); + const [appLocaLat, setAppLocaLat] = useState(); + const [appLocaLng, setAppLocaLng] = useState(); + const [appLocaRadius, setAppLocaRadius] = useState(); + + const handleChipClick = (chipLabel) => { + setSelectedCatChip(chipLabel); + }; + + const chipStyle = (chipLabel) => ({ + cursor: 'pointer', + backgroundColor: selectedCatChip === chipLabel ? theme.palette.primary.main : 'default', + color: selectedCatChip === chipLabel ? '#fff' : '', + '&:hover': { + backgroundColor: selectedCatChip === chipLabel ? theme.palette.primary.main : theme.palette.primary.light, + color: '#fff' + } + }); + + const fetchTenantPricing = async () => { + try { + const pricingResponse = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantpricing/?tenantid=${tid}`); + console.log('pricingResponse', pricingResponse.data.details); + setBasePrice(pricingResponse.data.details.baseprice); + setPricePerKm(pricingResponse.data.details.priceperkm); + setMinKm(pricingResponse.data.details.minkm); + } catch (error) { + console.log('fetchTenantPricing error', error); + } + }; + useEffect(() => { + fetchTenantPricing(); + }, []); + + useEffect(() => { + console.log('startPoint', startPoint); + console.log('endPoint', endPoint); + if (startPoint.latitude != 0 && startPoint.longitude != 0 && endPoint.latitude != 0 && endPoint.longitude != 0) { + getDistance(); + } + }, [startPoint, endPoint]); + + const getDistance = () => { + const dist = geolib.getPreciseDistance(startPoint, endPoint, 1); + console.log(`Distance: ${dist} meters`); + const distanceInKm = (dist / 1000).toFixed(2); + const roundedDistance = Math.round(distanceInKm); + console.log('roundedDistance', roundedDistance); + setDistance(roundedDistance.toFixed(2)); + if (roundedDistance < minKm) { + setTotalCharge(basePrice); + } else { + const total = (roundedDistance - minKm) * pricePerKm + basePrice; + setTotalCharge(total); + } + setShowDistance(true); + if (roundedDistance > appLocaRadius) { + setShowDistance(true); + setOpen4(true); + } + }; + + useEffect(() => { + clientdetails(); + // clientdetailsbusiness(); + }, [searchCustList.length > 3, searchCustList == '']); + + useEffect(() => { + if (timeslotarr[0]) { + let arr = []; + timeslotarr.map((val) => { + if (dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0) { + arr.push(val); + } + }); + + if (arr[0]) { + setOrderarr([ + { + sno: 1, + address: '', + customerid: '', + deliverytime: dayjs(arr[0]) || '', + deliverylocationid: '', + clientname: '', + contactno: '', + latitude: '', + longitude: '' + } + ]); + } + } + }, [timeslotarr]); + + useEffect(() => { + if (searchword) { + let arr = clientdetail.filter((val) => { + return ( + val.address.toLowerCase().includes(searchword.toLowerCase()) || + val.firstname.toLowerCase().includes(searchword.toLowerCase()) || + val.contactno.toLowerCase().includes(searchword.toLowerCase()) + ); + }); + console.log(arr); + setClientdetailarr([...arr]); + } else { + setClientdetailarr([...clientdetail]); + } + }, [searchword]); + + // const { ref: materialRef } = usePlacesWidget({ + // apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY, + // onPlaceSelected: (place) => { + // console.log(place); + + // // setAddress(place.formatted_address) + // let city1, zipcode1, state1, suburb1; + // for (let i = 0; i < place.address_components.length; i++) { + // for (let j = 0; j < place.address_components[i].types.length; j++) { + // switch (place.address_components[i].types[j]) { + // case 'locality': + // city1 = place.address_components[i].long_name; + // break; + // case 'administrative_area_level_1': + // state1 = place.address_components[i].long_name; + // break; + // case 'postal_code': + // zipcode1 = place.address_components[i].long_name; + // break; + // case 'sublocality': + // suburb1 = place.address_components[i].long_name; + // break; + // } + // } + // } + // // setCity(city1 || '') + // // setState(state1 || ''); + // // setZipcode(zipcode1 || ''); + // // setSuburb(suburb1 || '') + // }, + + // options: { + // types: ['address' || 'geocode'] + // } + // }); + + // ==================================================== || fetchtenantinfo || ==================================================== + const fetchtenantinfo = async () => { + setLoading(true); + console.log('tid', tid); + + await axios + .get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`) + .then((res) => { + console.log('fetchtenantinfo', res); + if (res.data.status) { + setTenant(res.data.details); + setTenantid(res.data.details.tenantid); + fetchAppAdminTokens(); + setSubCatId(res.data.details.subcategoryid); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + useEffect(() => { + fetchtenantinfo(); + }, []); + // ==================================================== || getsubcategories || ==================================================== + const getsubcategories = async () => { + await axios + .get(`${process.env.REACT_APP_URL}/utils/getsubcategories/?moduleid=6`) + .then((res) => { + console.log('subcateRes', res.data.details); + if (res.data.status) { + setSubCat(res.data.details); + } + }) + .catch((err) => { + console.log(err); + }); + }; + useEffect(() => { + getsubcategories(); + }, []); + + // ==================================================== || fetchTiming || ==================================================== + const fetchTiming = async () => { + setLoading(true); + await axios + .get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`) + .then((res) => { + console.log('fetchTiming', res); + const { opentime, closetime, latitude, longitude, radius } = res.data.details[0]; + if (res.data.status) { + setAppLocaLat(latitude); + setAppLocaLng(longitude); + setAppLocaRadius(radius); + setStatrttime(`${dayjs().format('MM-DD-YYYY')} ${opentime}`); + setEndtime(`${dayjs().format('MM-DD-YYYY')} ${closetime}`); + console.log('starttime', `${dayjs().format('MM-DD-YYYY')} ${opentime}`); + console.log('endtime', `${dayjs().format('MM-DD-YYYY')} ${closetime} `); + let arr = []; + for ( + let i = `${dayjs().format('MM-DD-YYYY')} ${opentime}`, j = 0; + dayjs(`${dayjs().format('MM-DD-YYYY')} ${closetime} `).diff(i, 'm') >= 0; + j++, i = dayjs(i).add(30, 'm') + ) { + arr.push(i); + } + console.log('setTimeslotarr', arr); + setTimeslotarr(arr); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + useEffect(() => { + fetchTiming(); + }, [starttime, endtime]); + + // =============================================== || fetchAppAdminTokens (via appId) || =============================================== + const fetchAppAdminTokens = async () => { + setLoading(true); + await axios + .get(`${process.env.REACT_APP_URL}/utils/getapplocationconfig/?applocationid=${appId}`) + .then((res) => { + const userfcmtokemArray = res.data.details.applocationadmins.map((admin) => admin.userfcmtokem); + console.log('fetchAppAdminTokens', res); + console.log('userfcmtokemArray', userfcmtokemArray); + if (res.data.status) { + setAdmintoken(userfcmtokemArray); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + + useEffect(() => { + if (starttime && endtime) { + fetchAppAdminTokens(); + } + }, [starttime, endtime]); + + // =============================================== || opentoast || =============================================== + + const opentoast = (message, variant, time) => { + enqueueSnackbar(message, { + variant: variant, + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: time ? time : 1500 + }); + console.log(alertmessage); + }; + + function closeAddressModal() { + setOpen2(false); + } + function closetimemodal() { + setOpen3(false); + setCurrentsno(''); + } + + // =============================================== || createsubmitobj1 (create orders) || =============================================== + const createsubmitobj1 = async () => { + let arr = []; + let timecheck = false; + let addresscheck = false; + arr = [ + { + orderheaderid: 0, + tenantid: parseFloat(tid), + locationid: clientinfo.tenantlocationid ? clientinfo.tenantlocationid : tenantinfo.tenantlocationid, + moduleid: tenant.moduleid, + configid: 7, + orderid: '', + customerid: tenantinfo.customerid, + orderdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), + orderstatus: 'created', + pending: '', + processing: '', + ready: '', + delivered: '', + cancellled: '', + orderamount: totalCharge, + taxamount: 0, + ordercharges: 0, + ordervalue: totalCharge, + itemcount: 1, + paymenttype: 64, + paymentstatus: 0, + deliverycharge: totalCharge, + deliverytime: `${dayjs(startdate).format('YYYY-MM-DD')} ${dayjs(selectedtime.$d).format('HH:mm:ss')}`, + deliverylocationid: clientinfo.deliverylocationid, + deliveryaddress: clientinfo.address, + deliverytype: deliverytype, + pickupaddress: tenantinfo.address, + pickuplat: startPoint.latitude.toString(), + pickuplong: startPoint.longitude.toString(), + ordernotes: otherinstructions, + remarks: '', + tenantuserid: parseInt(localStorage.getItem('userid')), + categoryid: tenant.categoryid, + subcategoryid: tenant.subcategoryid, + partnerid: tenant.partnerid, + deliverylat: endPoint.latitude.toString(), + deliverylong: endPoint.longitude.toString(), + applocationid: parseInt(localStorage.getItem('applocationid')), + deliveryid: clientinfo.customerid, + pickuplocationid: tenantinfo.deliverylocationid, + kms: distance + } + ]; + + if (!tid) { + opentoast('Choose Client ', 'warning'); + } else if (!tenantinfo.address) { + opentoast('Choose Pickup Location', 'warning'); + } else if (!clientinfo.address) { + opentoast('Choose Drop Location', 'warning'); + } else if (!selectedtime) { + opentoast('Choose Schedule Time', 'warning'); + } else { + setLoading(true); + + await axios + .post(`${process.env.REACT_APP_URL}/orders/create/grouporders`, arr) + .then((res) => { + if (res.data.status) { + enqueueSnackbar('Order Created Successfully', { + variant: 'success', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + if (admintoken) { + // notifyadmin(admintoken); + sendnotifications(); + } + + navigate('/nearle/orders'); + } else { + opentoast(res.data.message, 'warning'); + } + setLoading(false); + console.log(res); + }) + .catch((err) => { + console.log(err); + opentoast(err.data.message, 'warning'); + setLoading(false); + }); + } + console.log(arr); + }; + + const createsubmitobj2 = async () => { + let arr = {}; + arr = { + orders: { + applocationid: tenant.applolcationid, + cancellled: '', + categoryid: +tenant.categoryid, + configid: +localStorage.getItem('configid'), + customerid: +pickCust.customerid || 0, + deliveryaddress: dropCust.address || '', + deliverycharge: +totalCharge.toFixed(2) || 0, + deliverycity: dropCust.city || '', + deliverycontactno: dropCust.contactno || '', + deliverycustomer: dropCust.firstname || '', + deliveryid: dropCust.customerid || 0, + deliverylandmark: dropCust.landmark || '', + deliverylat: dropCust.latitude.toString(), + deliverylocation: dropCust.suburb || '', + deliverylocationid: dropCust.deliverylocationid || 0, + deliverylong: dropCust.longitude.toString(), + deliverytime: `${dayjs(startdate).format('YYYY-MM-DD')} ${dayjs(selectedtime.$d).format('HH:mm:ss')}`, + deliverytype: pickCust.customerid !== 0 || dropCust.customerid !== 0 ? 'B' : 'C', + delivered: '', + itemcount: 1, + kms: distance || 0, + locationid: +tenanatLocoId, //main or branch + moduleid: +tenant.moduleid, + orderamount: +totalCharge.toFixed(2) || 0, + ordercharges: 0.0, + orderdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), + orderheaderid: 0, + orderid: '', // + ordernotes: otherinstructions, + orderstatus: 'created', + ordervalue: +totalCharge.toFixed(2) || 0, + partnerid: tenant.partnerid, + paymentstatus: 1, + paymenttype: 42, + pending: '', + pickupaddress: pickCust.address || '', + pickupcity: pickCust.city || '', + pickupcontactno: pickCust.contactno || '', + pickupcustomer: pickCust.firstname || '', + pickuplandmark: pickCust.landmark || '', + pickuplat: pickCust.latitude.toString(), + pickuplocation: pickCust.suburb || '', + pickuplocationid: pickCust.deliverylocationid || 0, + pickuplong: pickCust.longitude.toString(), + pickupaddress: pickCust.address || '', + processing: '', + ready: '', + remarks: '', + smsdelivery: 1, + subcategoryid: +subCatId, + taxamount: 0.0, + tenantid: tenant.tenantid, + tenantuserid: parseInt(localStorage.getItem('userid')) + }, + + pickup: { + address: pickCust.address || '', + applocationid: tenant.applolcationid, + city: pickCust.city || '', + configid: parseInt(localStorage.getItem('configid')), + contactno: pickCust.contactno || '', + customertoken: '', + customerid: pickCust.customerid || 0, + devicetype: '', + deviceid: '', + dialcode: '+91', + doorno: pickCust.doorno || '', + email: pickCust.email || '', + firstname: pickCust.firstname || '', + landmark: pickCust.landmark || '', + latitude: pickCust.latitude.toString() || '', + longitude: pickCust.longitude.toString() || '', + locationid: pickCust.deliverylocationid || 0, + postcode: pickCust.postcode || '', + primaryaddress: 1, + profileimage: '', + state: pickCust.state || '', + suburb: pickCust.suburb || '', + tenantid: tenant.tenantid + }, + + drop: { + address: dropCust.address || '', + applocationid: tenant.applolcationid, + city: dropCust.city || '', + configid: parseInt(localStorage.getItem('configid')), + contactno: dropCust.contactno || '', + customertoken: '', + customerid: dropCust.customerid || 0, + devicetype: '', + deviceid: '', + dialcode: '+91', + doorno: dropCust.doorno || '', + email: dropCust.email || '', + firstname: dropCust.firstname || '', + landmark: dropCust.landmark || '', + latitude: dropCust.latitude.toString(), + longitude: dropCust.longitude.toString(), + locationid: dropCust.deliverylocationid || 0, + postcode: dropCust.postcode || '', + primaryaddress: 1, + profileimage: '', + state: dropCust.state || '', + suburb: dropCust.suburb || '', + tenantid: tenant.tenantid + } + }; + console.log('createsubmitobj2', arr); + if (!pickCust.firstname) { + opentoast('Enter Pickup Contact Name ', 'warning'); + } else if (!pickCust.contactno) { + opentoast('Enter Pickup Contact Number ', 'warning'); + } else if (!pickCust.suburb) { + opentoast('Enter Pickup Location ', 'warning'); + } else if (!pickCust.city) { + opentoast('Enter Pickup City ', 'warning'); + } else if (!pickCust.postcode) { + opentoast('Enter Pickup Postcode ', 'warning'); + } else if (!pickCust.landmark) { + opentoast('Enter Pickup Landmark ', 'warning'); + } else if (!dropCust.firstname) { + opentoast('Enter Drop Contact Name ', 'warning'); + } else if (!dropCust.contactno) { + opentoast('Enter Drop Contact Number', 'warning'); + } else if (!dropCust.suburb) { + opentoast('Enter Drop Suburb ', 'warning'); + } else if (!dropCust.city) { + opentoast('Enter Drop City ', 'warning'); + } else if (!dropCust.postcode) { + opentoast('Enter Drop postcode ', 'warning'); + } else if (!dropCust.landmark) { + opentoast('Enter Drop Landmark ', 'warning'); + } else if (!selectedtime) { + opentoast('Choose deliverytime ', 'warning'); + } else if (!setSubCatId) { + opentoast('Choose SubCategory ', 'warning'); + } else { + try { + const createRes = await axios.post(`${process.env.REACT_APP_URL2}/orders/createorder`, arr); + // const createRes = await axios.post(`${process.env.REACT_APP_URL}/orders/createorder`, arr); + if (createRes.data.status) { + console.log('createRes', createRes); + enqueueSnackbar('Order Created Successfully', { + variant: 'success', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + if (admintoken) { + // notifyadmin(admintoken); + sendnotifications(); + } + + navigate('/orders'); + } else { + opentoast(res.data.message, 'warning'); + } + setLoading(false); + console.log(res); + } catch (error) { + console.log('createResErr', error); + } + } + }; + + useEffect(() => { + console.log('shiftarr'); + console.log(shiftarr); + }, [shiftarr]); + + const clicked1 = (e) => { + setShift(e); + }; + + const dialogopen = (i, result) => { + console.log(i, result); + setOpen({ shiftsno: result.sno, sno: i.sno }); + }; + const dialogclose = () => { + setOpen(''); + }; + // ========================================================= || clientdetails || ========================================================= + const clientdetails = async () => { + setLoading2(true); + try { + let url = + searchCustList == '' + ? `${process.env.REACT_APP_URL}/customers/getbytid/?tenantid=${tid}&pageno=1&pagesize=10` + : `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tid}&keyword=${searchCustList}`; + + await axios + .get(url) + .then((res) => { + console.log('clientdetails', res.data.details); + if (res.data.status) { + setClientdetail(res.data.details); + setCustomerlist(res.data.details); + let arr = []; + res.data.details.map((val) => { + arr.push({ + label: `${val.firstname} | ${val.contactno}`, + ...val + }); + }); + setClientdetailarr(arr); + } + setLoading2(false); + }) + .catch((err) => { + console.log(err); + setLoading2(false); + opentoast('server error', 'warning'); + }); + } catch (err) { + console.log(err); + setLoading2(false); + } + }; + // ========================================================= || clientdetailsbusiness || ========================================================= + const clientdetailsbusiness = async () => { + setLoading2(true); + try { + await axios + .get(`${process.env.REACT_APP_URL}/customers/getbytid/?tenantid=${tid}&locationid=1`) + .then((res) => { + console.log('clientdetailsbusiness', res.data.details); + if (res.data.status) { + setClientdetail(res.data.details); + // if (!searchword) { + // setClientdetailarr(res.data.details) + // } + let arr = []; + res.data.details.map((val) => { + arr.push({ + label: `${val.firstname} | ${val.contactno}`, + ...val + }); + }); + setClientdetailbusinessarr(arr); + } + setLoading2(false); + }) + .catch((err) => { + console.log(err); + setLoading2(false); + opentoast('server error', 'warning'); + }); + } catch (err) { + console.log(err); + setLoading2(false); + } + }; + + // ================================================== || sendnotifications || ================================================== + const sendnotifications = async () => { + setLoading(true); + await axios + .post(`${process.env.REACT_APP_URL}/utils/sendnotifications`, { + priority: 'high', + registration_ids: admintoken, + data: { + accessid: process.env.REACT_APP_RIDER_ACCESS_ID + }, + notification: { + title: 'Nearle Merchant', + body: 'An Order has been placed successfully,kindly process the same', + sound: 'ring' + } + }) + .then((res) => { + console.log(res); + if (res.data.message == 'Success') { + enqueueSnackbar('Notification sent Successfully', { + variant: 'success', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + enqueueSnackbar(err.message, { + variant: 'error', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + setLoading(false); + }); + }; + // ============================================= || Google Maps Autocomplete(pick) || ============================================= + useEffect(() => { + // Initialize Google Maps Autocomplete + if (inputValue2) { + const autocompleteInput = document.getElementById('addressAuto1'); + const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, { + // types: ['(cities)'], // You can adjust the types parameter based on your requirements + strictBounds: true, + bounds: new window.google.maps.Circle({ + // center: new window.google.maps.LatLng(11.0050707, 76.9509083), + // radius: 100000 + center: new window.google.maps.LatLng(appLocaLat, appLocaLng), + radius: appLocaRadius * 1000 + }).getBounds() + }); + let arr = []; + // Event listener for autocomplete place changed + autocomplete.addListener('place_changed', () => { + const place = autocomplete.getPlace(); + setInputValue2(`${place.name}, ${place.formatted_address}`); + console.log('new place', place); // Do something with the selected place + console.log(' pick (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place + // to trigger getDistance + setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() }); + setValue(place); + setAddress(`${place.name} ${place.formatted_address}`); + setPickCust({ ...pickCust, address: `${place.name} ${place.formatted_address}` }); + const address = { + address: `${place.name} ${place.formatted_address}`, + street_number: '', + route: '', + locality: '', + sublocality_level_1: '', + administrative_area_level_3: '', + administrative_area_level_1: '', + country: '', + postal_code: '' + }; + place.address_components.forEach((component) => { + component.types.forEach((type) => { + switch (type) { + case 'street_number': + address.street_number = component.long_name; + break; + case 'route': + address.route = component.long_name; + break; + case 'locality': + address.locality = component.long_name; + break; + case 'sublocality_level_1': + address.sublocality_level_1 = component.long_name; + break; + case 'administrative_area_level_3': + address.administrative_area_level_3 = component.long_name; + break; + case 'administrative_area_level_1': + address.administrative_area_level_1 = component.long_name; + break; + case 'country': + address.country = component.long_name; + break; + case 'postal_code': + address.postal_code = component.long_name; + break; + // Add more cases as needed for other types + } + }); + }); + + // Use address object as per your requirements + setPickCust({ + ...pickCust, + address: address.address, + doorno: `${address.street_number} ${address.route}`, + suburb: address.sublocality_level_1, + city: address.locality, + postcode: address.postal_code, + latitude: place.geometry.location.lat(), + longitude: place.geometry.location.lng() + }); + console.log('Pick Address:', address); + }); + } + }, [inputValue2]); + // ============================================= || Google Maps Autocomplete(Drop) || ============================================= + + useEffect(() => { + if (inputValue3) { + // Initialize Google Maps Autocomplete + const autocompleteInput = document.getElementById('addressAuto2'); + const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, { + // types: ['(cities)'], // You can adjust the types parameter based on your requirements + strictBounds: true, + bounds: new window.google.maps.Circle({ + // center: new window.google.maps.LatLng(11.0050707, 76.9509083), + center: new window.google.maps.LatLng(appLocaLat, appLocaLng), + radius: appLocaRadius * 1000 //km to m + // radius: 100000 //km to m + }).getBounds() + }); + let arr = []; + // Event listener for autocomplete place changed + autocomplete.addListener('place_changed', () => { + const place = autocomplete.getPlace(); + setInputValue3(`${place.name}, ${place.formatted_address}`); + console.log('new place', place); // Do something with the selected place + console.log('drop (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place + setEndPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() }); + setValue1(place); + setAddress1(`${place.name} ${place.formatted_address}`); + setDropCust({ ...dropCust, address: `${place.name} ${place.formatted_address}` }); + const address = { + address: `${place.name} ${place.formatted_address}`, + street_number: '', + route: '', + locality: '', + sublocality_level_1: '', + administrative_area_level_3: '', + administrative_area_level_1: '', + country: '', + postal_code: '' + }; + place.address_components.forEach((component) => { + component.types.forEach((type) => { + switch (type) { + case 'street_number': + address.street_number = component.long_name; + break; + case 'route': + address.route = component.long_name; + break; + case 'locality': + address.locality = component.long_name; + break; + case 'sublocality_level_1': + address.sublocality_level_1 = component.long_name; + break; + case 'administrative_area_level_3': + address.administrative_area_level_3 = component.long_name; + break; + case 'administrative_area_level_1': + address.administrative_area_level_1 = component.long_name; + break; + case 'country': + address.country = component.long_name; + break; + case 'postal_code': + address.postal_code = component.long_name; + break; + // Add more cases as needed for other types + } + }); + }); + + // Use address object as per your requirements + setDropCust({ + ...dropCust, + address: address.address, + doorno: `${address.street_number} ${address.route}`, + suburb: address.sublocality_level_1, + city: address.locality, + postcode: address.postal_code, + latitude: place.geometry.location.lat(), + longitude: place.geometry.location.lng() + }); + console.log('Drop Address:', address); + }); + } + }, [inputValue3]); + + // ============================================= || gettenantlocations (branches) || ============================================= + const gettenantlocations = async () => { + try { + const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${tid}`); + console.log('gettenantlocations', res.data.details); + setTenantlocations(res.data.details); + if (res.data.details.length == 1) { + setIsLocation(true); + setTenanatLocoId(res.data.details[0].locationid); + } + } catch (err) { + console.log('gettenantlocations', err); + } + }; + useEffect(() => { + gettenantlocations(); + }, []); + + return ( + <> + {loading && } + + + + + + Create Order + {tenantLocations.length == 1 ? ( + + + + ) + }} + /> + ) : ( + `${option.locationname} (${option.locationsuburb})` || ''} + sx={{ width: 300 }} + renderInput={(params) => } + onChange={(event, value, reason) => { + if (value) { + console.log('locationid', value.locationid); + setTenanatLocoId(value.locationid); + setIsLocation(true); + } + if (reason == 'clear') { + setIsLocation(false); + } + }} + /> + )} + + + + + + + + + + {/* ================================================= || Pickup || ================================================= */} + + + + + + Pickup + + {/* Customer + { + if (val) { + setPickupswitch(true); + setDeliverytype('B'); + setTenantinfo({}); + } else { + setPickupswitch(false); + setTenantinfo({}); + setDeliverytype('C'); + } + }} + /> + Business */} + + + + + {/* } + onChange={(e, val) => { + console.log(pickupswitch ? 'clientdetailbusinessarr value' : 'clientdetailarr value', val); + if (val) { + // clientdetails(val.tenantid) + if (clientinfo.customerid == val.customerid) { + opentoast('choose different Location', 'warning'); + setTenantinfo({}); + } else { + setTenantinfo(val); + } + } else { + setTenantinfo({}); + } + + console.log(val); + setStartPoint({ latitude: val.latitude, longitude: val.longitude }); + }} + noOptionsText={ + // + + No options + {!pickupswitch && ( + + )} + + } + /> */} + + {addId1 == 0 ? ( + // (typeof option === 'string' ? option : option.description)} + // filterOptions={(x) => x} + // options={options} + // // options={pickCust} + // autoComplete + // includeInputInList + // filterSelectedOptions + // value={value} + // // value={pickCust.address} + // noOptionsText="No locations" + // onChange={(event, newValue, reason) => { + // if (reason == 'clear') { + // setValue(null); + // } + // console.log('pick location', newValue); + // // setOptions(newValue ? [newValue, ...options] : options); + // setValue(newValue); + // setAddress(newValue.description); + // setPickCust({ ...pickCust, address: newValue.description }); + // }} + // onInputChange={(event, newInputValue) => { + // setInputValue(newInputValue); + // }} + // renderInput={(params) => } + // renderOption={(props, option) => { + // const matches = option.structured_formatting.main_text_matched_substrings || []; + // const parts = parse( + // option.structured_formatting.main_text, + // matches.map((match) => [match.offset, match.offset + match.length]) + // ); + + // return ( + //
  • + // + // + // + // + // + // {parts.map((part, index) => ( + // + // {part.text} + // + // ))} + // + // {option.structured_formatting.secondary_text} + // + // + // + //
  • + // ); + // }} + // /> +
    + setInputValue2(e.target.value)} + InputProps={{ + endAdornment: ( + { + setInputValue2(''); + setPickCust({ + ...pickCust, + doorno: '', + suburb: '', + city: '', + postcode: '', + landmark: '' + }); + setShowDistance(false); + setStartPoint({ latitude: 0, longitude: 0 }); + }} + size="small" + > + + + ) + }} + /> +
    + ) : ( + { + setAddId1(0); + setPickCust({ + ...pickCust, + firstname: '', + contactno: '', + doorno: '', + suburb: '', + city: '', + postcode: '', + landmark: '' + }); + setShowDistance(false); + setStartPoint({ latitude: 0, longitude: 0 }); + }} + > + + + ) + }} + variant="outlined" + placeholder="Select" + value={pickCust.address} + onChange={(e) => { + setPickCust({ ...pickCust, address: e.target.value }); + if (e.target.value == '') { + setAddId1(0); + setShowDistance(false); + setStartPoint({ latitude: 0, longitude: 0 }); + } + }} + /> + )} +
    +
    + + + + Pickup Details + + + {/* ====================================== ||Contact Name (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Contact Name" + value={pickCust.firstname} + onChange={(e) => { + setPickCust({ ...pickCust, firstname: e.target.value }); + }} + /> + + {/* ====================================== ||Contact Number(pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Contact Number" + value={pickCust.contactno} + onChange={(e) => { + if (e.target.value.length <= 10) { + setPickCust({ ...pickCust, contactno: e.target.value }); + } + }} + /> + + {/* ====================================== ||Door No (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Door No / Street" + value={pickCust.doorno} + onChange={(e) => { + setPickCust({ ...pickCust, doorno: e.target.value }); + }} + /> + + {/* ====================================== || Suburb (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Location" + value={pickCust.suburb} + onChange={(e) => { + setPickCust({ ...pickCust, suburb: e.target.value }); + }} + /> + + {/* ====================================== || City (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="City" + value={pickCust.city} + onChange={(e) => { + setPickCust({ ...pickCust, city: e.target.value }); + }} + /> + + {/* ====================================== || postcode (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Postcode" + value={pickCust.postcode} + onChange={(e) => { + setPickCust({ ...pickCust, postcode: e.target.value }); + }} + /> + + {/* ====================================== || Landmark (pick) || ====================================== */} + + + + + ) + }} + variant="outlined" + label="Landmark" + value={pickCust.landmark} + onChange={(e) => { + setPickCust({ ...pickCust, landmark: e.target.value }); + }} + /> + + + + {/* + + */} +
    +
    +
    + {/* ================================================= || Drop || ================================================= */} + + + + + + Drop + + {/* Customer */} + {/* { + if (val) { + setDropswitch(true); + setClientinfo({}); + } else { + setDropswitch(false); + setClientinfo({}); + } + }} + size="small" + /> + Business */} + + + + {/* } + onChange={(e, val) => { + if (val) { + if (tenantinfo.customerid == val.customerid) { + opentoast('choose different Location', 'warning'); + setClientinfo({}); + } else { + setClientinfo(val); + } + } else { + setClientinfo({}); + } + + console.log(val); + setEndPoint({ latitude: val.latitude, longitude: val.longitude }); + }} + noOptionsText={ + // + + No options + {!dropswitch && ( + + )} + + } + /> */} + + {addId2 == 0 ? ( + // (typeof option === 'string' ? option : option.description)} + // filterOptions={(x) => x} + // options={options1} + // autoComplete + // includeInputInList + // filterSelectedOptions + // value={value1} + // // value={dropCust.address} + // noOptionsText="No locations" + // onChange={(event, newValue) => { + // setOptions1(newValue ? [newValue, ...options1] : options1); + // setValue1(newValue); + // console.log(newValue); + // setAddress1(newValue.description); + // setDropCust({ ...dropCust, address: newValue.description }); + // }} + // onInputChange={(event, newInputValue) => { + // setInputValue1(newInputValue); + // }} + // renderInput={(params) => } + // renderOption={(props, option) => { + // const matches = option.structured_formatting.main_text_matched_substrings || []; + // const parts = parse( + // option.structured_formatting.main_text, + // matches.map((match) => [match.offset, match.offset + match.length]) + // ); + + // return ( + //
  • + // + // + // + // + // + // {parts.map((part, index) => ( + // + // {part.text} + // + // ))} + // + // {option.structured_formatting.secondary_text} + // + // + // + //
  • + // ); + // }} + // /> +
    + setInputValue3(e.target.value)} + InputProps={{ + endAdornment: ( + { + setInputValue3(''); + setDropCust({ + ...dropCust, + doorno: '', + suburb: '', + city: '', + postcode: '', + landmark: '' + }); + setShowDistance(false); + setEndPoint({ latitude: 0, longitude: 0 }); + }} + size="small" + > + + + ) + }} + /> +
    + ) : ( + { + setAddId2(0); + setDropCust({ + ...dropCust, + firstname: '', + contactno: '', + doorno: '', + suburb: '', + city: '', + postcode: '', + landmark: '' + }); + setShowDistance(false); + setEndPoint({ latitude: 0, longitude: 0 }); + }} + > + + + ) + }} + variant="outlined" + placeholder="Select" + value={dropCust.address} + onChange={(e) => { + setPickCust({ ...dropCust, address: e.target.value }); + if (e.target.value == '') { + setAddId2(0); + setShowDistance(false); + setEndPoint({ latitude: 0, longitude: 0 }); + } + }} + /> + )} +
    +
    + + + + Drop Details + + + {/* ====================================== ||Contact Name (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.firstname} + onChange={(e) => { + setDropCust({ ...dropCust, firstname: e.target.value }); + }} + /> + + {/* ====================================== ||Contact Number (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.contactno} + onChange={(e) => { + if (e.target.value.length <= 10) { + setDropCust({ ...dropCust, contactno: e.target.value }); + } + }} + /> + + {/* ====================================== ||Door No (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.doorno} + onChange={(e) => { + setDropCust({ ...dropCust, doorno: e.target.value }); + }} + /> + + {/* ====================================== ||Suburb (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.suburb} + onChange={(e) => { + setDropCust({ ...dropCust, suburb: e.target.value }); + }} + /> + + {/* ====================================== ||City (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.city} + onChange={(e) => { + setDropCust({ ...dropCust, city: e.target.value }); + }} + /> + + + {/* ====================================== ||Postcode (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.postcode} + onChange={(e) => { + setDropCust({ ...dropCust, postcode: e.target.value }); + }} + /> + + {/* ====================================== ||Landmark (drop) || ====================================== */} + + + + + ) + }} + value={dropCust.landmark} + onChange={(e) => { + setDropCust({ ...dropCust, landmark: e.target.value }); + }} + /> + + + + {/* + + */} +
    +
    +
    + {/* ================================================= || Time || ================================================= */} + + + + + Date + + { + let dateres11 = dayjs().diff(dayjs(`${dayjs(e).format('YYYY-MM-DD')}`), 'd'); + console.log('dateres11'); + console.log(dateres11); + setSelectedtime(''); + if (dateres11 <= 0) { + console.log('startdate', e); + setStartdate(e); + setEnddate(e); + + let arr = []; + timeslotarr.map((val) => { + if ( + dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0 + ) { + arr.push(val); + } + }); + if (arr[0]) { + setOrderarr([ + { + sno: 1, + address: '', + customerid: '', + deliverytime: dayjs(arr[0]) || '', + deliverylocationid: '', + clientname: '', + contactno: '', + latitude: '', + longitude: '' + } + ]); + } else { + setOrderarr([]); + } + } else { + setAlertmessage('choose Upcoming Date'); + opentoast('choose Upcoming Date', 'warning'); + setStartdate(NaN); + } + }} + value={dayjs(startdate)} + sx={{ width: '100%', mt: 2 }} + disablePast + /> + + + Time + + + + {timeslotarr.map((val, index) => { + if ( + dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0 + ) { + return ( + + + { + if (distance > appLocaRadius) { + setOpen4(true); + } else if (showDistance) { + console.log('selectedtime', val); + setSelectedtime(val); + } else { + opentoast('Select Pickup and Drop', 'error'); + } + }} + /> + + + ); + } + })} + + + + + + + + + {showDistance && ( + + + Distance + + + + + Charges + + + + )} + Category + `${option.subcategoryname}` || ''} + sx={{ my: 2, zIndex: '100' }} + fullWidth + renderInput={(params) => } + onChange={(event, value, reason) => { + if (value) { + console.log(value); + setSubCatName(value.subcategoryname); + setSubCatId(value.subcategoryid); + } + }} + /> + Weight + + { + handleChipClick('1-10kgs'); + setWeight('1-10kgs'); + }} + /> + { + handleChipClick('11-20kgs'); + setWeight('11-20kgs'); + }} + /> + { + handleChipClick('21-30kgs'); + setWeight('21-30kgs'); + }} + /> + + + +
    +
    +
    + {/* ================================================= || Notes || ================================================= */} + + + + setOtherinstructions(e.target.value)} + /> + + {/* */} + + + + +
    + +
    + + + {/* + + + Select Time + + + + + + {timeslotarr.map((val) => { + if ( + dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0 + // && currentsno + ) { + return ( + <> + + { + setSelectedtime(dayjs(val).format('hh:mm A')); + }} + /> + + + ); + } + })} + + + + + + + + */} + + {/* ============================================= || saved address Dialog || ============================================= */} + { + setIsCustomerOpen(false); + }} + fullWidth + sx={{ minWidth: 'lg' }} + > + + + {`Select Location (${pickordrop === 1 ? 'Pickup' : 'Drop'})`} + + + setSearchCustList(e.target.value)} + // onChange={handleSearch} + sx={{ + '& .MuiOutlinedInput-input': { + p: '10.5px 0px 12px' + }, + bgcolor: 'white' + }} + startAdornment={ + + + + } + endAdornment={ + { + setSearchCustList(''); + }} + > + + + } + autoComplete="off" + /> + + + + + + + {customerlist.length == 0 ? ( + + + + ) : ( + + {customerlist && + customerlist.map((address, index) => ( + + ))} + + )} + + + + + {/* */} + + + + { + setOpen4(false); + }} + > + + + + Error + + + + + Service not available at this location + + + { + setOpen4(false); + }} + > + Close + + + + + ); +}; + +export default Createorder1; diff --git a/src/pages/nearle/reports/MapWithRouteGoogle.js b/src/pages/nearle/reports/MapWithRouteGoogle.js index e7e1e5e..7cf75cf 100644 --- a/src/pages/nearle/reports/MapWithRouteGoogle.js +++ b/src/pages/nearle/reports/MapWithRouteGoogle.js @@ -1,5 +1,6 @@ import React, { useEffect, useRef } from 'react'; import { LoadScriptNext, GoogleMap } from '@react-google-maps/api'; +import { DT } from 'themes/dt/tokens'; const containerStyle = { width: '100%', @@ -46,7 +47,7 @@ const MapWithRouteGoogle = ({ coordinates, additionalProps, setMapOpen }) => { const route = new window.google.maps.Polyline({ path: numericCoordinates, geodesic: false, - strokeColor: '#1A73E8', + strokeColor: DT.brand, strokeOpacity: 1.0, strokeWeight: 4 }); @@ -69,11 +70,13 @@ const MapWithRouteGoogle = ({ coordinates, additionalProps, setMapOpen }) => { right: 10, zIndex: 999, padding: '6px 12px', - background: '#1A73E8', + background: DT.brand, color: 'white', - borderRadius: 6, + borderRadius: DT.radiusInner, cursor: 'pointer', - border: 'none' + border: 'none', + fontWeight: 600, + boxShadow: DT.shadowMd }} > Close diff --git a/src/pages/nearle/reports/RiderLocationMap.js b/src/pages/nearle/reports/RiderLocationMap.js deleted file mode 100644 index 5dfabf2..0000000 --- a/src/pages/nearle/reports/RiderLocationMap.js +++ /dev/null @@ -1,76 +0,0 @@ -import { Button } from '@mui/material'; -import { LoadScriptNext, GoogleMap, Marker, OverlayView } from '@react-google-maps/api'; - -const containerStyle = { - width: '100%', - height: 'calc(100vh - 150px)' -}; - -export default function RiderLocationMap({ riderLocations }) { - console.log('riderLocations', riderLocations); - - const center = { - lat: Number(riderLocations?.[0]?.latitude || 11.0056), - lng: Number(riderLocations?.[0]?.longitude || 76.9661) - }; - const GreenIcon = { - url: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png', - scaledSize: new window.google.maps.Size(25, 41), - anchor: new window.google.maps.Point(12, 41) - }; - - const RedIcon = { - url: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png', - scaledSize: new window.google.maps.Size(25, 41), - anchor: new window.google.maps.Point(12, 41) - }; - - return ( - - - {riderLocations && - riderLocations?.map((r, index) => { - const lat = Number(r.latitude); - const lng = Number(r.longitude); - return ( -
    - {/* Marker */} - - -
    - -
    -
    -
    - ); - })} -
    -
    - ); -} diff --git a/src/pages/nearle/reports/RidersRoutes.js b/src/pages/nearle/reports/RidersRoutes.js index 24fc765..c82e945 100644 --- a/src/pages/nearle/reports/RidersRoutes.js +++ b/src/pages/nearle/reports/RidersRoutes.js @@ -107,7 +107,7 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o // brand purple to match the planned-route polyline below. const stepIcon = (n, isFocused) => { const size = isFocused ? 38 : 32; - const color = isFocused ? '#4D1C61' : '#662582'; + const color = isFocused ? '#910E1D' : '#C01227'; const svg = encodeURIComponent( `` + `` + @@ -126,7 +126,7 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o px: 2, py: 1.25, borderBottom: '1px solid rgba(15, 23, 42, 0.08)', - background: 'linear-gradient(135deg, #662582 0%, #9255AB 100%)', + background: 'linear-gradient(135deg, #C01227 0%, #D25463 100%)', color: '#fff', flexShrink: 0 }} @@ -207,12 +207,12 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o {/* Translucent backdrop so the route stays legible on busy tiles. */} {/* Road-following planned route from the Directions API. */} ) : ( @@ -221,7 +221,7 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o { console.log('additionalProps', additionalProps); const mapRef = useRef(null); - const theme = useTheme(); const [routePoints, setRoutePoints] = useState([]); const [loading, setLoading] = useState(false); @@ -145,13 +144,13 @@ const MapWithRoute = ({ coordinates, additionalProps, order, setMapOpen }) => { top: 12, right: 12, zIndex: 2000, - bgcolor: theme.palette.error.main, + bgcolor: DT.brand, color: '#fff', fontWeight: 600, - borderRadius: '12px', + borderRadius: DT.radiusInner + 'px', px: 1.5, py: 0.5, - boxShadow: theme.shadows[4], + boxShadow: DT.shadowMd, cursor: 'pointer', '& .MuiChip-icon': { color: '#fff' } }} @@ -183,7 +182,7 @@ const MapWithRoute = ({ coordinates, additionalProps, order, setMapOpen }) => { width: '100%', bgcolor: 'rgba(255,255,255,0.96)', p: 2, - boxShadow: theme.shadows[3], + boxShadow: DT.shadowPop, zIndex: 1500 }} > diff --git a/src/pages/nearle/reports/ordersDetails.js b/src/pages/nearle/reports/ordersDetails.js index 0698c22..e595f68 100644 --- a/src/pages/nearle/reports/ordersDetails.js +++ b/src/pages/nearle/reports/ordersDetails.js @@ -114,8 +114,8 @@ const soft = (c) => a(c, '18'); const ring = (c) => a(c, '26'); const edge = (c) => a(c, '55'); -const BRAND = '#662582'; -const BRAND_LIGHT = '#9255AB'; +const BRAND = '#C01227'; +const BRAND_LIGHT = '#D25463'; const SoftPaper = (props) => ( ({ '& fieldset': { borderColor: '#e2e8f0', borderWidth: 1 }, '&:hover fieldset': { borderColor: '#cbd5e1' }, '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring(color)}` }, - '&.Mui-focused fieldset': { borderColor: '#662582', borderWidth: 1.5 } + '&.Mui-focused fieldset': { borderColor: '#C01227', borderWidth: 1.5 } } }); @@ -1104,15 +1104,15 @@ export default function OrdersDetails() { flexShrink: 0, cursor: 'pointer', borderRadius: 999, - border: `1px solid ${active ? meta.color : DT.borderSubtle}`, - bgcolor: active ? meta.color : DT.surface, + border: `1px solid ${active ? '#C01227' : DT.borderSubtle}`, + bgcolor: active ? '#C01227' : DT.surface, color: active ? '#fff' : DT.textSecondary, fontWeight: 600, boxShadow: 'none', transition: 'background-color 0.15s, border-color 0.15s, color 0.15s', '&:hover': { - borderColor: active ? meta.color : '#cbd5e1', - bgcolor: active ? meta.color : DT.surfaceAlt + borderColor: active ? '#C01227' : '#cbd5e1', + bgcolor: active ? '#C01227' : DT.surfaceAlt } }} > @@ -1172,7 +1172,7 @@ export default function OrdersDetails() { bgcolor: '#ffffff', '& fieldset': { borderColor: '#e2e8f0', borderWidth: 1 }, '&:hover fieldset': { borderColor: '#cbd5e1' }, - '&.Mui-focused fieldset': { borderColor: '#662582', borderWidth: 1.5 }, + '&.Mui-focused fieldset': { borderColor: '#C01227', borderWidth: 1.5 }, '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring(BRAND)}` } }} /> @@ -1817,10 +1817,21 @@ export default function OrdersDetails() { )} - + {rows?.length !== 0 && ( - - + + {isFetchingNextPage || hasNextPage ? ( ) : ( @@ -1829,7 +1840,7 @@ export default function OrdersDetails() { )} - +
    )}
    diff --git a/src/pages/nearle/reports/ordersSummary.js b/src/pages/nearle/reports/ordersSummary.js index 7a48691..d14068c 100644 --- a/src/pages/nearle/reports/ordersSummary.js +++ b/src/pages/nearle/reports/ordersSummary.js @@ -87,8 +87,8 @@ const soft = (c) => a(c, '18'); const ring = (c) => a(c, '26'); const edge = (c) => a(c, '55'); -const BRAND = '#662582'; -const BRAND_LIGHT = '#9255AB'; +const BRAND = '#C01227'; +const BRAND_LIGHT = '#D25463'; const SoftPaper = (props) => ( ({ '& fieldset': { borderColor: '#e2e8f0', borderWidth: 1 }, '&:hover fieldset': { borderColor: '#cbd5e1' }, '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring(color)}` }, - '&.Mui-focused fieldset': { borderColor: '#662582', borderWidth: 1.5 } + '&.Mui-focused fieldset': { borderColor: '#C01227', borderWidth: 1.5 } } }); @@ -588,7 +588,7 @@ export default function OrdersReport() { bgcolor: '#ffffff', '& fieldset': { borderColor: '#e2e8f0', borderWidth: 1 }, '&:hover fieldset': { borderColor: '#cbd5e1' }, - '&.Mui-focused fieldset': { borderColor: '#662582', borderWidth: 1.5 }, + '&.Mui-focused fieldset': { borderColor: '#C01227', borderWidth: 1.5 }, '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring(BRAND)}` } }} /> diff --git a/src/pages/nearle/reports/profitability.js b/src/pages/nearle/reports/profitability.js deleted file mode 100644 index dd95339..0000000 --- a/src/pages/nearle/reports/profitability.js +++ /dev/null @@ -1,790 +0,0 @@ -import React, { useState, useEffect, useMemo } from 'react'; -import PropTypes from 'prop-types'; -import { useInfiniteQuery } from '@tanstack/react-query'; -import { - Avatar, - Box, - Chip, - Grid, - Paper, - Stack, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Tooltip, - Typography, - useMediaQuery, - useTheme -} from '@mui/material'; -import { - MdMyLocation, - MdCalendarMonth, - MdPerson, - MdOutlineCurrencyRupee, - MdStraighten, - MdPayments, - MdRoute, - MdTrendingUp, - MdTrendingDown -} from 'react-icons/md'; - -import dayjs from 'dayjs'; -var utc = require('dayjs/plugin/utc'); -dayjs.extend(utc); - -import { fetchDeliveries } from 'pages/api/api'; -import Loader from 'components/Loader'; -import DateFilterDialog from 'components/DateFilterDialog'; -import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete'; -import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar'; -import PageHeader from 'components/nearle_components/PageHeader'; -import StatCard from 'components/nearle_components/StatCard'; -import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; - -const DT = { - radiusPill: 999, - radiusCard: 14, - shadowSoft: '0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.05)', - shadowMd: '0 1px 3px rgba(15, 23, 42, 0.06)', - shadowPop: '0 12px 32px rgba(15, 23, 42, 0.12)', - textPrimary: '#0f172a', - textSecondary: '#64748b', - textMuted: '#94a3b8', - borderSubtle: '#e2e8f0', - divider: '#f1f5f9', - surface: '#ffffff', - surfaceAlt: '#f8fafc' -}; - -const aColor = (c, suffix) => `${c}${suffix}`; -const soft = (c) => aColor(c, '18'); -const tint = (c) => aColor(c, '08'); -const edge = (c) => aColor(c, '55'); -const ring = (c) => aColor(c, '26'); - -const BRAND = '#662582'; - -const SoftPaper = (props) => ( - -); - -SoftPaper.propTypes = { - children: PropTypes.node -}; - -const AccentAvatar = ({ color, selected, size = 24, children }) => ( - - {children} - -); - -AccentAvatar.propTypes = { - color: PropTypes.string.isRequired, - selected: PropTypes.bool, - size: PropTypes.number, - children: PropTypes.node -}; - -const MetricPill = ({ color, icon, label, tooltip, minWidth = 80 }) => ( - - - {icon} - {label} - - -); - -MetricPill.propTypes = { - color: PropTypes.string.isRequired, - icon: PropTypes.node, - label: PropTypes.string.isRequired, - tooltip: PropTypes.string, - minWidth: PropTypes.number -}; - -const BATCHES = [ - { id: 'morning', name: 'Morning Batch', startHour: 0, endHour: 8 }, - { id: 'afternoon', name: 'Afternoon Batch', startHour: 9, endHour: 12.5 }, - { id: 'evening', name: 'Evening Batch', startHour: 16, endHour: 19 } -]; - -const getBatchForHour = (h, batches = BATCHES) => { - for (const b of batches) { - if (h >= b.startHour && h < b.endHour) return b.id; - } - return null; -}; - -const getRowBatch = (r, batches = BATCHES) => { - const t = r?.assigntime; - if (!t) return null; - const str = String(t).trim(); - if (/^\d{4}-\d{2}-\d{2}$/.test(str)) return null; - const d = dayjs(t); - if (!d.isValid()) return null; - return getBatchForHour(d.hour() + d.minute() / 60, batches); -}; - -function formatNumberToRupees(value) { - return new Intl.NumberFormat('en-IN', { - style: 'currency', - currency: 'INR', - minimumFractionDigits: 2 - }).format(Number(value) || 0); -} - -export default function ProfitabilityReport() { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('md')); - - const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD')); - const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD')); - const [locaName, setLocoName] = useState('All'); - const [open, setOpen] = useState(false); - const [datestatus, setDatestatus] = useState('Today'); - const [appId, setAppId] = useState(0); - - const [searchword, setSearchword] = useState(''); - const [debouncedSearch, setDebouncedSearch] = useState(''); - - const liveUserid = typeof window !== 'undefined' ? localStorage.getItem('userid') || 0 : 0; - - // Load slots configuration from localStorage to match Dispatch page edits - const customBatches = useMemo(() => { - if (typeof window === 'undefined') return BATCHES; - try { - const raw = window.localStorage.getItem('dispatch.slots.v9'); - if (!raw) return BATCHES; - const parsed = JSON.parse(raw); - if (!Array.isArray(parsed) || parsed.length !== BATCHES.length) return BATCHES; - return parsed.map((s, i) => { - const id = s.id || `slot-${i + 1}`; - const startHour = Number(s.startHour) || 0; - const endHour = Number(s.endHour) || 24; - return { - id, - name: s.name || BATCHES.find((b) => b.id === id)?.name || `Slot ${i + 1}`, - startHour, - endHour - }; - }); - } catch (e) { - return BATCHES; - } - }, []); - - // Fetch all deliveries for the selected date range and zone - const { - data: deliveriesData, - isLoading: isLoadingDeliveries, - fetchNextPage, - hasNextPage, - isFetchingNextPage - } = useInfiniteQuery({ - queryKey: ['fetchdeliveries', appId, liveUserid, 'all', startdate, enddate, 2000, '', 0, 0, 0], - queryFn: fetchDeliveries, - getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined, - refetchOnWindowFocus: false - }); - - // Auto-page through all results - useEffect(() => { - if (hasNextPage && !isFetchingNextPage) { - fetchNextPage(); - } - }, [hasNextPage, isFetchingNextPage, fetchNextPage]); - - // Flatten and deduplicate deliveries by orderid - const liveRows = useMemo(() => { - const all = (deliveriesData?.pages || []).flatMap((p) => p.rows || []); - const seen = new Set(); - const out = []; - for (const r of all) { - const key = r.orderid != null ? String(r.orderid) : null; - if (key && seen.has(key)) continue; - if (key) seen.add(key); - out.push(r); - } - return out; - }, [deliveriesData]); - - // Group deliveries by rider - const ridersList = useMemo(() => { - const riderMap = {}; - liveRows.forEach((r) => { - const key = String(r.userid || r.rider_id || ''); - if (!key || key === 'unassigned' || key === '0') return; - if (!riderMap[key]) { - riderMap[key] = { - id: key, - riderName: r.ridername || r.rider_name || r.username || `Rider ${key}`, - orders: [] - }; - } - if (!riderMap[key].orders.some((existing) => existing.orderid === r.orderid)) { - riderMap[key].orders.push(r); - } - }); - - return Object.values(riderMap) - .map((r) => ({ - ...r, - orders: [...r.orders].sort((a, b) => { - const tA = a.trip_number || 1; - const tB = b.trip_number || 1; - if (tA !== tB) return tA - tB; - return (a.step || 0) - (b.step || 0); - }) - })) - .sort((a, b) => b.orders.length - a.orders.length); - }, [liveRows]); - - // Calculate profitability metrics for all riders - const stats = useMemo(() => { - let activeRiders = 0; - let profitableRiders = 0; - let lossRiders = 0; - let totalKms = 0; - let totalPlannedKms = 0; - let totalActualKms = 0; - - const list = ridersList - .map((r) => { - let rRevenue = 0; - let rKms = 0; - let rPlannedKms = 0; - let rActualKms = 0; - const slotsByDate = {}; - let ordersInSlots = 0; - - r.orders.forEach((o) => { - const status = String(o.orderstatus || '').toLowerCase(); - if (status === 'cancelled' || status === 'skipped') return; - - const slot = getRowBatch(o, customBatches); - if (!slot) return; - - const oKms = parseFloat(o.riderkms || 0); - rKms += oKms; - rPlannedKms += parseFloat(o.kms || 0); - rActualKms += parseFloat(o.actualkms || 0); - rRevenue += oKms <= 8 ? 30 : 30 + (oKms - 8) * 6; - - const dateStr = o.assigntime - ? dayjs(o.assigntime).format('YYYY-MM-DD') - : o.deliverydate - ? dayjs(o.deliverydate).format('YYYY-MM-DD') - : null; - if (!dateStr) return; - - if (!slotsByDate[dateStr]) { - slotsByDate[dateStr] = new Set(); - } - slotsByDate[dateStr].add(slot); - ordersInSlots++; - }); - - if (ordersInSlots === 0) { - return null; - } - - // Sum unique slots per day, capping at 3 slots max per day - let slotCount = 0; - Object.values(slotsByDate).forEach((set) => { - slotCount += Math.min(set.size, 3); - }); - - const rVarCost = rKms * 2.5; - const rFixedCost = slotCount * (500 / 3); - const rTotalCost = rVarCost + rFixedCost; - const rNet = rRevenue - rTotalCost; - const rMargin = rRevenue > 0 ? (rNet / rRevenue) * 100 : 0; - - if (rNet >= 0) { - profitableRiders++; - } else { - lossRiders++; - } - - totalKms += rKms; - totalPlannedKms += rPlannedKms; - totalActualKms += rActualKms; - activeRiders++; - - return { - ...r, - orderCount: ordersInSlots, - kms: rKms, - plannedKms: rPlannedKms, - actualKms: rActualKms, - revenue: rRevenue, - varCost: rVarCost, - fixedCost: rFixedCost, - totalCost: rTotalCost, - net: rNet, - margin: rMargin - }; - }) - .filter(Boolean); - - return { - activeRiders, - profitableRiders, - lossRiders, - totalKms, - totalPlannedKms, - totalActualKms, - enrichedRiders: list - }; - }, [ridersList, customBatches]); - - // Filter riders by search query - const filteredRiders = useMemo(() => { - if (!stats?.enrichedRiders || !Array.isArray(stats.enrichedRiders)) return []; - const baseList = stats.enrichedRiders.filter(Boolean); - if (!debouncedSearch) return baseList; - const q = debouncedSearch.toLowerCase().trim(); - return baseList.filter( - (r) => r && [r.riderName, String(r.id)].filter(Boolean).some((field) => String(field).toLowerCase().includes(q)) - ); - }, [stats?.enrichedRiders, debouncedSearch]); - - const KPI_META = [ - { - key: 'riders', - label: 'Riders Active', - color: BRAND, - icon: MdPerson, - value: stats?.activeRiders ?? 0 - }, - { - key: 'planned-kms', - label: 'Planned KMs', - color: '#0ea5e9', - icon: MdRoute, - value: `${(stats?.totalPlannedKms ?? 0).toFixed(1)} km` - }, - { - key: 'actual-kms', - label: 'Actual KMs', - color: '#f59e0b', - icon: MdMyLocation, - value: `${(stats?.totalActualKms ?? 0).toFixed(1)} km` - }, - { - key: 'rider-kms', - label: 'Rider KMs', - color: BRAND, - icon: MdStraighten, - value: `${(stats?.totalKms ?? 0).toFixed(1)} km` - }, - { - key: 'total-distance', - label: 'Trip KMs', - color: '#10b981', - icon: MdStraighten, - value: `${(stats?.totalKms ?? 0).toFixed(1)} km` - } - ]; - - return ( - <> - {(isLoadingDeliveries || isFetchingNextPage) && } - - {/* Page Header */} - } - placeholder="Select Zone" - paperComponent={SoftPaper} - sx={{ width: { xs: '100%', sm: 280 }, zIndex: 100 }} - /> - } - /> - - {/* KPI Cards Grid */} - - {KPI_META.map((item) => { - const Icon = item.icon; - return ( - - } - color={item.color} - loading={isLoadingDeliveries} - /> - - ); - })} - - - {/* Filter Bar (date + search) */} - - - - - - - - - Profitability Overview · {datestatus} - - - {filteredRiders.length} riders · {stats.profitableRiders} profitable · {stats.lossRiders} at loss - - - - setOpen(true)} - sx={{ - display: 'inline-flex', - alignItems: 'center', - gap: 0.75, - px: 1.25, - py: 0.75, - borderRadius: 999, - cursor: 'pointer', - bgcolor: tint('#f59e0b'), - border: `1.5px solid ${edge('#f59e0b')}`, - color: '#f59e0b', - fontWeight: 800, - fontSize: 12, - ml: 1, - transition: 'all 0.18s', - '&:hover': { borderColor: '#f59e0b', boxShadow: `0 0 0 3px ${ring('#f59e0b')}` } - }} - > - - {dayjs(startdate).format('DD/MM/YY')} – {dayjs(enddate).format('DD/MM/YY')} - - - - - - - - - - {/* Table & Mobile List Container */} - - {isMobile ? ( - - {!filteredRiders || filteredRiders.length === 0 ? ( - - - - - - No riders to show - - - ) : ( - filteredRiders.map((row, index) => { - if (!row) return null; - const isProfit = (row.net ?? 0) >= 0; - return ( - - - - - - - {row.riderName} - - - ID #{row.id} - - - - } - > - - - - - - - - - - - - ); - }) - )} - - ) : ( - - - - - # - Rider - Orders - Rider KMs - Revenue - Fixed Cost - Variable Cost - Total Cost - Net Profit - Margin - - - - {!filteredRiders || filteredRiders.length === 0 ? ( - - - - - - - - No riders to show - - - - - ) : ( - filteredRiders.map((row, index) => { - if (!row) return null; - const isProfit = (row.net ?? 0) >= 0; - return ( - - - - {String(index + 1).padStart(2, '0')} - - - - - - - - - - {row.riderName} - - - ID #{row.id} - - - - - - - {row.orderCount} - - - - } label={`${Math.round(row.kms)} km`} tooltip="KMS" /> - - - } - label={formatNumberToRupees(row.revenue).replace('₹', '').trim()} - tooltip="Revenue" - /> - - - } - label={formatNumberToRupees(row.fixedCost).replace('₹', '').trim()} - tooltip="Fixed Cost" - /> - - - } - label={formatNumberToRupees(row.varCost).replace('₹', '').trim()} - tooltip="Variable Cost" - /> - - - } - label={formatNumberToRupees(row.totalCost).replace('₹', '').trim()} - tooltip="Total Cost" - /> - - - : } - label={`${isProfit ? '+' : ''}${formatNumberToRupees(row.net).replace('₹', '').trim()}`} - tooltip="Net Profit" - /> - - - - - - ); - }) - )} - -
    -
    - )} -
    - - {/* Date Filter Dialog */} - setOpen(false)} - onSelect={(range) => { - setStartdate(range.startDate); - setEnddate(range.endDate); - setDatestatus(range.label); - }} - /> - - ); -} diff --git a/src/pages/nearle/reports/ridersLogs.js b/src/pages/nearle/reports/ridersLogs.js deleted file mode 100644 index 2fa714f..0000000 --- a/src/pages/nearle/reports/ridersLogs.js +++ /dev/null @@ -1,355 +0,0 @@ -import React, { useState, useEffect, Fragment } from 'react'; -import { - Box, - Drawer, - IconButton, - Toolbar, - Typography, - AppBar, - useMediaQuery, - Divider, - List, - ListItem, - ListItemText, - useTheme, - ListItemAvatar, - Stack, - Button, - Checkbox, - Skeleton -} from '@mui/material'; - -import MenuIcon from '@mui/icons-material/Menu'; -import SearchBar from 'components/nearle_components/SearchBar'; -import { useQuery } from '@tanstack/react-query'; -import { fetchRidersLogs } from 'pages/api/api'; -import RiderLocationMap from './RiderLocationMap'; -import MainCard from 'components/MainCard'; -import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; -import dayjs from 'dayjs'; - -import error500 from 'assets/images/maintenance/Error500.png'; - -const drawerWidth = 350; - -const RidersLogs = () => { - const theme = useTheme(); - const isDesktop = useMediaQuery('(min-width:900px)'); - const isMobile = useMediaQuery(theme.breakpoints.down('md')); - const [open, setOpen] = useState(false); - const [selectedRiders, setSelectedRiders] = useState([]); - const [riderSearch, setRiderSearch] = useState(''); - const appId = 1; - const { - data: riders, - isLoading: ridersIsLoading, - isFetching: riderIsFetching, - refetch: riderLogsRefetch, - error: riderLogsError - } = useQuery({ - queryKey: [appId, dayjs().format('YYYY-MM-DD'), riderSearch], - queryFn: fetchRidersLogs, - refetchInterval: 5 * 60 * 1000 - }); - - useEffect(() => { - // const sortedRiders = riders?.sort((a, b) => a.firstname.localeCompare(b.firstname)); - setSelectedRiders(riders); - }, [riders]); - - useEffect(() => { - console.log('selectedRiders', selectedRiders); - }, [selectedRiders]); - - useEffect(() => { - setOpen(isDesktop); - }, [isDesktop]); - - return ( - - - {/* Drawer */} - !isDesktop && setOpen(false)} - ModalProps={{ keepMounted: true }} - sx={{ - '& .MuiDrawer-paper': { - width: isMobile ? '100vw' : drawerWidth, - maxWidth: isMobile ? '100vw' : drawerWidth, - position: 'absolute', - left: 0, - top: 0, - height: '100%', - overflowY: 'auto', - transition: 'transform 0.35s ease-in-out', - zIndex: 13 - } - }} - > - {/* Search */} - - setRiderSearch(e.target.value)} - sx={{ - height: 60, - bgcolor: 'white', - '& .MuiOutlinedInput-notchedOutline': { - borderBottom: '1px solid', - borderColor: theme.palette.secondary.light - } - }} - /> - - - - { - if (e.target.checked) { - setSelectedRiders(riders); - } - }} - /> - - - - - - - {/* Rider List */} - - {/* Individuals */} - {ridersIsLoading || riderIsFetching - ? Array.from({ length: 10 }).map((_, index) => ( - - - - - - - } - secondary={} - /> - - - - - - - - - - )) - : !isMobile && - riders?.map((row) => { - return ( - - - - {row.userid} - - - {dayjs(row.logdate).format('DD/MM/YYYY hh:mm A')} - - - } - > - - { - if (e.target.checked) { - // SELECT ONE RIDER - setSelectedRiders([row]); - } else { - // UNCHECK -> SELECT ALL - setSelectedRiders(riders); - } - }} - /> - - - - {row.username?.slice(0, 25) || ''} - {row.username?.length > 25 && '...'} - - {/* {row.status === 'active' && } */} - - } - secondary={ - - {row.contactno || '##########'} - - } - /> - - - - - ); - })} - - - {/* Mobile: rider rows rendered as app-style cards (same selection behaviour) */} - {isMobile && !ridersIsLoading && !riderIsFetching && ( - - {riders?.map((row) => { - const isActive = row.status == 'active'; - const isSelected = selectedRiders?.length === 1 && selectedRiders[0]?.userid === row?.userid; - return ( - - { - if (e.target.checked) { - setSelectedRiders([row]); - } else { - setSelectedRiders(riders); - } - }} - /> - - - {row.username?.slice(0, 25) || ''} - {row.username?.length > 25 && '...'} - - - {row.contactno || '##########'} - - - - } - > - - - - {row.userid} - - - - - - - ); - })} - - )} - - - {/* AppBar */} - - - - - setOpen(!open)}> - - - - - Riders Locations - - - - - - - - - {/* Map */} - - {(ridersIsLoading || riderIsFetching) && ( - - {/* */} - - - )} - - {selectedRiders?.length > 0 && } - {riderLogsError && ( - - mantis - - )} - - - - ); -}; - -export default RidersLogs; diff --git a/src/pages/nearle/reports/ridersSummary.js b/src/pages/nearle/reports/ridersSummary.js index 300612d..ee16068 100644 --- a/src/pages/nearle/reports/ridersSummary.js +++ b/src/pages/nearle/reports/ridersSummary.js @@ -86,8 +86,8 @@ const soft = (c) => a(c, '18'); const ring = (c) => a(c, '26'); const edge = (c) => a(c, '55'); -const BRAND = '#662582'; -const BRAND_LIGHT = '#9255AB'; +const BRAND = '#C01227'; +const BRAND_LIGHT = '#D25463'; const SoftPaper = (props) => ( { setRouteLoading(true); try { - // /deliveries/getdeliveries treats applocationid=0 differently from a - // real location id — when appId===0 ("All") the backend expects the - // logged-in operator's userid via appuserid instead. Mirrors the - // branching in api.js#fetchDeliveries. - const loggedInUserId = typeof window !== 'undefined' ? localStorage.getItem('userid') || 0 : 0; - const scopeParam = appId === 0 - ? `appuserid=${loggedInUserId}` - : `applocationid=${appId}`; const url = `${process.env.REACT_APP_URL}/deliveries/getdeliveries/` + - `?${scopeParam}` + + `?applocationid=${appId}` + `&status=all` + `&fromdate=${startdate}` + `&todate=${enddate}` + @@ -456,7 +448,7 @@ export default function RidersSummary() { bgcolor: '#ffffff', '& fieldset': { borderColor: '#e2e8f0', borderWidth: 1 }, '&:hover fieldset': { borderColor: '#cbd5e1' }, - '&.Mui-focused fieldset': { borderColor: '#662582', borderWidth: 1.5 }, + '&.Mui-focused fieldset': { borderColor: '#C01227', borderWidth: 1.5 }, '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring(BRAND)}` } }} /> diff --git a/src/pages/nearle/requests/requests.js b/src/pages/nearle/requests/requests.js index 7d13a77..ed9b78c 100644 --- a/src/pages/nearle/requests/requests.js +++ b/src/pages/nearle/requests/requests.js @@ -31,9 +31,12 @@ import { FormLabel, DialogActions, useMediaQuery, - useTheme + useTheme, + Paper } from '@mui/material'; import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; +import { MdPayments } from 'react-icons/md'; +import { DT, tint } from 'themes/dt/tokens'; import { Autocomplete as Autocomplete1 } from '@mui/material'; import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'; @@ -202,7 +205,7 @@ const Requests = () => { const [currenttenantid] = useState(''); const [latlong, setLatlong] = useState({}); Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY); - // const [alertmessage, setAlertmessage] = useState(''); + const [alertmessage, setAlertmessage] = useState(''); // const [toast, setToast] = useState(false); const [rolesarr, setRolesarr] = useState([]); const [roleslist] = useState([]); @@ -215,6 +218,12 @@ const Requests = () => { const [refno, setRefno] = useState(''); const [requestor, setRequestor] = useState(''); const [bankname, setBankname] = useState(''); + const [amount, setAmount] = useState(''); + const [accountno, setAccountno] = useState(''); + const [ifsc, setIfsc] = useState(''); + const [reason, setReason] = useState(''); + const [expandopen, setExpandopen] = useState(''); + const [editexpandopen, setEditexpandopen] = useState(''); useEffect(() => { setRolesarr([ @@ -902,7 +911,7 @@ const Requests = () => { {loading && [0, 1, 2, 3, 4].map((item) => ( - + @@ -923,13 +932,13 @@ const Requests = () => { return ( handleClick(event, row.sno)} header={ - + {row.requestor ? String(row.requestor).charAt(0).toUpperCase() : '#'} @@ -942,7 +951,7 @@ const Requests = () => { {row.amount != null && ( - + )} } @@ -1813,8 +1822,6 @@ const Requests = () => { const [searchword, setSearchword] = useState(''); const [dialogopen, setDialogopen] = useState(false); - // const [expandopen, setExpandopen] = React.useState(''); - // const setinitial = (val)=>{ // if(val){ @@ -1938,26 +1945,52 @@ const Requests = () => { xs={12} // sx={{ mb: -2.25 }} > - - Payment Requests - - + + + + + Payment Requests + + + + - + {/* diff --git a/src/pages/nearle/riders/createrider.js b/src/pages/nearle/riders/createrider.js index 45ac77b..1d61862 100644 --- a/src/pages/nearle/riders/createrider.js +++ b/src/pages/nearle/riders/createrider.js @@ -2,7 +2,9 @@ import { useEffect, useState } from 'react'; // material-ui -import { Box, Button, Grid, InputLabel, MenuItem, Select, Stack, TextField, Typography, useMediaQuery, useTheme } from '@mui/material'; +import { Avatar, Box, Button, Grid, InputLabel, MenuItem, Paper, Select, Stack, TextField, Typography, useMediaQuery, useTheme } from '@mui/material'; +import { MdDirectionsBike } from 'react-icons/md'; +import { DT, tint } from 'themes/dt/tokens'; // third-party // import { PatternFormat } from 'react-number-format'; @@ -113,12 +115,6 @@ const Createrider = () => { }); }; - useEffect(() => { - if (selectedImage) { - setAvatar(URL.createObjectURL(selectedImage)); - } - }, [selectedImage]); - const { ref: materialRef } = usePlacesWidget({ apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY, onPlaceSelected: (place) => { @@ -160,14 +156,6 @@ const Createrider = () => { }); const createprofile = async () => { - console.log('res', businessname, businessno, mobilenumber, emailaddress, address, city, zipcode); - - // if (!businessname) { - // opentoast('Fill Business name') - // } else if (!businessno) { - // opentoast('Fill Registration No') - // } - // else if (!firstname) { opentoast('Fill Full name'); } else if (!mobilenumber) { @@ -260,16 +248,27 @@ const Createrider = () => { - - Create Rider - + + + + + Create Rider + +
    - + { .get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`) .then((res) => { console.log(res); - if (res.data.status) { - setTenantinfo(res.data.details); - } setLoading(false); }) .catch((err) => { @@ -231,12 +231,6 @@ const EditRider = () => { }); }; - useEffect(() => { - if (selectedImage) { - setAvatar(URL.createObjectURL(selectedImage)); - } - }, [selectedImage]); - const { ref: materialRef } = usePlacesWidget({ apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY, onPlaceSelected: (place) => { @@ -265,7 +259,6 @@ const EditRider = () => { setCity(city1 || ''); setState(state1 || ''); - setZipcode(zipcode1 || ''); setSuburb(suburb1 || ''); // setAddress(place.formatted_address) @@ -328,7 +321,7 @@ const EditRider = () => { autoHideDuration: 2000 }); setRiderdata(null); - navigate('/nearle/riders'); + navigate('/doormile/riders'); setLoading(false); } else { enqueueSnackbar('Update Failed', { @@ -349,22 +342,33 @@ const EditRider = () => { )} - Edit Rider + + + + + Edit Rider + - - - - - Did not receive the email? Check your spam filter, or - - Resend code - - - - - ); -}; - -export default AuthCodeVerification; diff --git a/src/themes/astryx.js b/src/themes/astryx.js new file mode 100644 index 0000000..c8b2126 --- /dev/null +++ b/src/themes/astryx.js @@ -0,0 +1,17 @@ +// Astryx brand theme — extends the stock "neutral" theme with Doormile's +// brand red so migrated surfaces match the existing DT token palette +// (see themes/dt/tokens.js). Runtime-injected (not `astryx theme build`) +// since this repo doesn't do SSR — see `astryx docs theme`. +import { defineTheme } from '@astryxdesign/core/theme'; +import { neutralTheme } from '@astryxdesign/theme-neutral'; +import { DT } from 'themes/dt/tokens'; + +export const doormileTheme = defineTheme({ + name: 'doormile', + extends: neutralTheme, + color: { accent: DT.brand, neutralStyle: 'cool' }, + components: { + card: { base: { borderRadius: `${DT.radiusCard}px` } }, + 'text-input': { base: { borderRadius: `${DT.radiusInner}px` } } + } +}); diff --git a/src/themes/dt/tokens.js b/src/themes/dt/tokens.js new file mode 100644 index 0000000..3896018 --- /dev/null +++ b/src/themes/dt/tokens.js @@ -0,0 +1,56 @@ +// ============================================================================ +// Shared "DT" design tokens — canonical values extracted from the polished +// pages (originally deliveries.js's "Batch" dropdown design language). +// Every redesigned operator page should import from here instead of +// redefining its own DT object, so radius/shadow/color decisions stay in one +// place. See CLAUDE.md §6 for the full page-anatomy convention these tokens +// support. +// ============================================================================ +export const DT = { + radiusPill: 999, + radiusCard: 14, + radiusInner: 10, + radiusField: 10, + // Restrained, low-contrast elevation — corporate (Linear/Stripe), not flashy. + shadowSoft: '0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.05)', + shadowMd: '0 1px 3px rgba(15, 23, 42, 0.06)', + shadowPop: '0 12px 32px rgba(15, 23, 42, 0.12)', + textPrimary: '#0f172a', + textSecondary: '#64748b', + textMuted: '#94a3b8', + borderSubtle: '#e2e8f0', + borderHover: '#cbd5e1', + divider: '#f1f5f9', + surface: '#ffffff', + surfaceAlt: '#f8fafc', + brand: '#C01227' +}; + +// Quick alpha helpers (hex + percentage suffix). +export const a = (c, suffix) => `${c}${suffix}`; +export const tint = (c) => a(c, '08'); // very subtle surface tint +export const soft = (c) => a(c, '18'); // soft chip / avatar bg +export const ring = (c) => a(c, '26'); // focus ring color +export const edge = (c) => a(c, '55'); // resting border + +// Pill input sx — used by every filter Autocomplete/TextField on a page. +// Neutral, corporate filter field: white surface, hairline border, brand +// focus ring. Width is driven by parent flex/grid so this helper stays +// width-agnostic. +export const pillFieldSx = () => ({ + cursor: 'pointer', + '& .MuiOutlinedInput-root': { + borderRadius: DT.radiusField + 'px', + bgcolor: DT.surface, + fontWeight: 600, + color: DT.textPrimary, + paddingRight: '8px', + cursor: 'pointer', + transition: 'border-color 0.15s, box-shadow 0.15s', + '& fieldset': { borderColor: DT.borderSubtle, borderWidth: 1 }, + '&:hover fieldset': { borderColor: DT.borderHover }, + '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring(DT.brand)}` }, + '&.Mui-focused fieldset': { borderColor: DT.brand, borderWidth: 1.5 } + }, + '& .MuiAutocomplete-endAdornment .MuiSvgIcon-root': { color: DT.textMuted } +}); diff --git a/src/themes/index.js b/src/themes/index.js index 9444eaf..115d2b6 100644 --- a/src/themes/index.js +++ b/src/themes/index.js @@ -121,7 +121,7 @@ export default function ThemeCustomization({ children }) { // soft slate that tints toward brand purple on hover. '*': { scrollbarWidth: 'thin', // Firefox - scrollbarColor: 'rgba(102, 37, 130, 0.28) transparent' + scrollbarColor: 'rgba(192, 18, 39, 0.28) transparent' }, '*::-webkit-scrollbar': { width: '10px', @@ -131,13 +131,13 @@ export default function ThemeCustomization({ children }) { backgroundColor: 'transparent' }, '*::-webkit-scrollbar-thumb': { - backgroundColor: 'rgba(102, 37, 130, 0.26)', + backgroundColor: 'rgba(192, 18, 39, 0.26)', borderRadius: '8px', border: '2px solid transparent', backgroundClip: 'padding-box' }, '*::-webkit-scrollbar-thumb:hover': { - backgroundColor: 'rgba(102, 37, 130, 0.45)' + backgroundColor: 'rgba(192, 18, 39, 0.45)' }, '*::-webkit-scrollbar-corner': { backgroundColor: 'transparent' diff --git a/src/themes/overrides/Autocomplete.js b/src/themes/overrides/Autocomplete.js index 5c61492..e999218 100644 --- a/src/themes/overrides/Autocomplete.js +++ b/src/themes/overrides/Autocomplete.js @@ -23,10 +23,10 @@ export default function Autocomplete() { borderRadius: 8, margin: '1px 0', '&[aria-selected="true"]': { - backgroundColor: 'rgba(102, 37, 130, 0.08)' + backgroundColor: 'rgba(192, 18, 39, 0.08)' }, '&.Mui-focused': { - backgroundColor: 'rgba(102, 37, 130, 0.06)' + backgroundColor: 'rgba(192, 18, 39, 0.06)' } } }, diff --git a/src/themes/theme/default.js b/src/themes/theme/default.js index 8272d42..bb25d38 100644 --- a/src/themes/theme/default.js +++ b/src/themes/theme/default.js @@ -40,16 +40,16 @@ const Default = (colors) => { // contrastText // }, primary: { - lighter: '#E8D9EF', - 100: '#CBA7DA', - 200: '#AE76C4', - light: '#9255AB', - 400: '#9255AB', - main: '#662582', - dark: '#4D1C61', - 700: '#3A1549', - darker: '#260E30', - 900: '#17081D', + lighter: '#F5DBDE', + 100: '#E9ADB4', + 200: '#DD7E8A', + light: '#D25463', + 400: '#D25463', + main: '#C01227', + dark: '#910E1D', + 700: '#6D0A16', + darker: '#48070F', + 900: '#2B0409', contrastText: '#FFFFFF' }, secondary: { diff --git a/src/utils/locales/en.json b/src/utils/locales/en.json index c8cc420..7c56bce 100644 --- a/src/utils/locales/en.json +++ b/src/utils/locales/en.json @@ -7,16 +7,10 @@ "orders": "Orders", "orderspreview": "Orders Preview", "deliveries": "Deliveries", - "tenants": "Tenants", - "pricing": "Pricing", - "customers": "Customers", "riders": "Riders", "reports": "Reports", "ordersummary": "Orders Summary", "ordersdetails": "Orders Details", "riderssummary": "Riders Summary", - "riderslogs": "Riders Logs", - "invoice": "Invoice", - "dispatch": "Dispatch", - "profitability": "Profitability" + "dispatch": "Dispatch" } diff --git a/src/utils/logger.js b/src/utils/logger.js index 3da46c0..549143b 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -12,7 +12,7 @@ const GLOBAL_LOG_LEVEL = isDev ? LOG_LEVELS.DEBUG : LOG_LEVELS.WARN; const style = (bg, color) => `background: ${bg}; color: ${color}; padding: 2px 5px; border-radius: 4px; font-weight: bold;`; -const PREFIX = '%c[NearlExpress]'; +const PREFIX = '%c[Doormile Express]'; const PREFIX_STYLE = style('#2563eb', '#ffffff'); // Capture original console methods before any global overrides occur diff --git a/yarn.lock b/yarn.lock index 4118619..49c64fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,14 +7,6 @@ resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== -"@ampproject/remapping@^2.2.0": - version "2.2.0" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== - dependencies: - "@jridgewell/gen-mapping" "^0.1.0" - "@jridgewell/trace-mapping" "^0.3.9" - "@ant-design/colors@^7.0.0", "@ant-design/colors@^7.2.0": version "7.2.0" resolved "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.0.tgz" @@ -87,40 +79,54 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.8.3": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== +"@astryxdesign/core@^0.1.9", "@astryxdesign/core@0.1.9": + version "0.1.9" + resolved "https://registry.npmjs.org/@astryxdesign/core/-/core-0.1.9.tgz" + integrity sha512-2ZNypZFfujMPxdU8M9Jbe77vwFPP1FYvA0Pk/3L3OLxEIzi98qLbRJjhDEHLbrflANSFVB4l5+qYpJjYtFFXMQ== dependencies: - "@babel/helper-validator-identifier" "^7.27.1" + intl-messageformat "^11.2.9" + +"@astryxdesign/theme-neutral@^0.1.9": + version "0.1.9" + resolved "https://registry.npmjs.org/@astryxdesign/theme-neutral/-/theme-neutral-0.1.9.tgz" + integrity sha512-HZPJmzg0S+aKuxUBGJ1L9O+bBZzwzWlROey/7VilM8kUzHA4lYrFcIPzRixoiXs/qyR5sSfJE8YG7jsXuAuywA== + dependencies: + lucide-react "^1.18.0" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.29.7", "@babel/code-frame@^7.8.3": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz" + integrity sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw== + dependencies: + "@babel/helper-validator-identifier" "^7.29.7" js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.27.2", "@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.0": - version "7.28.0" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz" - integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.0", "@babel/compat-data@^7.29.7": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz" + integrity sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg== "@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.16.0", "@babel/core@^7.21.3", "@babel/core@^7.21.4", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.4.0-0", "@babel/core@^7.7.2", "@babel/core@^7.8.0", "@babel/core@>=7.11.0": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz" - integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz" + integrity sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA== dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-compilation-targets" "^7.21.4" - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.4" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.4" - "@babel/types" "^7.21.4" - convert-source-map "^1.7.0" + "@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.2" - semver "^6.3.0" + json5 "^2.2.3" + semver "^6.3.1" "@babel/eslint-parser@^7.16.3", "@babel/eslint-parser@^7.21.3": version "7.21.3" @@ -131,13 +137,13 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.0" -"@babel/generator@^7.21.4", "@babel/generator@^7.28.0", "@babel/generator@^7.7.2": - version "7.28.0" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz" - integrity sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg== +"@babel/generator@^7.29.7", "@babel/generator@^7.7.2": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz" + integrity sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ== dependencies: - "@babel/parser" "^7.28.0" - "@babel/types" "^7.28.0" + "@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" @@ -149,13 +155,13 @@ dependencies: "@babel/types" "^7.27.3" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.21.4", "@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2", "@babel/helper-compilation-targets@^7.29.7": + 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== dependencies: - "@babel/compat-data" "^7.27.2" - "@babel/helper-validator-option" "^7.27.1" + "@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" @@ -205,10 +211,10 @@ lodash.debounce "^4.0.8" resolve "^1.22.10" -"@babel/helper-globals@^7.28.0": - version "7.28.0" - resolved "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz" - integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== +"@babel/helper-globals@^7.28.0", "@babel/helper-globals@^7.29.7": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz" + integrity sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA== "@babel/helper-member-expression-to-functions@^7.27.1": version "7.27.1" @@ -218,22 +224,22 @@ "@babel/traverse" "^7.27.1" "@babel/types" "^7.27.1" -"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz" - integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.27.1", "@babel/helper-module-imports@^7.29.7": + 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== dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" + "@babel/traverse" "^7.29.7" + "@babel/types" "^7.29.7" -"@babel/helper-module-transforms@^7.21.2", "@babel/helper-module-transforms@^7.27.1": - version "7.27.3" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz" - integrity sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg== +"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.29.7": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz" + integrity sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.27.3" + "@babel/helper-module-imports" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" + "@babel/traverse" "^7.29.7" "@babel/helper-optimise-call-expression@^7.27.1": version "7.27.1" @@ -242,10 +248,10 @@ dependencies: "@babel/types" "^7.27.1" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz" - integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.29.7", "@babel/helper-plugin-utils@^7.8.0": + 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== "@babel/helper-remap-async-to-generator@^7.27.1": version "7.27.1" @@ -280,20 +286,20 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" - integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== +"@babel/helper-string-parser@^7.29.7": + 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== -"@babel/helper-validator-identifier@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz" - integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== +"@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.29.7": + 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== -"@babel/helper-validator-option@^7.21.0", "@babel/helper-validator-option@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz" - integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== +"@babel/helper-validator-option@^7.21.0", "@babel/helper-validator-option@^7.27.1", "@babel/helper-validator-option@^7.29.7": + 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== "@babel/helper-wrap-function@^7.27.1": version "7.27.1" @@ -304,21 +310,20 @@ "@babel/traverse" "^7.27.1" "@babel/types" "^7.27.1" -"@babel/helpers@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz" - integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== +"@babel/helpers@^7.29.7": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz" + integrity sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg== dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" + "@babel/template" "^7.29.7" + "@babel/types" "^7.29.7" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.21.4", "@babel/parser@^7.27.2", "@babel/parser@^7.28.0": - version "7.28.0" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz" - integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.29.7": + 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== dependencies: - "@babel/types" "^7.28.0" + "@babel/types" "^7.29.7" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.27.1": version "7.27.1" @@ -561,11 +566,11 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.20.0", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz" - integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz" + integrity sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.29.7" "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" @@ -1150,42 +1155,42 @@ "@babel/plugin-transform-modules-commonjs" "^7.21.2" "@babel/plugin-transform-typescript" "^7.21.3" -"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.6", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.26.9" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz" integrity sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.20.7", "@babel/template@^7.27.1", "@babel/template@^7.27.2", "@babel/template@^7.3.3": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz" - integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== +"@babel/template@^7.27.1", "@babel/template@^7.29.7", "@babel/template@^7.3.3": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz" + integrity sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/parser" "^7.27.2" - "@babel/types" "^7.27.1" + "@babel/code-frame" "^7.29.7" + "@babel/parser" "^7.29.7" + "@babel/types" "^7.29.7" -"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.4", "@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0", "@babel/traverse@^7.7.2": - version "7.28.0" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz" - integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg== +"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.29.7", "@babel/traverse@^7.7.2": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz" + integrity sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.0" - "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.0" - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.0" + "@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" -"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.6", "@babel/types@^7.21.0", "@babel/types@^7.21.3", "@babel/types@^7.21.4", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.28.0" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz" - integrity sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg== +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.6", "@babel/types@^7.21.3", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.29.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz" + integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA== dependencies: - "@babel/helper-string-parser" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" + "@babel/helper-string-parser" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" @@ -1341,14 +1346,7 @@ resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz" integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== -"@emotion/is-prop-valid@^0.8.2": - version "0.8.8" - resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz" - integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== - dependencies: - "@emotion/memoize" "0.7.4" - -"@emotion/is-prop-valid@^1.2.0": +"@emotion/is-prop-valid@*", "@emotion/is-prop-valid@^1.2.0": version "1.2.0" resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz" integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg== @@ -1365,12 +1363,7 @@ resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz" integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== -"@emotion/memoize@0.7.4": - version "0.7.4" - resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz" - integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== - -"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.10.6", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0", "@emotion/react@^11.7.1", "@emotion/react@^11.9.0": +"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.10.6", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0", "@emotion/react@^11.9.0": version "11.10.6" resolved "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz" integrity sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw== @@ -1400,7 +1393,7 @@ resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz" integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== -"@emotion/styled@^11.10.6", "@emotion/styled@^11.3.0", "@emotion/styled@^11.6.0", "@emotion/styled@^11.8.1": +"@emotion/styled@^11.10.6", "@emotion/styled@^11.3.0", "@emotion/styled@^11.8.1": version "11.10.6" resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.6.tgz" integrity sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og== @@ -1884,7 +1877,7 @@ "@floating-ui/core" "^1.6.0" "@floating-ui/utils" "^0.2.9" -"@floating-ui/react-dom@^2.0.8": +"@floating-ui/react-dom@^2.0.8", "@floating-ui/react-dom@^2.1.1": version "2.1.2" resolved "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz" integrity sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A== @@ -1896,76 +1889,74 @@ resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz" integrity sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg== -"@formatjs/ecma402-abstract@2.2.4": - version "2.2.4" - resolved "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz" - integrity sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg== +"@formatjs/ecma402-abstract@2.3.6": + version "2.3.6" + resolved "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz" + integrity sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw== dependencies: - "@formatjs/fast-memoize" "2.2.3" - "@formatjs/intl-localematcher" "0.5.8" - tslib "2" + "@formatjs/fast-memoize" "2.2.7" + "@formatjs/intl-localematcher" "0.6.2" + decimal.js "^10.4.3" + tslib "^2.8.0" -"@formatjs/fast-memoize@2.2.3": - version "2.2.3" - resolved "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.3.tgz" - integrity sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA== +"@formatjs/fast-memoize@2.2.7": + version "2.2.7" + resolved "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz" + integrity sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ== dependencies: - tslib "2" + tslib "^2.8.0" -"@formatjs/icu-messageformat-parser@2.9.4": - version "2.9.4" - resolved "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.4.tgz" - integrity sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg== - dependencies: - "@formatjs/ecma402-abstract" "2.2.4" - "@formatjs/icu-skeleton-parser" "1.8.8" - tslib "2" +"@formatjs/fast-memoize@3.1.7": + version "3.1.7" + resolved "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.7.tgz" + integrity sha512-zXfhLpvA6T7+efdt9JLbBwZ00tT7NsBMDVnDu8rpHeNNv8KfRZAMo2gkG0k9lK/Nzc//3kJ9pImsfuJxk3KhUA== -"@formatjs/icu-skeleton-parser@1.8.8": - version "1.8.8" - resolved "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.8.tgz" - integrity sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA== +"@formatjs/icu-messageformat-parser@2.11.4": + version "2.11.4" + resolved "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz" + integrity sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw== dependencies: - "@formatjs/ecma402-abstract" "2.2.4" - tslib "2" + "@formatjs/ecma402-abstract" "2.3.6" + "@formatjs/icu-skeleton-parser" "1.8.16" + tslib "^2.8.0" -"@formatjs/intl-displaynames@6.8.5": - version "6.8.5" - resolved "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.8.5.tgz" - integrity sha512-85b+GdAKCsleS6cqVxf/Aw/uBd+20EM0wDpgaxzHo3RIR3bxF4xCJqH/Grbzx8CXurTgDDZHPdPdwJC+May41w== +"@formatjs/icu-messageformat-parser@3.5.15": + version "3.5.15" + resolved "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.15.tgz" + integrity sha512-5o4grXKotAB3JqQuisLApHG43g17N+paoRTa92Jiz35Zvfemq0cVf4EDvuxyHAzmsJji7igaEowicLO/VmfJ8Q== dependencies: - "@formatjs/ecma402-abstract" "2.2.4" - "@formatjs/intl-localematcher" "0.5.8" - tslib "2" + "@formatjs/icu-skeleton-parser" "2.1.11" -"@formatjs/intl-listformat@7.7.5": - version "7.7.5" - resolved "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.7.5.tgz" - integrity sha512-Wzes10SMNeYgnxYiKsda4rnHP3Q3II4XT2tZyOgnH5fWuHDtIkceuWlRQNsvrI3uiwP4hLqp2XdQTCsfkhXulg== +"@formatjs/icu-skeleton-parser@1.8.16": + version "1.8.16" + resolved "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz" + integrity sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ== dependencies: - "@formatjs/ecma402-abstract" "2.2.4" - "@formatjs/intl-localematcher" "0.5.8" - tslib "2" + "@formatjs/ecma402-abstract" "2.3.6" + tslib "^2.8.0" -"@formatjs/intl-localematcher@0.5.8": - version "0.5.8" - resolved "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz" - integrity sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg== - dependencies: - tslib "2" +"@formatjs/icu-skeleton-parser@2.1.11": + version "2.1.11" + resolved "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.11.tgz" + integrity sha512-j8cUmOJzVgkHuS0QiQ6ga76UIoLOFSAMWhs7aZJztH3aAdCOAE6vpC8KVvFB4cU10ON0y2/5oOVmPJ43s2lTwA== -"@formatjs/intl@2.10.15": - version "2.10.15" - resolved "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.15.tgz" - integrity sha512-i6+xVqT+6KCz7nBfk4ybMXmbKO36tKvbMKtgFz9KV+8idYFyFbfwKooYk8kGjyA5+T5f1kEPQM5IDLXucTAQ9g== +"@formatjs/intl-localematcher@0.6.2": + version "0.6.2" + resolved "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz" + integrity sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA== dependencies: - "@formatjs/ecma402-abstract" "2.2.4" - "@formatjs/fast-memoize" "2.2.3" - "@formatjs/icu-messageformat-parser" "2.9.4" - "@formatjs/intl-displaynames" "6.8.5" - "@formatjs/intl-listformat" "7.7.5" - intl-messageformat "10.7.7" - tslib "2" + tslib "^2.8.0" + +"@formatjs/intl@3.1.8": + version "3.1.8" + resolved "https://registry.npmjs.org/@formatjs/intl/-/intl-3.1.8.tgz" + integrity sha512-LWXgwI5zTMatvR8w8kCNh/priDTOF/ZssokMBHJ7ZWXFoYLVOYo0EJERD9Eajv+xsfQO1QkuAt77KWQ1OI4mOQ== + dependencies: + "@formatjs/ecma402-abstract" "2.3.6" + "@formatjs/fast-memoize" "2.2.7" + "@formatjs/icu-messageformat-parser" "2.11.4" + intl-messageformat "10.7.18" + tslib "^2.8.0" "@googlemaps/js-api-loader@1.16.8": version "1.16.8" @@ -2255,38 +2246,20 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.12": - version "0.3.12" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz" - integrity sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg== +"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5": + 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== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/trace-mapping" "^0.3.24" -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + resolved "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.8" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz" - integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== - dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" "@jridgewell/resolve-uri@^3.1.0": @@ -2294,11 +2267,6 @@ resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1", "@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - "@jridgewell/source-map@^0.3.3": version "0.3.6" resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz" @@ -2307,12 +2275,12 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.4" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz" integrity sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw== -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": version "0.3.29" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz" integrity sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ== @@ -2325,15 +2293,15 @@ resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@mui/base@^5.0.0-alpha.126", "@mui/base@^5.0.0-beta.22": - version "5.0.0-dev.20240529-082515-213b5e33ab" - resolved "https://registry.npmjs.org/@mui/base/-/base-5.0.0-dev.20240529-082515-213b5e33ab.tgz" - integrity sha512-3ic6fc6BHstgM+MGqJEVx3zt9g5THxVXm3VVFUfdeplPqAWWgW2QoKfZDLT10s+pi+MAkpgEBP0kgRidf81Rsw== +"@mui/base@^5.0.0-beta.70": + version "5.0.0-beta.70" + resolved "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.70.tgz" + integrity sha512-Tb/BIhJzb0pa5zv/wu7OdokY9ZKEDqcu1BDFnohyvGCoHuSXbEr90rPq1qeNW3XvTBIbNWHEF7gqge+xpUo6tQ== dependencies: - "@babel/runtime" "^7.24.6" - "@floating-ui/react-dom" "^2.0.8" - "@mui/types" "^7.2.14-dev.20240529-082515-213b5e33ab" - "@mui/utils" "^6.0.0-dev.20240529-082515-213b5e33ab" + "@babel/runtime" "^7.26.0" + "@floating-ui/react-dom" "^2.1.1" + "@mui/types" "~7.2.24" + "@mui/utils" "^6.4.8" "@popperjs/core" "^2.11.8" clsx "^2.1.1" prop-types "^15.8.1" @@ -2356,7 +2324,7 @@ resolved "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.14.tgz" integrity sha512-sbjXW+BBSvmzn61XyTMun899E7nGPTXwqD9drm1jBUAvWEhJpPFIRxwQQiATWZnd9rvdxtnhhdsDxEGWI0jxqA== -"@mui/icons-material@^5.0.4", "@mui/icons-material@^5.14.19": +"@mui/icons-material@^5.14.19": version "5.16.14" resolved "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.14.tgz" integrity sha512-heL4S+EawrP61xMXBm59QH6HODsu0gxtZi5JtnXF2r+rghzyU/3Uftlt1ij8rmJh+cFdKTQug1L9KkZB5JgpMQ== @@ -2376,7 +2344,7 @@ clsx "^2.1.0" prop-types "^15.8.1" -"@mui/material@^5.0.0", "@mui/material@^5.12.1", "@mui/material@^5.2.6", "@mui/material@^5.8.6", "@mui/material@>=5.15.0": +"@mui/material@^5.0.0", "@mui/material@^5.12.1", "@mui/material@^5.15.14 || ^6.0.0 || ^7.0.0", "@mui/material@>=5.15.0": version "5.16.14" resolved "https://registry.npmjs.org/@mui/material/-/material-5.16.14.tgz" integrity sha512-eSXQVCMKU2xc7EcTxe/X/rC9QsV2jUe8eLM3MUCPYbo6V52eCE436akRIvELq/AqZpxx2bwkq7HC0cRhLB+yaw== @@ -2413,7 +2381,7 @@ csstype "^3.1.3" prop-types "^15.8.1" -"@mui/system@^5.0.6", "@mui/system@^5.16.12", "@mui/system@^5.16.14", "@mui/system@^5.8.0": +"@mui/system@^5.15.14 || ^6.0.0 || ^7.0.0", "@mui/system@^5.16.12", "@mui/system@^5.16.14": version "5.16.14" resolved "https://registry.npmjs.org/@mui/system/-/system-5.16.14.tgz" integrity sha512-KBxMwCb8mSIABnKvoGbvM33XHyT+sN0BzEBG+rsSc0lLQGzs7127KWkCA6/H8h6LZ00XpBEME5MAj8mZLiQ1tw== @@ -2427,22 +2395,10 @@ csstype "^3.1.3" prop-types "^15.8.1" -"@mui/types@^7.2.14-dev.20240529-082515-213b5e33ab", "@mui/types@^7.2.15", "@mui/types@^7.2.21": - version "7.2.21" - resolved "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz" - integrity sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww== - -"@mui/utils@^5.14.16": - version "5.16.14" - resolved "https://registry.npmjs.org/@mui/utils/-/utils-5.16.14.tgz" - integrity sha512-wn1QZkRzSmeXD1IguBVvJJHV3s6rxJrfb6YuC9Kk6Noh9f8Fb54nUs5JRkKm+BOerRhj5fLg05Dhx/H3Ofb8Mg== - 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" +"@mui/types@^7.2.15", "@mui/types@~7.2.24": + version "7.2.24" + resolved "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz" + integrity sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw== "@mui/utils@^5.16.12": version "5.16.14" @@ -2468,31 +2424,39 @@ prop-types "^15.8.1" react-is "^19.0.0" -"@mui/utils@^6.0.0-dev.20240529-082515-213b5e33ab": - version "6.4.3" - resolved "https://registry.npmjs.org/@mui/utils/-/utils-6.4.3.tgz" - integrity sha512-jxHRHh3BqVXE9ABxDm+Tc3wlBooYz/4XPa0+4AI+iF38rV1/+btJmSUgG4shDtSWVs/I97aDn5jBCt6SF2Uq2A== +"@mui/utils@^5.16.6 || ^6.0.0 || ^7.0.0", "@mui/utils@^6.4.8": + version "6.4.9" + resolved "https://registry.npmjs.org/@mui/utils/-/utils-6.4.9.tgz" + integrity sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg== dependencies: "@babel/runtime" "^7.26.0" - "@mui/types" "^7.2.21" + "@mui/types" "~7.2.24" "@types/prop-types" "^15.7.14" clsx "^2.1.1" prop-types "^15.8.1" react-is "^19.0.0" -"@mui/x-date-pickers@^6.18.2": - 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== +"@mui/x-date-pickers@^7.29.4": + version "7.29.4" + resolved "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.29.4.tgz" + integrity sha512-wJ3tsqk/y6dp+mXGtT9czciAMEO5Zr3IIAHg9x6IL0Eqanqy0N3chbmQQZv3iq0m2qUpQDLvZ4utZBUTJdjNzw== 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" + "@babel/runtime" "^7.25.7" + "@mui/utils" "^5.16.6 || ^6.0.0 || ^7.0.0" + "@mui/x-internals" "7.29.0" + "@types/react-transition-group" "^4.4.11" + clsx "^2.1.1" prop-types "^15.8.1" react-transition-group "^4.4.5" +"@mui/x-internals@7.29.0": + version "7.29.0" + resolved "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.29.0.tgz" + integrity sha512-+Gk6VTZIFD70XreWvdXBwKd8GZ2FlSCuecQFzm6znwqXg1ZsndavrhG9tkxpxo2fM1Zf7Tk8+HcOO0hCbhTQFA== + dependencies: + "@babel/runtime" "^7.25.7" + "@mui/utils" "^5.16.6 || ^6.0.0 || ^7.0.0" + "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz" @@ -2718,20 +2682,22 @@ resolved "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.20.0.tgz" integrity sha512-tieX9Va5w1yP88vMgfH1pHTacDQ9TgDTjox3tLlisKDXRQWdjw+QeVVghhf5XqqIxXHgPdcGwBvKY6UP+SIvLw== -"@react-leaflet/core@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz" - integrity sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg== +"@react-leaflet/core@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz" + integrity sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ== -"@reduxjs/toolkit@^1.9.5": - version "1.9.5" - resolved "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz" - integrity sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ== +"@reduxjs/toolkit@^2.12.0": + version "2.12.0" + resolved "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz" + integrity sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw== dependencies: - immer "^9.0.21" - redux "^4.2.1" - redux-thunk "^2.4.2" - reselect "^4.1.8" + "@standard-schema/spec" "^1.0.0" + "@standard-schema/utils" "^0.3.0" + immer "^11.0.0" + redux "^5.0.1" + redux-thunk "^3.1.0" + reselect "^5.1.0" "@remix-run/router@1.22.0": version "1.22.0" @@ -2804,6 +2770,25 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@standard-schema/spec@^1.0.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz" + integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== + +"@standard-schema/utils@^0.3.0": + version "0.3.0" + resolved "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz" + integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g== + +"@stylexjs/stylex@^0.19.0": + version "0.19.0" + resolved "https://registry.npmjs.org/@stylexjs/stylex/-/stylex-0.19.0.tgz" + integrity sha512-CnUFp7YMaDLDeemsWOfJgoC/gKM5P/yBNMcpJaE6ChJmXr7s0DJwSeGTTlHJcqqwN9OW1qGtmARWLFhGZN1pTA== + dependencies: + css-mediaquery "^0.1.2" + invariant "^2.2.4" + styleq "0.2.1" + "@surma/rollup-plugin-off-main-thread@^2.2.3": version "2.2.3" resolved "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz" @@ -3022,17 +3007,17 @@ "@svgr/plugin-jsx" "^7.0.0" "@svgr/plugin-svgo" "^7.0.0" -"@tanstack/query-core@5.17.9": - version "5.17.9" - resolved "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.17.9.tgz" - integrity sha512-8xcvpWIPaRMDNLMvG9ugcUJMgFK316ZsqkPPbsI+TMZsb10N9jk0B6XgPk4/kgWC2ziHyWR7n7wUhxmD0pChQw== +"@tanstack/query-core@5.101.4": + version "5.101.4" + resolved "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz" + integrity sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw== -"@tanstack/react-query@^5.17.9": - version "5.17.9" - resolved "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.17.9.tgz" - integrity sha512-M5E9gwUq1Stby/pdlYjBlL24euIVuGbWKIFCbtnQxSdXI4PgzjTSdXdV3QE6fc+itF+TUvX/JPTKIwq8yuBXcg== +"@tanstack/react-query@^5.101.4": + version "5.101.4" + resolved "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.4.tgz" + integrity sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA== dependencies: - "@tanstack/query-core" "5.17.9" + "@tanstack/query-core" "5.101.4" "@tootallnate/once@1": version "1.1.2" @@ -3164,7 +3149,7 @@ dependencies: "@types/node" "*" -"@types/hoist-non-react-statics@^3.3.1", "@types/hoist-non-react-statics@>= 3.3.1", "@types/hoist-non-react-statics@3": +"@types/hoist-non-react-statics@^3.3.1", "@types/hoist-non-react-statics@>= 3.3.1": version "3.3.1" resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz" integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== @@ -3267,12 +3252,12 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.8": +"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.11": 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== -"@types/react@*", "@types/react@^16.8 || ^17.0 || ^18.0", "@types/react@^17.0.0 || ^18.0.0", "@types/react@^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react@>= 16", "@types/react@16 || 17 || 18": +"@types/react@*", "@types/react@^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react@^18.2.25 || ^19", "@types/react@>= 16", "@types/react@16 || 17 || 18 || 19": version "18.3.18" resolved "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz" integrity sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ== @@ -3338,10 +3323,10 @@ resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== -"@types/use-sync-external-store@^0.0.3": - version "0.0.3" - resolved "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz" - integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== +"@types/use-sync-external-store@^0.0.6": + version "0.0.6" + resolved "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz" + integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg== "@types/ws@^8.5.5": version "8.5.14" @@ -4643,7 +4628,7 @@ clsx@^1.1.0: resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== -clsx@^2.0.0, clsx@^2.1.0, clsx@^2.1.1: +clsx@^2.1.0, clsx@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== @@ -4805,6 +4790,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + 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== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" @@ -4977,6 +4967,11 @@ css-loader@^6.5.1: postcss-value-parser "^4.2.0" semver "^7.5.4" +css-mediaquery@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz" + integrity sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q== + css-minimizer-webpack-plugin@^3.2.0: version "3.4.1" resolved "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz" @@ -5221,7 +5216,7 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -"date-fns@^2.25.0 || ^3.2.0", date-fns@^2.28.0, date-fns@^2.30.0, "date-fns@>= 2.x": +"date-fns@^2.25.0 || ^3.2.0 || ^4.0.0", date-fns@^2.30.0, "date-fns@>= 2.x": version "2.30.0" resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== @@ -5261,7 +5256,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -decimal.js@^10.2.1: +decimal.js@^10.2.1, decimal.js@^10.4.3: version "10.5.0" resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz" integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== @@ -6519,14 +6514,14 @@ fraction.js@^4.3.7: resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== -framer-motion@^10.12.4: - version "10.18.0" - resolved "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz" - integrity sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w== +framer-motion@^12.42.2: + version "12.42.2" + resolved "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.2.tgz" + integrity sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw== dependencies: + motion-dom "^12.42.2" + motion-utils "^12.39.0" tslib "^2.4.0" - optionalDependencies: - "@emotion/is-prop-valid" "^0.8.2" fresh@0.5.2: version "0.5.2" @@ -6865,7 +6860,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2, hoist-non-react-statics@3: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: 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== @@ -7052,7 +7047,12 @@ ignore@^5.2.0: resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -immer@^9.0.21, immer@^9.0.7: +immer@^11.0.0: + version "11.1.15" + resolved "https://registry.npmjs.org/immer/-/immer-11.1.15.tgz" + integrity sha512-VrNANlmnWQnh5COXIIOQXM9oOJw7naGKlBT74ZOOR6lpVXc3gFEu9FJLDFcpCJ2j+NWr8TIwtWD//T6ZX6TKiQ== + +immer@^9.0.7: version "9.0.21" resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz" integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== @@ -7110,17 +7110,25 @@ internal-slot@^1.1.0: hasown "^2.0.2" side-channel "^1.1.0" -intl-messageformat@10.7.7: - version "10.7.7" - resolved "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.7.tgz" - integrity sha512-F134jIoeYMro/3I0h08D0Yt4N9o9pjddU/4IIxMMURqbAtI2wu70X8hvG1V48W49zXHXv3RKSF/po+0fDfsGjA== +intl-messageformat@^11.2.9: + version "11.2.12" + resolved "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.12.tgz" + integrity sha512-KW70Xxfcvy7vV3qODfvShWkFDPMqKDAa4N+hSyVBWGNtVhTUFYaqlD/l88DaYPKiVcPP4rPQ3qnH7i5K82Mg7g== dependencies: - "@formatjs/ecma402-abstract" "2.2.4" - "@formatjs/fast-memoize" "2.2.3" - "@formatjs/icu-messageformat-parser" "2.9.4" - tslib "2" + "@formatjs/fast-memoize" "3.1.7" + "@formatjs/icu-messageformat-parser" "3.5.15" -invariant@2.2.4: +intl-messageformat@10.7.18: + version "10.7.18" + resolved "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz" + integrity sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g== + dependencies: + "@formatjs/ecma402-abstract" "2.3.6" + "@formatjs/fast-memoize" "2.2.7" + "@formatjs/icu-messageformat-parser" "2.11.4" + tslib "^2.8.0" + +invariant@^2.2.4, invariant@2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -8083,7 +8091,7 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.0, json5@^2.2.2: +json5@^2.1.2, json5@^2.2.0, json5@^2.2.3: version "2.2.3" resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -8373,7 +8381,7 @@ long@^5.0.0: resolved "https://registry.npmjs.org/long/-/long-5.3.1.tgz" integrity sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -8399,6 +8407,11 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lucide-react@^1.18.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lucide-react/-/lucide-react-1.27.0.tgz" + integrity sha512-rJicGl/3Fly/E0rOH1YmPZ6e49JCnKknh1ox1vpHnkfjujAkKA6sqUZvH3MTAaXXjgexyUwgNwTJzTtYuAFYJw== + magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.9" resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz" @@ -8587,6 +8600,18 @@ mkdirp@~0.5.1: dependencies: minimist "^1.2.6" +motion-dom@^12.42.2: + version "12.42.2" + resolved "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.2.tgz" + integrity sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA== + dependencies: + motion-utils "^12.39.0" + +motion-utils@^12.39.0: + version "12.39.0" + resolved "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz" + integrity sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ== + ms@^2.1.1, ms@^2.1.3, ms@2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" @@ -8597,13 +8622,6 @@ ms@2.0.0: resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -mui-daterange-picker@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/mui-daterange-picker/-/mui-daterange-picker-1.0.5.tgz" - integrity sha512-+7Mp9DXQw6iLGotwp1rKfMV8sJxtaEto0IVId54orkvrCdqTgn167nzLBz6hd34tJLftg6DKAygkUom7tmg5Bw== - dependencies: - "@babel/runtime" "^7.16.7" - multicast-dns@^7.2.5: version "7.2.5" resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" @@ -10299,13 +10317,12 @@ react-dnd@^16.0.1: fast-deep-equal "^3.1.3" hoist-non-react-statics "^3.3.2" -react-dom@*, "react-dom@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8 || ^17 || ^18 || ^19", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0 || ^19.0.0", react-dom@^17.0.2, react-dom@^18.0.0, react-dom@^18.2.0, "react-dom@>= 0.14.0", react-dom@>=16.0.0, react-dom@>=16.11.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, react-dom@>=16.9.0, "react-dom@16.2.0 - 18": - version "18.3.1" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz" - integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== +react-dom@*, "react-dom@^16.8 || ^17 || ^18 || ^19", "react-dom@^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^18.0.0 || ^19.0.0", react-dom@^19.0.0, react-dom@^19.2.8, "react-dom@>= 0.14.0", react-dom@>=16.0.0, react-dom@>=16.11.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, react-dom@>=16.9.0, react-dom@>=19.0.0: + version "19.2.8" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz" + integrity sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ== dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.2" + scheduler "^0.27.0" react-error-overlay@^6.0.11, react-error-overlay@6.0.11: version "6.0.11" @@ -10337,21 +10354,19 @@ react-icons@^4.12.0: resolved "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz" integrity sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw== -react-intl@^6.4.1: - version "6.8.9" - resolved "https://registry.npmjs.org/react-intl/-/react-intl-6.8.9.tgz" - integrity sha512-TUfj5E7lyUDvz/GtovC9OMh441kBr08rtIbgh3p0R8iF3hVY+V2W9Am7rb8BpJ/29BH1utJOqOOhmvEVh3GfZg== +react-intl@^7.1.14: + version "7.1.14" + resolved "https://registry.npmjs.org/react-intl/-/react-intl-7.1.14.tgz" + integrity sha512-VE/0Wi/lHJlBC7APQpCzLUdIt3GB5B0GZrRW8Q+ACbkHI4j+Wwgg9J1TniN6zmLHmPH5gxXcMy+fkSPfw5p1WQ== dependencies: - "@formatjs/ecma402-abstract" "2.2.4" - "@formatjs/icu-messageformat-parser" "2.9.4" - "@formatjs/intl" "2.10.15" - "@formatjs/intl-displaynames" "6.8.5" - "@formatjs/intl-listformat" "7.7.5" - "@types/hoist-non-react-statics" "3" - "@types/react" "16 || 17 || 18" - hoist-non-react-statics "3" - intl-messageformat "10.7.7" - tslib "2" + "@formatjs/ecma402-abstract" "2.3.6" + "@formatjs/icu-messageformat-parser" "2.11.4" + "@formatjs/intl" "3.1.8" + "@types/hoist-non-react-statics" "^3.3.1" + "@types/react" "16 || 17 || 18 || 19" + hoist-non-react-statics "^3.3.2" + intl-messageformat "10.7.18" + tslib "^2.8.0" react-is@^16.13.1: version "16.13.1" @@ -10383,29 +10398,25 @@ react-is@^19.0.0: resolved "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz" integrity sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g== -react-leaflet@^4.2.1: - version "4.2.1" - resolved "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz" - integrity sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q== +react-leaflet@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz" + integrity sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw== dependencies: - "@react-leaflet/core" "^2.1.0" + "@react-leaflet/core" "^3.0.0" react-loading-icons@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/react-loading-icons/-/react-loading-icons-1.1.0.tgz" integrity sha512-Y9eZ6HAufmUd8DIQd6rFrx5Bt/oDlTM9Nsjvf8YpajTa3dI8cLNU8jUN5z7KTANU+Yd6/KJuBjxVlrU2dMw33g== -"react-redux@^7.2.1 || ^8.0.2", react-redux@^8.0.5: - version "8.1.3" - resolved "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz" - integrity sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw== +"react-redux@^7.2.1 || ^8.1.3 || ^9.0.0", react-redux@^9.2.0: + version "9.2.0" + resolved "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz" + integrity sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g== dependencies: - "@babel/runtime" "^7.12.1" - "@types/hoist-non-react-statics" "^3.3.1" - "@types/use-sync-external-store" "^0.0.3" - hoist-non-react-statics "^3.3.2" - react-is "^18.0.0" - use-sync-external-store "^1.0.0" + "@types/use-sync-external-store" "^0.0.6" + use-sync-external-store "^1.4.0" react-refresh@^0.11.0, "react-refresh@>=0.10.0 <1.0.0": version "0.11.0" @@ -10487,10 +10498,10 @@ react-timer-hook@^3.0.5: resolved "https://registry.npmjs.org/react-timer-hook/-/react-timer-hook-3.0.8.tgz" integrity sha512-bi2e7DhPBU1MRPU4ZHaVqBmgM9e2HK0ae8O2AIqwqjcPo4/qR7lVGQonOQLAKOZPQCJSYfV8F5aBWzOLXElzqQ== -react-to-print@^2.15.0: - version "2.15.1" - resolved "https://registry.npmjs.org/react-to-print/-/react-to-print-2.15.1.tgz" - integrity sha512-1foogIFbCpzAVxydkhBiDfMiFYhIMphiagDOfcG4X/EcQ+fBPqJ0rby9Wv/emzY1YLkIQy/rEgOrWQT+rBKhjw== +react-to-print@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/react-to-print/-/react-to-print-3.3.0.tgz" + integrity sha512-7j9GIeNZA9glZlbv9mIbIHDOOx+WYfRMbJzh04NiSKjdaeGkxJuKjJQrtRuNKtt5AvEVVjrLCPokZ9yJX51Fvg== react-transition-group@^4.4.5: version "4.4.5" @@ -10502,17 +10513,10 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react@*, "react@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.6.0 || 17 || 18", "react@^16.8 || ^17 || ^18 || ^19", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.9.0 || ^17.0.0 || ^18", "react@^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0 || ^19.0.0", react@^17.0.2, react@^18.0.0, react@^18.2.0, react@^18.3.1, "react@>= 0.14.0", "react@>= 16", "react@>= 16.14", react@>=16.0.0, react@>=16.11.0, react@>=16.6.0, react@>=16.8, react@>=16.8.0, react@>=16.9.0, "react@16.2.0 - 18": - 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== - dependencies: - loose-envify "^1.1.0" - -react18-input-otp@^1.1.3: - version "1.1.4" - resolved "https://registry.npmjs.org/react18-input-otp/-/react18-input-otp-1.1.4.tgz" - integrity sha512-35xvmTeuPWIxd0Z0Opx4z3OoMaTmKN4ubirQCx1YMZiNoe+2h1hsOSUco4aKPlGXWZCtXrfOFieAh46vqiK9mA== +react@*, "react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ~19", "react@^16.9.0 || ^17.0.0 || ^18 || ^19", "react@^17.0.0 || ^18.0.0 || ^19.0.0", "react@^18 || ^19", "react@^18.0 || ^19", "react@^18.0.0 || ^19.0.0", react@^19.0.0, react@^19.2.8, "react@>= 0.14.0", "react@>= 16", "react@>= 16.14", react@>=16.0.0, react@>=16.11.0, react@>=16.6.0, react@>=16.8, react@>=16.8.0, react@>=16.9.0, react@>=19, react@>=19.0.0, "react@16 || 17 || 18 || 19": + version "19.2.8" + resolved "https://registry.npmjs.org/react/-/react-19.2.8.tgz" + integrity sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw== read-cache@^1.0.0: version "1.0.0" @@ -10566,18 +10570,23 @@ recursive-readdir@^2.2.2: dependencies: minimatch "^3.0.5" -redux-thunk@^2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz" - integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q== +redux-thunk@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz" + integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== -redux@^4, "redux@^4 || ^5.0.0-beta.0", redux@^4.2.0, redux@^4.2.1: +redux@^4.2.0: version "4.2.1" resolved "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz" integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w== dependencies: "@babel/runtime" "^7.9.2" +redux@^5.0.0, redux@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz" + integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== + reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: version "1.0.10" resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz" @@ -10696,10 +10705,10 @@ requires-port@^1.0.0: resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== -reselect@^4.1.8: - version "4.1.8" - resolved "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz" - integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== +reselect@^5.1.0: + version "5.2.0" + resolved "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz" + integrity sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw== resize-observer-polyfill@^1.5.1: version "1.5.1" @@ -10879,12 +10888,10 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -scheduler@^0.23.2: - version "0.23.2" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz" - integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== - dependencies: - loose-envify "^1.1.0" +scheduler@^0.27.0: + version "0.27.0" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz" + integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== schema-utils@^2.6.5: version "2.7.1" @@ -11547,6 +11554,11 @@ stylehacks@^5.1.1: browserslist "^4.21.4" postcss-selector-parser "^6.0.4" +styleq@0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/styleq/-/styleq-0.2.1.tgz" + integrity sha512-L0TR0NQb+X4/ktDEKmjWyp27gla+LUYi/by5k5SjKXf6/pvZP7wbwEB5J+tqxdFVPgzbsuz+d4RTScO/QZquBw== + stylis-plugin-rtl@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/stylis-plugin-rtl/-/stylis-plugin-rtl-2.1.1.tgz" @@ -11889,7 +11901,7 @@ tslib@^1.8.1: resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@2: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.8.0: version "2.8.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -12000,10 +12012,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -"typescript@^3.2.1 || ^4", "typescript@^4.7 || 5", "typescript@>= 2.7", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta", typescript@>=4.9.5: - version "4.9.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +"typescript@^3.2.1 || ^4", typescript@^5.6.0, "typescript@>= 2.7", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta", typescript@>=4.9.5: + version "5.9.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== ua-parser-js@^1.0.33: version "1.0.40" @@ -12118,7 +12130,7 @@ use-debounce@^10.0.5: resolved "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.5.tgz" integrity sha512-Q76E3lnIV+4YT9AHcrHEHYmAd9LKwUAbPXDm7FlqVGDHiSOhX3RDjT8dm0AxbJup6WgOb1YEcKyCr11kBJR5KQ== -use-sync-external-store@^1.0.0: +use-sync-external-store@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz" integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==