From 7d672139b581657553ca9e2f4b94a0070d22b3fd Mon Sep 17 00:00:00 2001 From: dharaneesh-r Date: Wed, 29 Jul 2026 18:16:25 +0530 Subject: [PATCH] initialisation on the doormile express console with astryx --- .claude/skills/nearlexpress-docs/SKILL.md | 76 +- API_ENDPOINTS.md | 95 + CLAUDE.md | 73 +- FLOW.md | 26 +- README.md | 2 +- config-overrides.js | 22 + package-lock.json | 979 ++-- package.json | 29 +- public/favicon.png | Bin 4054 -> 70275 bytes public/index.html | 4 +- public/logo192.png | Bin 0 -> 83551 bytes src/assets/images/doormile-logo.png | Bin 0 -> 13651 bytes src/assets/images/doormile-mark.png | Bin 0 -> 83551 bytes src/components/CircularLoader.js | 4 +- src/components/DateFilterDialog.js | 2 +- src/components/nearle_components/CLAUDE.md | 8 +- .../nearle_components/DateRangePicker.js | 90 + .../nearle_components/LoaderWithImage.js | 5 +- .../nearle_components/MobileCard.js | 2 +- src/components/nearle_components/StatCard.js | 2 +- src/firebase_notification/notification.js | 2 +- src/index.js | 19 +- src/layout/MainLayout/AppSideNav.js | 176 + src/layout/MainLayout/AppTopNav.js | 199 + .../Drawer/DrawerContent/NavCard.js | 32 - .../DrawerContent/Navigation/NavCollapse.js | 503 --- .../DrawerContent/Navigation/NavGroup.js | 343 -- .../DrawerContent/Navigation/NavItem.js | 294 -- .../Drawer/DrawerContent/Navigation/index.js | 111 - .../DrawerContent/Navigation/navitemdemo.js | 99 - .../MainLayout/Drawer/DrawerContent/index.js | 37 - .../Drawer/DrawerHeader/DrawerHeaderStyled.js | 22 - .../MainLayout/Drawer/DrawerHeader/index.js | 58 - src/layout/MainLayout/Drawer/HorizontalBar.js | 62 - .../MainLayout/Drawer/MiniDrawerStyled.js | 51 - src/layout/MainLayout/Drawer/index.js | 74 - src/layout/MainLayout/Header/AppBarStyled.js | 35 - .../Header/HeaderContent/MegaMenuSection.js | 298 -- .../Header/HeaderContent/Message.js | 252 -- .../Header/HeaderContent/MobileSection.js | 104 - .../Header/HeaderContent/Notification.js | 303 -- .../HeaderContent/Profile/ProfileTab.js | 61 - .../HeaderContent/Profile/SettingTab.js | 53 - .../Header/HeaderContent/Profile/index.js | 211 - .../MainLayout/Header/HeaderContent/Search.js | 30 - .../MainLayout/Header/HeaderContent/index.js | 292 -- src/layout/MainLayout/Header/index.js | 86 - src/layout/MainLayout/index.js | 102 +- src/menu-items/nearle.js | 98 +- src/pages/api/api.js | 131 +- src/pages/auth/code-verification.js | 29 - .../nearle/clientPricing/clientPricing.js | 602 --- src/pages/nearle/clients/Tenants.js | 2265 ---------- src/pages/nearle/clients/createCustomer.js | 545 --- src/pages/nearle/clients/createclient.js | 35 +- src/pages/nearle/customers/customers.js | 1067 ----- src/pages/nearle/deliveries/deliveries.js | 125 +- src/pages/nearle/dispatch/CLAUDE.md | 4 +- src/pages/nearle/dispatch/Dispatch.css | 336 +- src/pages/nearle/dispatch/Dispatch.js | 8 +- src/pages/nearle/dispatch/Preview.js | 6 +- src/pages/nearle/invoice/invoice.js | 859 ---- src/pages/nearle/invoice/invoicePreview.js | 486 -- src/pages/nearle/login.js | 526 +-- src/pages/nearle/login1.js | 8 +- src/pages/nearle/orders/CLAUDE.md | 4 +- src/pages/nearle/orders/OrdersPreview.js | 31 +- src/pages/nearle/orders/OrdersRedesign.css | 1946 +------- .../nearle/orders/OrdersTableSkeleton.js | 1 + src/pages/nearle/orders/RidersPinPointOSM.js | 79 + src/pages/nearle/orders/createorder1.js | 3967 +++++++++-------- src/pages/nearle/orders/details.js | 2897 ++++++------ src/pages/nearle/orders/map.js | 157 + src/pages/nearle/orders/miltiUploadBackup.js | 1610 +++++++ src/pages/nearle/orders/multiOrderBackup.js | 846 ++++ src/pages/nearle/orders/multipleOrders.js | 1512 ++----- .../nearle/orders/multipleorders_copy.js | 3 +- src/pages/nearle/orders/newcreateOrder.js | 5 +- .../nearle/orders/optimisedOrderPreview.js | 9 +- src/pages/nearle/orders/orders.js | 3166 +++---------- src/pages/nearle/orders/ridersPinPoint.js | 54 + src/pages/nearle/orders/rough.js | 2560 +++++++++++ .../nearle/reports/MapWithRouteGoogle.js | 11 +- src/pages/nearle/reports/RiderLocationMap.js | 76 - src/pages/nearle/reports/RidersRoutes.js | 12 +- src/pages/nearle/reports/mapWithRoute.js | 11 +- src/pages/nearle/reports/ordersDetails.js | 35 +- src/pages/nearle/reports/ordersSummary.js | 8 +- src/pages/nearle/reports/profitability.js | 790 ---- src/pages/nearle/reports/ridersLogs.js | 355 -- src/pages/nearle/reports/ridersSummary.js | 16 +- src/pages/nearle/requests/requests.js | 81 +- src/pages/nearle/riders/RiderSubstitution.js | 4 +- src/pages/nearle/riders/createrider.js | 45 +- src/pages/nearle/riders/editRider.js | 68 +- src/pages/nearle/riders/riders.js | 22 +- src/pages/nearle/viewProfile.js | 6 +- src/routes/LoginRoutes.js | 5 - src/routes/MainRoutes.js | 58 +- .../auth/auth-forms/AuthCodeVerification.js | 66 - src/themes/astryx.js | 17 + src/themes/dt/tokens.js | 56 + src/themes/index.js | 6 +- src/themes/overrides/Autocomplete.js | 4 +- src/themes/theme/default.js | 20 +- src/utils/locales/en.json | 8 +- src/utils/logger.js | 2 +- yarn.lock | 800 ++-- 108 files changed, 12536 insertions(+), 21351 deletions(-) create mode 100644 API_ENDPOINTS.md create mode 100644 public/logo192.png create mode 100644 src/assets/images/doormile-logo.png create mode 100644 src/assets/images/doormile-mark.png create mode 100644 src/components/nearle_components/DateRangePicker.js create mode 100644 src/layout/MainLayout/AppSideNav.js create mode 100644 src/layout/MainLayout/AppTopNav.js delete mode 100644 src/layout/MainLayout/Drawer/DrawerContent/NavCard.js delete mode 100644 src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavCollapse.js delete mode 100644 src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.js delete mode 100644 src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.js delete mode 100644 src/layout/MainLayout/Drawer/DrawerContent/Navigation/index.js delete mode 100644 src/layout/MainLayout/Drawer/DrawerContent/Navigation/navitemdemo.js delete mode 100644 src/layout/MainLayout/Drawer/DrawerContent/index.js delete mode 100644 src/layout/MainLayout/Drawer/DrawerHeader/DrawerHeaderStyled.js delete mode 100644 src/layout/MainLayout/Drawer/DrawerHeader/index.js delete mode 100644 src/layout/MainLayout/Drawer/HorizontalBar.js delete mode 100644 src/layout/MainLayout/Drawer/MiniDrawerStyled.js delete mode 100644 src/layout/MainLayout/Drawer/index.js delete mode 100644 src/layout/MainLayout/Header/AppBarStyled.js delete mode 100644 src/layout/MainLayout/Header/HeaderContent/MegaMenuSection.js delete mode 100644 src/layout/MainLayout/Header/HeaderContent/Message.js delete mode 100644 src/layout/MainLayout/Header/HeaderContent/MobileSection.js delete mode 100644 src/layout/MainLayout/Header/HeaderContent/Notification.js delete mode 100644 src/layout/MainLayout/Header/HeaderContent/Profile/ProfileTab.js delete mode 100644 src/layout/MainLayout/Header/HeaderContent/Profile/SettingTab.js delete mode 100644 src/layout/MainLayout/Header/HeaderContent/Profile/index.js delete mode 100644 src/layout/MainLayout/Header/HeaderContent/Search.js delete mode 100644 src/layout/MainLayout/Header/HeaderContent/index.js delete mode 100644 src/layout/MainLayout/Header/index.js delete mode 100644 src/pages/auth/code-verification.js delete mode 100644 src/pages/nearle/clientPricing/clientPricing.js delete mode 100644 src/pages/nearle/clients/Tenants.js delete mode 100644 src/pages/nearle/clients/createCustomer.js delete mode 100644 src/pages/nearle/customers/customers.js delete mode 100644 src/pages/nearle/invoice/invoice.js delete mode 100644 src/pages/nearle/invoice/invoicePreview.js create mode 100644 src/pages/nearle/orders/RidersPinPointOSM.js create mode 100644 src/pages/nearle/orders/map.js create mode 100644 src/pages/nearle/orders/miltiUploadBackup.js create mode 100644 src/pages/nearle/orders/multiOrderBackup.js create mode 100644 src/pages/nearle/orders/ridersPinPoint.js create mode 100644 src/pages/nearle/orders/rough.js delete mode 100644 src/pages/nearle/reports/RiderLocationMap.js delete mode 100644 src/pages/nearle/reports/profitability.js delete mode 100644 src/pages/nearle/reports/ridersLogs.js delete mode 100644 src/sections/auth/auth-forms/AuthCodeVerification.js create mode 100644 src/themes/astryx.js create mode 100644 src/themes/dt/tokens.js 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 73597f04c094157c52d147c74ff9fdbee2df066b..fc80e35611ad8f6aa1ad13bae0fefd4f7270af15 100644 GIT binary patch literal 70275 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 IM6N<$g6DeM00000 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 TSQj!V?NC`+u!%)&79fKfU(hZVIcgN5T1JWQ}(hN1^ z%zMVq^FDvXbA9H_}KMDL#0{@f1|0MALrUXtDm(WN_N#|ddPZ7;Uvi*ybpy3y2B&4KlQMJmO4b%00 z4EPU$=;_AyFGiYGsI$UFe*; zH`^Y1d8Wn#MBG!tf6>+|*T|Zj5D2hhn5gO0?Ox4S7(&l$v|j+vXiB4E??*o6%dP7d znUIww=@@bLdhla|_s-x7f^`6XcK2;FutHB8TWiE$$D}g}y>X|Y*9`Mk- z8tNhIf(MWUGHeWH<~T2WG#QzvoUhiB-{(pJ6RQ9BMjY$CI;SAQK${A9{Mo-xxd|%Y z6fCe20^g4N7yU)kSnrsjDtg#5GN7OT<`oTKy=Pv0R{|gkP{_X#Gue2**s1~^VCKFA zlsM+;)6OFfXz@{xw6FoPj4dlrfXG|_o7XNEwWs=Tz%0A@sCVj9Q15thrCmiN#Ki7v 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ℑLH^vV6!ltU%*7=%^zH&!eaFX1 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 V!zj{wQHMGNsHWkAiu;al{txy&3PS(@ literal 0 HcmV?d00001 diff --git a/src/components/CircularLoader.js b/src/components/CircularLoader.js index e27d38c..5673005 100644 --- a/src/components/CircularLoader.js +++ b/src/components/CircularLoader.js @@ -1,6 +1,6 @@ import { styled } from '@mui/material/styles'; import CircularProgress from '@mui/material/CircularProgress'; -import nelogo from '../assets/images/logo-sm.png'; +import nelogo from '../assets/images/doormile-mark.png'; // Styled Loader Wrapper const LoaderWrapper = styled('div')(() => ({ @@ -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

    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ℑLH^vV6!ltU%*7=%^zH&!eaFX1 zTGuY5gNDx`((lOzxsa!w=g6$Aa`2^fN2Pj+*X9wTu- zx+2Hrm#nO=vHy@GBhDwWJK;fHL{6PAE0E!>AMRkHb;C;bP-g8yf}+80?!W3AIpG%C zjuz;;Wpn%kMN1Z8V|mxb?T$WPem|Kyvw(HcKOdyd-?tozh{a6gj!#ks#$EEui5qWJ zqF*0&ICAfe1(6p)UE}RbX?H6wRiT=7F^KuDNedJ!j5HykLDvbMiEdF}IXlyTrj~|P zEVA?L7ewSQZgiw-veE;H!`;C;`jG=>VIPY66?w_p)c2w|m@~_tF_@m{JH9C`AldzN zFW%DqH?Zj~I=s)6iW>QtTWiV^9%`HCrzIjbmg5>E=W}z8l7BPS)U?95PVKcAJ(1i0 zi$6GgWn1>tW${YO$t)V4;@X;7H|{4{B);4%w=Y-GSp>)^%*u+MU*o~=b5-RY8hcEd zaQ*_YE}GJ*^aRD5{}!-ZZD1yvb_~yWZf-9XY-ry+dxGJxeo`sOvz zcit_W7@fcpCsDskd%#aD9ybLqGx7BnU=my8E*+X>|KG zwnc^`$LIu!D!;K7I$=~n5o{aN;_|0h?;LXj!eS-3)tY%(6digj5?^qiqV?A!p1cBQ8{T4Xpj73afx+9Wx0N3My4wWe-CCfxF#GFuW3zgpT`+ajW z-gHWiRxS!*H0NX_hSR-n5Jg*D;CbfK65p`H$4qU&Jw~FwTQprFEVSGBsjo^uIu=VO zIuwswrRRSCtldGp72w`tdT~=)V~h`d-j}Cq!ar?Ni`1X5=Dlf;Kp?5@t=U5_Jtrch z;f(gffHppkdM1X`0gRM(=xLF-`F}QHaL`<>wS(K`#+K*<7s(SvZ=ev z^@yP{`f~$G{C_(0ai*Dy=fz|tCQ8P`pT|SO0WHK;G8qjWQFc#Do}$@8{l328Z@i4C zENJ(ERO+b76e;*+x%q}4gh<7T=AkEU_IFJD1hRM-2l-8tuu}B`D@>+$_IyxR6rlLP zggpI^@FOI$C#s<87$3lqY|Jm%lpzrkSbdW-{2BQrOex}M0j@qV!W$59JlN* z1;!d-@5P**7h;1B0%gyn`@7O;=CM5`|8nQ$e+wL3_Th4He9GK8_N=x3>{>%0!7YE# zBz7umVFg38=*`&6#2qB%G)Gf?SOSrk7_EirZvBo+d6iMVm~8HCDOZ00Az3F$IX}B^ znd8q?NJbTAIFg*Xm`&&LIYP1a%H}2LOdbo*}D=OWKL~whOo|2NE5~56;zJ% zCM9ova}YZ~fm?z4?W#fjeB}l5<{Z(^bSOk z?(M<=M{d0Rl34*HPGQ^Q!ugRs!;E6>=XVCbj~2z-TGMbEyv31L{^%eK3lO}V&4`A_CI&I^7jhAbXlfg5P)h*f2#2rH2>C9IhYC~ z%vBYU9x5-DO?ZSp{dK+n@#%TvzyJB$3d&a2i}9h+#?b_eIxpPcB!*@J5TlH2E|o< zl7V8L-O%~Fp_k1JrvmW68zpzkJ9h9@G@lpnO8>|tv(cq9rT9(7gHSydzZ;`TL?KXq$j2se5NeRr@FE{G#Tio{f;!)dY(mr%1R>S~p$*CiHNNENLj zO;NOmJ*XkkA6o!;`?}+=3>eVWe+8h}XZvFui-C}hZ*+Z0JRQ6+IJkdNB3sNO$7tjr<9dd+?(KeV}Wcdju7x7nIc1b~Mn)eT*iU4XH7)ywSaQS>EQxw64 zRy{pMIp;OKUtIaaph;GMDaUX%{79@#npnRK94Z+85*aKEXf5KKM-TzO13%|pTWa;@ znwqs}aI>_NFec1B#CA%e_k`{;6D8cze`;(VB3rY4EszUP(av3DDWPF0!Sn5G}~z#%dw&vVe`>ivxLhAlo`Oi2{{c}n)emeVrqv;nAT)RwhB z>vl52{LVi7L1+v9UOb?mo=k%o!+~N<+K=Y6St`DXK%-S<-qlqD@arPtw64A|=|tx| z6pc^&-gm$?wev%760u-9CZwtTMu(r%Cr&>_jV{HY&)M#V`{v~M_F7-v6jNbDtElLs zmaYAJl_7P>i~Gi%jjy%6ky;PdA)Ag|Jqn)_;(Nm|XG)0N>px?J#-_ly=V-telW7lG~=!Sm>H^YN%tV(Zk_a=NgNQjy4k+uOd zk={#Rtj;!!H`wJ0l(v+hPAv3fg;dO+-Tv~giG{Uy5V5%V>(M&7sz55(m0FSC{>~ts zInR2NDtdIi!o8Wit*eeqz7y9qVDdX{lbm2jbM=J>>ItJ$p*CB%@9Mu_j}H}{%?)$+ zS}}P$=6#;VhPT$-QhtdzN_DMSqi0lc+x;vKvoB_@_Y+v(je5{)HxKy%XldvaCkoxV3x-hp&-H|Kn2 z505Rc;(*P09%(mjR3LE?>7r=WjHxV#z>nQSFL?rHX(yYUj&VKYS`67%lAhZOzxTKY zk(d2Kk>>q=cg2Y3$J$zLHuJv+i3GPyP%PJ*lxV<2N#Kj>V=F~EEZRs8?V4@e4$EsO zBB6R<6|JVv^|g&eg~x9gI>=Z@w&LkUFK-0Dm? zujeWR9byDv_E(5?l=x0!6ywA$r;k zdEbSc3e0!5s}FlHt%~q!OIi<}!`oJW+=|ichB#=|G`$P~uxrJxH|nN;&{$;~7KG3i zlF;NW>*Hgz8lKFI3&{IYMMaQmiqyQN#_8quaquRcer5YSKBvR#G!d$4nRbaF8LQC^ zKb9WagL|HwSZD>!<~AnzQ^A&NsZ|}d49cpHxwipR32}Ons2~j)T-+o-;izD5_$FPw zj&LlBcC(J6D<>;*7Bn}MI#7mK*RTJq)&$^<8nkg%{ymphpNAS3#h71M#w++S*fgwk zHG7WR1b(;kGRHwX>a=T12n~HipL_Y|U9t|gOFS_z+fn%-Hn`$oR^6%-o%YbMmIRl3 z+wC4?SDwJV(#clGME`ag=a)zv%E_LH2yCU3r!zLO`7HYO>obbBpD#HOo*vnP%vjIP zdA(%wyh+0y9^Yafrrj^DotC;^Wk`5GIkD|O$6I5irB2ZcS7Ny>y~5rn&c4pDKeO87 zPt*&_u}Q0TUh|jkr;sMC#$ndXDMp7B)ExW(T)5@8oiDCxw}ptl4CD5-UaUaRV?ewp+77mk_JoLj+aStWPt7%jJ8D=4C%VeyLzxef|F`VktI**)069Tf?j9Lq zT1wT}hX=mH0rQu2v4=6eC3IJ+Ke%+8;*JATknEUp5u*3S2z5=0YN)-cR)6w6A}z;; z8DIT0Tq7qqTD5>vF$$4mc>tt1yms=XVA(mFI+B87U@A|jl{LPl@ z208wyxXouWpfj!Qkb`XeLj@mkbJ?gmoRmKJ-jnYvVB#%vS{T|n-J7^9Pga0zId-q< z{%l{J|0DhL!;w967+NWraOf(R&tR3^m`sT3fpF}f!Egy*a8nfVCX<}Vh;LBAuQ`FV zW!xIb$G{}250U3~s(S`#Tslyo%iiig?T>3HOvzO6SkFJF0HT!QsZVN6#HcMRp7Tzl<$Aj5x6%d(P7j zr=9Ct?$E3tf>)nj5m5admYaxZ&F;M?6VI7gb6CgE(qL)cDxzdpPHg7E7lj!%r^1m* z20omD1h@piZ#F4czLW(RvT?t);XzveMAM7uec?)JEeR95N$5>ZZokX}T4ndo=VrL0 zeff7r2VRR;o$6ckL0vI%8$>_)f&^7=-^Xp4(I#u_(GfA4QS!M5ig+Km?otvkhdWS{ z)(q9<1Tuaq){ly2Ld~cBFQ~bk&i%K*MEuOwc9;+pO zp~|V*_6D)icYJbD>LlsHB7W?EJVY>+QJlg*vi4tnt- zn;CD)B^6u;L6(1r#`Yy#9gp4EMn}DBU)TbMq)aFAdM#$QAjjH!{7RIf{co?A%sR9C zFSN{o6r3sr9*1auLr-Z=W-r_5? zfhNV)gecO16U%gR`@g!P-`_+2UJX)z1g50US7e zG~fHqgOZM<#Ma1PyGP{j2(^ zHp(IWm@YYVPJ7-pMP)_!J$kw;Oru!D$B{$YfrFX_vue$936ElJ!*y@r3)`uYdkYp5Qp28Y7!Ckpb1o-If|tU3b1Q;&l63 z9E_qIU`ys}z)}R!C@Jw=RvPLX1sOG&x!G?v3 zdC8tDwKm%g`GP?b?y{(ku+DvW6BO%4q&Oda17dI;7db9uA&JTWU8)Aw4 zny1gdpYaFgRAtW=2~mq%RKhb}rCuc|kH~EAav(4l0~M$ndh-GdDthYprii|ZSWRe2 z%k!P{ey*!E1z6AhB8EbduH25tm#v4nih`DUBJ1`x2gzOE3$~BS-djZ`>Sg%nSUx8n zUOBb%lEuB+Gx4KU=Fk@Q*pGKYUx`m-Iz8SGf&j;t&BloTl#Qd*R(PXI*l<`h8%1hA zbKF!;!GG%B-VRpvD{3uw&$&J~-L|7g+BVEYNnB)8F5PW9A~-Od&VptuVsLK) z|23m|8J_M5PUpy+L@eSEAD9R^sdv!OHM#O$_^;nrLk(pIOA$jj2V*yeT(*>B_FW;Z z0|io89<)!ABH{j8exLHcJea&{#Rn!Zf^Qe|BxyKHiB$ z_Z%Rv$v~eN3w3+1UM=j;fKt1~*tk&XT^G%=mft0opHDCRQl@rYdGpErA7XZPy(yH? z)8ke;FfaYw@&L#*lq}|xP_DOdcm}ZgmkNrEIHNDUX|BU|oa!ah zJ2*f;d7N(3i;G|wKV;;Hw&LZ8R_UKRi*U9472hZuT7*GtLkOIEN#c2wxX>vVo!FQ# z)&Df>vUn=h`0wOP6Ppc420V-TzIk{p`dRJ&UxEn9n&saUT*@MPX+#+gW^n88c#C-R zR|Vq&UYBx*>JelO@>0@6z)0CA`6~#m1vV|&uy|JPZKlF+jU;E+Yxq|Q-aZyJ@Qf<= zpSdSq_+QfcYi5L4q@6)sD-)j@SJOb9$L`wjp59=jcz6zwkeQjKafY`nwPr=Q8I@Fw z&Icvr(31hLRJq`%PWy4<;IRaP_6WYsEFqu?bcIMrE6%cv*&dwa(c25qag2)j`JJ|1 zI|YvSAK^}TU`l>?xzlO?8BN>112VOSNK7bH7TsB zsx@_&r%~>o>U9)XiY~eJeG=)tA8r!A(mul>j*86omS^n=62jT?1Mga9JQBqU??Z@h zL4?cxB7JIWK-vF}bW-hVb3#$R(Q~?51JjFOSk9givxa zNz*F^Tqvkz@f=-;*&Rs5khLRWe9vsP&ABPIZ>HwLD9QgQ&0#6pc8@Q=P(-sZaa=Uz~yAj4k!EW4d(2tm&7-A_nR80r$JEaI(0@Rp=gSF*ME7B45Y zWbcG+JwiQ+4*NNWr-FBm&8M1O!OX>8=AWSaD?-ASVj^pl@k#`BOF)?p?u%TYk_@i- z>07dTT?2nCe{Mn(*nHFN8LKoKx#%0+353ssasX(?lmk_{k7L5xIZI^g1`%G=Y^BDZc>JVQQKMXzv*%hF3EfY>GB_d3qFv($zWEYbCagV#^Gs01(#iQ@QWZ z3-nUkJ$wEq+sDs*RFKJFhy~C7d}J%a0)Y zLU_>s)&?8`wMv)YW9mqxW2~+(vi5X1tmLMUa2^RKP^_ujQGWm6an<&{CPHzk(pJFc zjTYktnN7p;J3}6k{oftO;JsLU*I#`oXF0?qMXvqsg6j@!ve5%_^yjQrPQyJ>1lKj3DnJdOm>)ddW@w|`P zWi^Ww+FY4r)AeMP-x`Av{<9|R*Ke7D_0yUUEF%`c_6+yV&I;W|5%OE8v1x`ZZ!O=J`}k!bql;9&>-KE{=@P;h{*vZpbG3V*jw0_T z#8<6{xw1>Sjs+3Dccv;xu|~WMvASTK?R%5RG6xTX1t#_laI}s4-hpi-JR>SuEXlOA zeyS#-S5hCq&vmG0!L~ju7Royee^rK?aWDwzOsly%kNd%^4rXl3bhp-gq9izt>z0r- zOb-!o>0%)5`-+gbAJKB!O0gacux4)mPP}-{rA?ks^Wh9;L0G5?n#nWwnhbc$idh@o z`9tOxE@-}r&t1kNr*>%N?S;o)w<1+I1(?kerAc${t~cXoUHOS-yE%PSLCdN^aYI>K z9!Z5fYc)`=P1{WQe6U113vt_-dfv_k5ZU`3qIW9S;B_Bh7B(+2K>2 zkiXrU4=D!IB71ETOZI*XZ`gQ@O9~}HAJNej)_YPuR&%`@Dtf=`<2*-ZgtN@x=?lan zZeLFjVUz>Y^mHm{(+=tfapvI;57ar;H7GX<+*}+A$tE3I2|}k9_UDzD4-~+w;~JFL zo@epakW2KmIpk$mBy+6!^#kByiJ3+rD@nG7lt8jAOC&>feUiqn}KkV5*uVtyH_I2hLugCnf;>D+GIrSC``3DTYZ1Ol!OiDdC4?AkU!Cnachh#dv3L!`x_4?YNAlz#=BnvM4Bb) zHoVCvF*N}?9Hk7=q^70H8|1$N{w66stJ8^oiWXve9s@cK;i*}RZl>l*$qjq!nOIu; zUi^hw&Jp2{pdSQJxzNZPI5A#SzSa5 z-A7{v-e(FgmRUAR1Ok!cZ*JQY8}jXgr)j?PHf4t1c_iyP1N5fRO7D5ebXK3=mro)L zHPOPyz;EDG7BOFzbC}3#lSAHb(ZdZAnxV<$?oGqqMA_ggQ~-mW$3)BYm^t9OI8ZRnSve=U17++L+5n2lbA$cZ03L5$ChWZ|V)FP3)D*&kfU zepNkivYnXcxt+T@D_}aju7yT+EY?>ET1N}0+B+)hR!(6{g_lpKmt5&N@Wc;o!TBN1KNe2;Crzad8f+d?r!YMcU*-7zq- z+48v%O*-9FEmM%>m<~XKl|Y#X#=bfGyR{0MdBsSb#k#0Z(hNx7P0LVKgsIhkAl>#q zqV69d0y8tGlP<)3gL8+`>nz!Fc0S2FU=$DJVaDl&H4mK z8*+)c{s@tGOmy=~G1m4MsmVLS5}<$`Y_9-%7b76}k4B%kv%;3=m_-esHR@E5p1Cne z*A4o@!HYTPA;DS=0zJrsKK^ILR#GR?6j9tw#6P_EyW$dyYNarpzle4jr8>jtoNMf0 z%O6{k2;@cX`Ka(IihNKkQ}gP6CFIZ$V^QY&yh<=-2I3G&tt`mG0pcsFoL^1G*2UYo zh;ZT(YmPAUN(8?V=iFVgKn1&|03m9VV#NNkWhLB@!*V}p{KjUz+Yfk9QG9LEEA}l< z?WR(ZldsRdue)ZQ3PU}<8(J`yE|q$Xali!P-!FChff(UnmqFIU5~U;)3xpI2JEp{+ zc`gKtPS#;Y(EvGtqqu5x3F|*W>-``}x$&DywJOLo230H=ZZS~;@y#az?_#MHIm_!D z5cVs>H;nTUCy*V~?ju688=osZPIEM($B9e`sXSh=zqtX%r1Rvg$5rP3tBD~mZ(EQl zI>NldOI<+adOx^*!9{IV&DPs~*JsTav%h}nE*mySc zj*Z#Rl&-=Hui;9Eiwo-*pKPh=CE;(MgU{7nZkAbon+dAk>$meYhsS<{SdPrerjRpa zQF{abN@fo%(U4P`4A|l)hnYh=id}{IAB&a2ZHy(~hLXg#v%P1`6x>9wP^E z0a$R|F42Y@G&KES#jF|X<9(8ht$!k4JVcyG*3yc+e|preLPo>=@xF?-#!J?OGo&dR{;N5#!LRe$*(M z@=StF^598&EU_qx~ad%-I*VPW?v6}|u)YIa0ru;AnDpw3;ik%lvh?{7%(U9N1$m`pDi zAW0QbyX+s#B#3EMo?*+`hufB^UG}|x!m=fS$pjnjkGyXlK&#-Y7h#t6wl?7%q;oZ!GO-6mvU2GEaw3aw2v`rB|N3uU~k0lV0y>@Lg|~XS4RXI^1BBfB6op3=?jy@ znN+tbiq5YIsvck-ebG=rZ5)dqSg4*_wJ65=rX&St8T>a0l7+p{o6r}6nDqC^l!4-@ z*|s*L*7Yj>tLV=6iR*mQDVGEit>J=Uljxs9u3NeW6eG+6@Ct?cIu8EtAOsQ`&-jy1 znWJYUU(_z}rx{EN%hQR&K*}a`*DNm8?^hnJyneQ`$?HU zlw6MPbPf?gDp;GuXHhs4kKM{A?ql`o?YWc&+${YYv0f$QMv-nI4|W&KL@mdYjrh>zwZ z6z5nhPF?{q9x{AEPkVSQSqg`*d|}085-v>#w$dXRl_}T6a#mBj-|)pUmxY-0(c*>x z6%W}988hErMGy0qGbXYq*1ICgbu^&&=)4KoMd#kBb6|GP$#E3l`nq|CL`yk$Rgv!D z_=!#q%$Hj}st_bfy-0MHTki=21=O<35)*dUD$-mw7AQc2yB` ztE24k@B9>uhjlOGF)FA{8*+ov%@QnlM6@Zw>pp2lg2^3sY2nh=?%S$gc+txt@#UEG zL4~YddzLW1O~>2hQ9#t4)pdbKrT>XM9-4FZ>C@19!R5sRCgo!?%5}HeI!Vd;=eZAQ zc=Z>Pe`7IS3%eI{L^y|&C7KTcvYovA<)6OndDICw-GGhpDu4mJM`nq>9A?CFn6!~b zfKl*5Tl87~x_9e)0Ukyj(iR*M3@Gp8bl66m{*=p5oJf^saeG7FG0-_8UcAs0x~Fax z-^ESgAa&We`QGc1smhe$r+s=Ru0$=n+{PY=|IyX00I8qcQcn_9fHKxDbm1MU!)wBp zPCRZlopyPbVf8PNQFU<-{l+lbzUlnM6)70b3O$<|#pH$c2IVF2jQ-^Dvt;~;2NrW= zK`RP9^(L|6$K1(s-g-lW0#(U%3X>~M#HXr?;nLu?- z1VNt+lz;qt9S>;6A|@pp%%xK&jjC_;;wn3mm%*CG-~iK;;#S6JJ;3t<)+xgz2ZVkl zpT!5s?;cXXHbP&#-UHo6MD<8co>Q4D(o6PtTy3_H4JQbO3H?duPL`RUZc8Yy+BDiDNPVkbYI$Krq5zV7cPDo``_?n{ z>Q+A#u&p*OPtLI-Faa9{=xHO5%0G}ytARwzjoby9oy?ZICGU^X#ktdXeybndX_4X@_<;-A&1Q#GA>b4 z0Lp5TE{c*4YWWw!h1t`JePI!m2NQP%LnKM6qYID(Ay4LL4wG8;CFDIrmPA^pZehkeb z27xR=*wLmn*1$$#Px1peJvD>p3kPl12fp>?OOMX+HUQP;-Y2K`2f4pZY-sLPpKK?Y zKEx<^F6>^((BX^;%JRbbv<%{)>(#yZ==;z^TSFFRqHO6= zHlm;uO{>7>i4H*ze2A47dDNi7tti3_Ba7L);s| zI0?1=`KNm4lpojTHqV8+eDstA#OSoN$|N8Y<_{ZKi6D*j!%w;O2Loh$FA=vljfl<& zZBdzQS{n*dD%uRB6;=;d4S?z6S4qVY5it=jqS~ZLQK{FY+8K81xYl?t)kvbBKeODM zds#CuL-Xu)N=4nyi-e&_Edz1a9El8P`li5h%294aWKy$@k}GX?#^u7Zy+Yh^ zq5=h5->8Z3Jz*%z?Filngl4KrBefvOc)cDeMV<`RqdCzd5N@n}&$?l|_>3#NKEKAe z6dzJZKJPzpAgZyO%Wew67bM>#;_D&fsB0jVK63I$`3X(l+Np(+wmHK6lO)7d5HdtINtrP zjQE-d>g)_r>El*PWw?_uT(si)j615A!GE`x%p8>|2Bd!*cw7c9RNOCoq^7MUB3$av z1nVU@(wUn%f7Ol)J-ni0nXT*GV!kHflLu6QjnT@Ku_$a6DU6xkya>8JnbOhv1}qO zhYOECad~?xElmc#blPP|W-x*&>sH^)c-YkB=>S=%Qb&n#u88=L-h0AQG^o(9z>yWD zhj02t@O;U>K$u#yqHlF8gq?;~)$uN0-5n^edVnn@>u$YAi-b@Ta3jx}pP8Lemg^PE z_UYgk0wZS8Nu2(MQqm3LaZo**8OPJiD59thhTNA{Y^ zjNVF7I&LfnU4_nzNUa&4Q`j=iWY53tAiZF@`1K!OF}KZy>^W4j1u4Lgv+iV33Q(aF zQ0hlK>FF?sca=IBvJ!2< z^;m?dX2gHJXxb$_gXpP+<%O$Z(H+LU1%Jkh2@awCeP1H%o^S_m@WrEJWgp6Aw|pN$ zbJPnk+3f$^VgysyVsO#JQvhDf`C2oU4`SPpZ>T~>{rPX$TcxeqO&KPAg)&+$?sSm% zypYn&I*y7e-JaDv^fL+RnB!^2-yASdz=4#IyF{c{TT;+*!}4`)-2~AK+{3!;jM?5A z2TxsM3d?TU2hQ5#%kh#D@`bv(35rAM&n@1<^*0Hd88*cJYa-cXclI8mG`*@tjS=Jg z3R^WEe06jL3oJXoG+UU3yPq67{f0|{DKznLLEYtdNr7mIUcuf6`@)l^iwvwV6IG9u z)Y-44E8-9$m$lFF1qm;Ag>zGGuQ}&|*}kGk)e1I2=j^k{$W(ZSdVH6h83uePuEKXU zrCWpsY~QdOlhnqv*+HMa1RfZ{T+t-YGMEtrcGx1N?=m=&4?xu1(H5FzN&NSi>~D1| zttX&;h&v9WD~#wEH(RWI`OnoE)_sM-)Iv4>>&TaI zpT9<(CLsOanF~Axl?EO*`4yset%{3b&k8}t?MP6P*hHhc==))vxb^4P?3fUWf#-X+ zLYcRiNW&fZin8z>C+r)4AkLd9qkbF}F~7OQuWb}j30N3Cn1$bTfgG7f2-jP# zjsLLcqUbo@R;asv$boHiH70iV0mEIfmA|eaODiv8?p_jFek@|uqzM4|2UC%dYPAA~ z`%jnO^{onQG%OJfwMjMo$dczA;twpDVZLu2Gr0Vs_D*Rop+?@ZZ!P?pcbb=&l7zsM z%f9T5gm!2hes};tolq~C;{q_l$DVU`Y!VwB%!M2BG#ZMi(}_WOGMm~saA&a^9zY2l zMh8v-r-G8}FGKTzES?I8YfsQj#de+yh0;D!<`$T>+A&v*>T`Ca%u?&mzAzK9>EdSird^0wXUmc?tHyBpC8o ze-@3r@px1+`95WpqC{9>LCYbi&pO^}+pt3^$OmETK3dBO6WG|;G~_ATo3!u6r6+WV z{eC~so@I7L-r1Fr%dPmJ$BYU#VEX@N6iE+NO6!=HCw^z;MrtI~<*_+z<3%3*Dw=gT zZ^+z8&lPShb1jy#`=xStq2Q$+053OoM-lxCqc1!qxm6eY`$9X}D!^yeu6p_R^&j-# z>c*jV?sIGMaT3bsl}u9hVoyNH)w^}RfmJ+Ul-A|ge%tgM`gY*k_NNHjlgryiJp4hb znM9ul?@nvFbd(+zn_D?isNhBK9hTYUo{N229rb?4Pl|M*$I{(vR(3K+hyi{xGQ+r= zVYyGI_woa^U#ZnT#I`(?T9|Tqx>rpBBd-U$iS{VLwjF|;(PnoUPJwtlknmrzm*HFy z>}&e5w(bVbC+>Gebj~%O?pUuh1&7|1R=@AdA55gcG_R(pDGH=r!Fv=RJsudtbP~w# z^iUb_4PQjwOPO8C0wn2hO1DXv1<%xJbX2~l@vmzX$XY(aqNktJu|u0C-yty1#)6cj z>Jge95uaV{7y@R)#oz{4UV&`g$9v-RFe+3}2!$vdg1UkZFE!cp(NmNm1^MB>80^yj zDGE3v}TJKaNt70 zC>(&He{cHh3jX-m46A1vE+GyCqW@BpvvEb0m)zNXEE%(8(_UbqmL{f+B(!)fGvrxx z&?CcXLp^>`dph>4h4Y-1GR)!bZ4aRN!)G#ZpqislwE&roaRe(Gs|Gq@>7)m=U}&7f zAmP-4S6x46XZ@mLby{GZEokf?>`A7TW zWjsOpB>Nx<>7iANn*>9SM#JGpq&nqdbJw4Qcg$)9Zqv6(x3sy)gvqqu!c#1cI2%(( zW9a0YaR4Ey%zUy5fA)0f;ZHjNm*5ntmOZrYNh*GjTnE~57wVDJ?)!`Xz&fICb-`6D@f0)x@;Za?jzI0Et(39?!nFF5bU->5^(-=FbLDx^` z_j&?SrQlZ5Jn99~{FhTl>F^`bf~7ABLB$d^>_K0?iJ3nrAv2Zp!vIdaPfmRm(7_Wt zRh8YKpUGzp&XKujUhYe2RLG^2J%P0Uhk{6$^VacgaU8x?jUGKaHAwsDPJ^<0y*jJJ z-hgS)-ruf)rgO#R0QXIKwrH#y`3nNO<#D5jY%2Z}R>bH7%N_1Use<)!pyg{;P?Yx4 zkPGhvv z-Aux@RT8ln;1s0^`wsZDQylPb-69&aut@#}jD(bYDu|drJ$X-+E(5f8fbx4I&vUd5 z7{KN^`p;L4xZ!&Ne>1LY`*o#VMr~I$YTs{C%lU6Se${gJ7Vd&w0IBV6SgkNovG?)0 zMY=CCmzg=8j1Xw?(=dXB#$@Rk-8Df*TQq1H;ZI!9N_sGS5nJXZjgz(sJUx*4iPvW&jv;~;8hMIA0~ct>xtAv z@&Ng1-M2+}HUE6?8wC{jQJT2?68pNU3iBSm>!geSQs%B z0og3`y11XU2JOMnkitrU4W(+mQrB!t$QzZQocPhFb1DOG-SPXkwvo-pcksU>nxUGN-wo35&{@L`gU`2`A{4)XmA6oeK$OapoNqIpYm8BlXVVwv?kq z*I+}>cAKF45W2^ZY#)u!;zZSl&YQvRG;t!+D)El`&Qc+2!*%}lJ7sSe8c0!9yKQ+r z;RV?k&!M{Q+UG@MJ;-0t>1>U2&)f%`$Xw+l|1>~9xy&bwz5Mv)Mvgi9;ZHu36mQ{! zr+j#mdjIOql!<68W*!((8o4aRO zX#K8Cbjn5jq?#f+cE|bh0-Nw=H^$d)xwkx~n98nS=^1lJp!>&r6Y8_g5Sv?W-o$RR zIXelJ5|&q0ImkJhb>6|dpN2PioYm3rFnr@DpD~?<1CjUVazU`A5xn<8-h1>uiboI>wu9BIJdnb(H+P{Mi1wv=bGF2{2UH5BAnz}U7}$Ih3( zI~TXG<_Meh2+#CwdyRIz=}b!uVc0??&p-TX0;G%7hu?UIvLvoUHWz5AfioYVf#udb z_VcwBWdePdXp#!;J7DgX6rCFTdEyi@H)soG?yI9#gzCQ04g#YhSO-1#PEo=7r!Kz! z7a(T5pL*JVM`yk@b#iaEKiWyd1J}BruiQ8|Bj7O=~|OPq~6*%j`Vfm>#F{GW~FaxOwB| z@Z=T7h>kk6@W}mGgL2aQN)Xhk%ON=R99znB&=GU&WhBaCf*kl=tjjcmv@JejkX-P- zULJ5d?5cU%i&jBE8rsf69DdV2!G2SNO%Aqu_4Q?<-r5%tG!152Pbz}q{f}*#cRgBM zWb$%7L)c-)n0Kzvw$o`J$S7(`-MZee{_vP-^2SVn9n6FiG-2tZLSQ4tka)R9i{!&c zVr5w7kB=n0_y{^z8PQWFo{WP;xC*McFq>Hjoyw!%7l?>37GRj0Y)3l%w;xtQb{eF_ zOFog|A|}KEbNEC2?WVOs2spHT{d^im4Zsd1h9b}>GOuU)d;9fVk~7%gdgcweLy^4C zR!Ym^4mM?~_dVg|njq&tg69(dB-}x_EtaxsThLywJnDcPla=Vs|6%DY!=n1WHhd^S zML|JIQjiWoN?H)4ySp2al4d{2457MOql?5=OeaYv$ea`@dhkxW+Sc_FjAK zwVri9prq4gg5(sVt(ABliTOAbRMgx>jy0=P0phpXV9&AmDV@D}N^q`Dyxubbv3H+4 zvuqusZ0r82Cyge>;MS-U9%G=hO8%&CN98o8fCBhyB27}%a*v2wdCz>U0Y&unY2Tc29c!UXRomX^k+aRioM+1SD>(Wm zk5Px70C767Aut790K(35&rz z=gxXS*B3N^e1Aw~_#O{9#m5#LjS(IAi|?%dO^Br82j$Yg2Ok6_zpIGE&{-YO;|h$`aoL=D&sEt-nsML5mn@#1~5E(&}eG>Fi zqVe~XcmQ1l=-AtCT+J6Qi;+8@o#F7eq7u$U=cb;yet#2Me=DDFE>=7pw4Zb>^jSw= z|8lhK_=%>g$soiM_pF~y#^QIr$j-tQX9hwnZi`+jlW_XwBHYYZE9<$vuSm9<1&T)G zUKX{{poGYonC*S_ z8~3yD?!lN7x=}zv9tQCehLEpxDj`e9R^}6KyL94jJ6rPV`g#?l0ZIqqe;09n!}?wv z9O5w5j{GvK=5K3dL4#GEjh7_6q~7wieEcA{G^KX)M`8XPC7m_?+3qb#MXEv{zO1!4 zz3N}nkwf6nvt|ap(oP)&p)=L;qVW$vfiB+PDa^u_R|D(#a<>&I<*hJZvXYOjrf_|E zre?dTiYwNq?L9#Tq-GswEH%x!rqH9p~K~MID~?n9!YG_Ovx~+Ww|nx4E?psUks|o!ojc9_5=u z`8O2TVfaF1ynL4Ye8;MW>Yrj<-p@4>uDoxn%#e&TP${f&YIYCu<%Y?aZK5mgahU>4 z%5!4B?-v$N6z%LPs5=xErQQ!uc^v}krC`c-SFS;;)~pv~-Z9a87i2;yk+_Bx2rey= zb2yjL?-o)Krk#oKjl{A1DbTPlo^QHc`6(7lCuZ2})zd$eCk>P6X%cJV{~`>BsL#EZ zmUH*m41y1_ZlT<9Km-4PdcUHu4j@1ie@<+I9n5>(zz+dRt3}C)pw-yP%RwwFj{ebQ z9gfI$(To!mk$8PoK}L{IWyw{y^t9 z*~Jr|-FBb%ahZtd?Y*ocvp`Q6^8Vg8sl-$f_V>L*@jyYlQofm7@;5WMBb5&o&qq(B z4X21CbR1Q2R`f*T2N=XHlvdh+^JmdQ=}+||P!3cqSKbJ*07r~d&zo-NwtJPTwweFH zP|7oqx##WtT3WQwczcuT6a3bc#w)PfVFf872snhylht%(G zyQX7AkT`&i6zJzyxxN7Pzp?Xw3)}DMGWcBN?;D+ z$1u8>XJ<0Bl6BuS=8yGjZtYXGT}N4gVTF&y6Z#I11ziN>S;{`{&qM$R5OxT(A<3F| zl-)T;^@C@aK{CQj)qcxVUMS;#n3Xme{g40Y%v%KjB1Abjm|txJaYojB@eYkh-@ci4 zUMfeXZMGEwH5X2$7X|=lR+}*!pz6OejP_A>CzT(aV@0$X&MdZhMc*WidVgxQbRd@d(qfl0NG+1lJq~+0Ue52B4S3P+ODRB}Fry&)P9ZGON zZYdqZXdPBFr7_;@CbTX-3N&`DK!HtVlkD5s_5+PVpj|ojL)n0y&k)JcN#GZ~%sNjG zbU)3S(i;=cB?RZC1RQr>GM&b>_CASTEx(+{Tu&Aa@(FrrFSC3-xC3aabiy!B{wQXI zJDX5~M~&&2nVPDI4-PAlV#SJ>;X*H7fwm{N|49`nPx60lu<)Ewl8fbLhIdBkOdWqw zaXUf@$Ik4E0ZJQJwB6T%PDY2DHIcIo^f{B;+sCo+Yit8`viDJ@Cxc_v;ZRRVN@4ZJ znK5Co03CzCLx!hzG|gYcKZnys;rHv>)I#o@1E4p7_n11Mns{#UK?4)nN_P9uzna^J2DmqV2c81F%@=tjuphgAh0tqcFDCrS!e2)$;py_pbj; z^Gdp`6CeCGebXly(bN6m03?4-gF#uKiIlupU0QhD6WW=pae0*S6)kGI91=Y^EB7*% zp#19?a$qXQ5{kSec;nNpFm;|4`yiC@oG|*`fVrjqB|to4aoI1uZ4xYq$3LHH zIoa42TRi?_xb1`59k{Nw_=1{OOoLjuHW1NpYX}M`HE#FCet~HV2b9OX)41P@DU@#J zc#|1r>`C8$l)=HsaQ{R1m_5+BsSDb(K)I-v>J#g_q@OYmrg4t;XCo4N?w{FUf~njb zkLMh?unx4uBxZ*Td&-_`WTr3C?$J4zF8Y8buD* zJ+gg&r6a?&IiEWGxV$g(BS8oPhY|YPiP1M>mnv-^aEIOH={^|TFIv8S+_V~+{FoN( z88gE*39EvJb!*V+IVLgKP6K@pUm!jxF=*)ON#nk`sff2yOi)No#LO=OlQ5y4exK_B za%z_IoRKx9lZ`lD5;l0%#99t^(C4#j8+D=Gb+1|Rr$%kbmOGY42}l!U79eu1m!*F& zEZ>V$qwx5BXO+`9@ts@9=jyzNQG_gMSF1YQ*JV0if7Theaz%BqEt6j_J&HmEFn!Qs ztBQN+UCt+);Z1A`q+V!r+#j;3*B)D^$)&dF*#8wc4ivT=dV{N=9`*Bl@refqX=j@a zMh9C*Ed?1b8@d&>kF(FOo#^vg-iS7HT|V#!sH~X=s(;=f09bU&JdeE!`WB$T%OekX z48KPjTRFuF>VG>mFgvjKk47P?gMtvq2VB{1{HBFJ!|fSO=TR+U=s24TN*})&?(}@$ ze4TwkSkZg#b@Hb?^eLbX`(+IqOg?`*ZNgF}A}|CM0abBjNadO&C591~M@_JYndPV1H?X5~)-v!A*+*ew>B2g5%yWi65u{y~nvU&ok z_ovG5{CCV(#r8HprBmqz5%VsKq)jbx?8m!HNtMkXZCf4^5T{vWoOLh+f4NpWP{y6m zA0s|F`fcki9}trsp9x*!0_x?z#>TF_Ot#6^%pP7T5$k$q5TnlA>F{u?`avfKs^Mo7U3@E3ffL10(B z$@lQz+tg6^BqFF-`(SoP-)7)-Id}A}rl(+V-vy(iSi~cDAuhRMg^ z4_h8lFLsy;;rhgmFQfiueux8A`(7=SCHiLEd~zRro}7gxaXTP{3JX*33tUj3u+;R4 z#_fq?^#%{mT3*}_she^j2JnH4B+Uj#X`9;J-Q|Z~iJQrMd)^M93}xO8nw+2QG6tM? zvqaOkX6`P(?y1smPR04;nA@c6oeREK?eHwPo=^BeX|(W+p-_M-FH<~Z9k)QsWb6@jXR)6 z<`Q;=3z`hWBTbGz5sSm6P|@Cuf}lu^_|xxJ%#NZAji#Id26p{0A3+`=tAX1YmK^dI z;CFf&b6@Wg=E1V`xZ&4q6xGL68Jq%rCQ9g5KI!(hu(%a!C@q|*+{Q4?p^jt!cqE&j-x4$0~O{-g0tN(C?X&H<)!ryJLE{*U2Cb4--W9?LE+A+;Jw4)Lf6soaK|P?a(_h`44OK6 zg|J2=PBIj&1(_Fb7sjQ zga-Yv~%y@51hK<_v>jDv9)WK^l<^lQ#p0$iZ3wph@@xyq1 zf`SJ%Ym3=`$jIDP06p(_YsB*I{fgIKPSu|4-w&&+PqwX7 zY`J}wi1G>pJ{D?i7yJ;qq2hJSs+j+NgvTYBamDUyHTRv;81s!;|rC3M_f< zOZej{&y5veb|JXGM7!m?%<&5RO;@n8Jb3xxA?dmJbOqUNtql3ZJ6t{_U+Q=H-bmK0 zy#?yDp-O)+7cE<2M^s}b8_;JCQQTmGVydgpt&LA;fi8#uy+hfLY0@`;4C6D`SaUEV zY|bM}X?d_)YkQA}x;3{BDDKAH&#zJonbbD#3PZen)AxS4)BeWIwR@D}ap?BVLS@m( z8IwZe-xg0ci(&(XgDyC_4<^G&#!cThYJeA(aUE4?`lIhjAQ6y0dDe}h1`y9($HfzZ z-@Ft16_o0e33Td#K#foB&S+cgct4+*5&u9~2)w$Up5-{t{rHn*`bk6xAd9HiWqRSW zrq~r9e{bRB zG&E-S7nH9%7Mx(d=>Av#=*$^|xp0F{O&?%zd)9yP1QNy2t#3wnVtgWBqlb^a>a_C7 zKGIBXTZ+DlME2PXU&h0|uGZR>d9_`9PU6PX%G9Go_xdW{+%S;oj z8zvJ}EQj{*6P6(f_OGLnb!My}=w539I&aNl=bpq`ZD9OQ*z=d_kQrPa(ksocr?CQO%so1g^`!m?A%3! zmj)Hi(H;cih-N0#F4pe|tIf>u<%Rv$Ph2B9K4m~OOl=9ri^LXb+pJgW1gT?-&Jibb z*|oZLd&ocipsP@Z{2SKGW%3Z$*yuhKRoD5FX32M%BD9T3JfmS~ z2sE<6!+7pdOHz^#on$1cmwC^zfjKJXx4z_o{Z?e(7g=wi_DK^brkLfM^%lj7@;Y^O ztD}Brc!{rW#>}CC7y9Is6J#xZ$=lancO{Br0t6R-<^hv421*JHhYi1FDYaA*Hp&g2 z{KaXM{D-H`VzFSfp4f7rog54j$~ype11hYP>jx_r%D8h*{5g5NrN#$O64O(4l=7}zMB?WC4(zG03+;~Nd|CwWl9EMZzql4%GWfIpV=vldy#J`$6`c+TQh58iG?^xQ} zld$3`KdFAIpbpd#jD6525`UO==Lf@b`UT(Z9``%{pn!lH2IHlQjjJierC<4J*4*RATr?l;`9dqd-`6Mw z6s7Mp9?8Ty8E=3IS@MR+k-Ii*73_45EMypCoAK2flRequuV&B6qD3J?fYApSibm-+yvRRdC*Q69 zT#g>z0=0Jp-UGLqyQqHx8Lus)t_-&I?siwL&}Y-L#sZ<-0i@Nq-7&0z+Sj}LG7vGr z@6`Siz@^`_a9mQ)Cvk&^sy29I5( zzpcrQJlTZXeB#D1Z6A(1%^EZcTiz4eUwR7lQOvLijNY;U1Hj=6z+gfqbo;u=9W|@> zDmT~Vr35!$T|{JwPg-vKrJNAl=k0ZC`c`olZ*_N|`u6o@7XbY4U8{3|vTtzUdNdak z;I)H_%P;@WW*`^XO-PXX64r9&gdOMcos5={pz3Qln0a6Uo&N*wFk!Z@>_e1<9^KxDcY_G{_0x2A^ZvilS61h*{G$?`lebQ6$cFZ{C3Pzsl<`R9EP9HP>dcXjF3d+ghxvoEtjU~g>Ug^DV z#AcZGY}L*4TN>OxKSU|LF<|)1M_yzntq+77qWYYmyQsd8YXnBbsqWSTauXOayP^;ZAE}(hK_^bkaLq0K^$rVq3zDOIey#OsK zqn+)GT@`Y0NKBLb;p?*jZyR?OPpyQH2J4P!s{#ZidBCjS^y@{Qih=>DOi;GFEpcW+ zEmz=4QlOv29vGa@rNk0m?syz(geZtEx zgDkS5^c}`I7Tc0@A^D&_MCwO0+M!>SY}@j)fB?p6j9fTBvGk9nt%<5M?mr}o1+Z?A zeK7(E0hr(6jr#xqqpu#Yx{HiY=(=Z~`C3cNn&FXZwL=T>~~? zvW5OC0QSOmu&gID+Jv>A4=KScEyyc5IStGc8rx+L*0rXdy~5qyJ$zE22Uvw_h*|gq z-2dL=54(u;dYKFA;6loQ_vrDaXuu@=CLTffgi2Y=J4wCl`~IkVZsz<$!g!9(JKmu8 z^Q!^whZB{mW>C9#y&`yV51`7{%ol4S z!A1HBCtGKXa$B+8-FoLVT#HkCenCGEpB`lGsqZ_>r1$=wx&8rv_HMw*tLx{oc3?#W z=AdJ);Kfgs{JF%dMN1Rq@VY(?{cy(G?Q@IifXnJ-so;~jwvcjeuPzbcws*icwq2$H z$Nh)py}+QnTYGkjX{tm_J1*tp<$Q%jOLz~i?Qt=YH>Tq$d_FTeFY zv0mH-hg2uw<52diY=NpnjXkE((R*?;jh}x+0W@SVtSMep9QhO&GCgjhVs9kRR>w2Q zzVcdSH~b80c7(WcSwfP3g_(S-v(~Ct>Gv2(Evxi;Y-r1+y~WhLGs@RYBs8fLj4;$w zETP1njyl_C-4AV`x<;j~%D%z$&fn~q5Fq3M3B%STk9*fRM66wz0|rciDN>I@z^j&d zMC9a^&FUa9BI7ij7DfhqWQ*9&qK_E}ZR)=|x84OTk(nYLv&NK9AH>@gi=R4_l5AAb8BS}b-Hsq-9hng2J(UbO9-+`OcJXZKx5VG5f1hCXXdom%k!(G?PEu|DUSWy(4UvvrSJL z>db?Kvxj=BF*_3kGBa+0zo^ShCnFo=(5P^KW(PDj4f&YGS z4f%Ce#kc3;x3wgu!XC2urH9Bh{c=!{Qv3Fz27QAP#0MGpVvtM2 z*}Lv$gd`!~Yd*Wj(&sx*Whz=icTBFW{n+Oi2W72W-j`1r_^P^MV=Ax`5gxmDrLvb_ z8_yE}Gp(gZ?*ew7{Gn(sO}O9^7P!CvLagWoyBuA+ZP|OL5O%*CHo=WX1iI%J3m11- zvLbwb{+AH<)3$oejvJ|Z8}Jt_Uiahw`%6ot;@|OPi^zDxUxKEo5HIMzg}1f7Yrecw z`IWwIXb%rMm~m?_Oa~p>U?B327Va$3z_k0Np0=UdQ4Nybre$zx1_yX?DYFzv$o?MJ5*k{%jI3}zM9`Pz)t~Bah414Z~ z(^&rFy3a6uS&h<3sa`52T}Zlfp!_5zvIfIQX2E~ism}8qy7BkR3=LxC_?HNS(P1?O zD$?6WybolS)}&sFX>7}`63GHgtr!M~@P6z=Eqmr2GXrkiPW|iCcb_X-*B_a(%iDqe zKBEDKGQ86M&cx!18|dg;05M4ZCO6nj z$m4W(u|>Lr6RY2KlUamHti@Prds{ZZWVUC*Ua8usU_8x{{wvp#%k}dt0MrN})abYo zjwRfE+YpJEAD6pgn*UfSnux$osps~(zyzJCk02rw!V9F!1vJ)Kr{5p!>pjk{e5r$* zOc>@&@LDzlr)n=2mfK(@EYG&uehntSJuK2%FI&}_5j|t}0EQ7~(iZa6Ie?GJ_f9Y!zmy%xsfOy6; zB@uEgF58Gj#|=G94RvX!rG3V2Iu-Ckfc$zOiCN_x4q#%xx)->SjIH*5UXL06y6dj* zsFOy7qvd{tceis>CV*4`5T|Za#=MPq(%ZMp6}W#K6XoA0JUBu6Tat2FUii-czAb)p z>vij1d*4uYg66r(9gO}HhTRWJJW`jiBV6?C-+I)QG>VqnAm#&_n$&fm2>^s6RD zTPfl8Oj8>ZVzYnSCkm8KWn9oEo@W3;&hP>^gysWts1h@za?DOo387 zW?J6>#k2XLkK$L11M?R$cYraf=dkRli|1J}t)Xdx`6mFGaJ=}n#y!qja=QeHC1>)p zX5V3r2wQoQEWT7VJos5>Gn-^xvISfy;m4>7tpuMmN1&HZAb%+Gkf0$0_L7Izqg`X{ zI8S1lI2<7kg!(z9R6oPX5x8LKmG`1~IP1=nrserp+ysS34+x%8kb5+e1cKKs<(*cf zp3lu2b;!HJMAZ`!+Y+pSy4FZ;I}v9o(+!RO>cu~KwnFicK=Dv<_WFPet)TCVk6Z11 zH_F`YW8-ox1xFK^!&#QsM|JTEU(9U@udrqRdQLKnUk^c#L%p1yHu$fWZr}7(TImGZ zJ!-Ek5Q!joO7fWD2_IBHP4>PfGbw>-SC~*Av6=F6s z*qr{Y$fU)Pm6DQfW#X{NHfcN}S@zA>fV(4607 zqsdxm%C5#-jbCK)!=^WoXVK6bnEtZ7E z<-zQ{#GP>^CXG@)CHaQZG0R&L!Y@5-G9Gd+a*ze%2gAO3~+!4I^`is_Kci zlo_@znA!tv{W7{c2$`kfMATg+M~Hy~@pD$D>*Xyph_&xBK$!efVoJg)ex77Z%!hPhycA?4+ zJho(rx(`Zw;^NAEtqMH`irnPqG-6bwVWUmxVVch}^u|A*bew_Kx8u!m%1ah^{?4D1 zZSJm4H+AZF>gL;%#mV1J%V24gnw8Xh-D?0Ux{%kkmu@u6%LwD&)d-{raGX~MLva?J zwy?l$P7EwM>Z*@xDe8_ssEa7WMvzzXcRe4pRU#xmUBOcJorCpmw*#j4?SaEPbh|1B zuW`Es+T;IG(JJ40rOC3*OHOZj_j>Vv+(jsZnJQ3 zsw;xtC#VI`Kli-vRG9T@vXe$}EtP|p>y59+Fz9tCRxchCnlytbs8`q;;tzu6= zL``*e)X{J60_b}%otS7yE1R7{*+Z?rYEtSBr+p047?8F0i|uW2>&xM7Hl+?2-+8nJ zK{6C_uU=~E;MzXYIU@mH>nB0}=({PXUh%p!c78?qy#OgEMdl#&U(;UwXN=0l4Yy{) ztGdl|34w+9vtKiK<^INIk!n`h&>Aq3|21HB;QRgdcOkUBA!1aipfKV0brS&JF#^p`>O9tO zhgHe$Iw2!T>H_{haY-q)wRe$%u1?UC|T#N|{{Wnj# z$UrA&Mp@Lwr}lNc|6cAP8QNz~(?L%A0~9$z4QBU7s!RJg^zbA+20*USNgWOKdIV03-);xW-TY5Jul#_1fD1MmmcCmLFyB1o@@Ff)L{P$w za{0U+HiQpsvkt?u^X4+H=t*k{!_YTl#d|hfd567tjg)7yCEdUP@+<}OFGxGDh(Q+< z+mX0WF_Uo6@jIq>x4BXsYK`p?DQALdl;C^DEH&S@v*&L__uA9#H`2zziuyF3X7T2d z$SIS`PGF<0_n%34C%UXv5IRaaA`bJo-Lk9{g-v1P9IyGRoO_(&$KUCU>-0#LGDEQ! z%}?%Y)a*9Zvm|(##Bq> zrai?2pqUjO(O4g{9>1%|b1J6;m)n?tCI2>%?^fayN(!@`BjX?Yk93xj(wI&_$y@$j z=e&i^{*?GNHe$#8GtW7x`5jp<{c!S*Gye&{Ygn;vq>Nd*CFBI|OXbl>gvk()wg;d( zOfXXbJbuL5sU|que4s}h{%1u8Wa4Qy`;~lI^@$47%1!H-lej2wfQ?Y*LUd`tqj4Kn zJ=9W5s76nO%R8-;%bx7|)A=>UN;~X$2|PldxR|e)=F$&dy{tBT^w>CG_uH9#)4x4T zRzoiBiuX9Ggcd*8?nHz~j69Hhi1#$_ljR6wYQPZZ)>+Ss=PcN=slQ)bZNsllB0i?d z=_XiKwzk8Kx0;bBSS19b2c|r~rQ@zAtT~!ih04z_P)TqDCJ2HwKfCc{jUT25R6LzD zWU9<(;iE!@;i!E*r!TZy5=eTx~N3MCQ<`gz};G(^Sbgxcx|{BJTFJCeGNrGp-bf9~kgT+aL1e&|4GcRe2(nZ) zunFs)dmHWgk~Y1-|9ItS;--Ow<8sDx%wx-mC##1Qf;wVS7~=QrE>r@(;Gj-CaHw`% zUDQ|RVEoqQT4JQOh1oC_uoIK0fyKA%aDw7pt}@b*`n_)jg=>-P|PrL<9Xi{EnV;0`hW|DXyO zcE4D_LuieY>YAv;QqS(-Nl)bMaeS|rzk|cKBMeM;j-AF;viV1n$f3Yt9zSPtzc*i2 z1NNV9W{T=`tLXH=BMe{X)uQ14*lsGu-QG*0y{}jI~ z<9w1=!r0mzyXsv^gAGYX_+DC;rL^JlKoTde`JyPf>YT!MuhfaTvrmqkDc^mS5(&oE zqZp9~tGvW0@P#lT)1Hz`Y~R(mFo%_oJw^12#A_u;=A7tng2{-9l5x*#Ik?fgqznc1 zGu2yAkuj$n13kyL?e_Z3$@T=qFehnG67_`kN4GYi&R3=LK4k?o-(cW9KP^E{-ZfRk9zL6F-hLQqk_x{Mk+MQyy*G--r_fzpZ2KVXNg2E1j*2 z0o3b;YHp9&l?>;kGpt9R2(8zDU1~Qsx7bAUIDXutoC?y#fVRD~VDt{Qj+$#pC{6** z#5{LCoJ5%hED=~2-{$v=lzhe@7GEhWsEC>~=BdS)#}QZkJ{uywyi&#JfJ?4M+n7#s z04MwFIWFx-2qI(Cf=SJuea_`QucT46$aw$sZu%1Wi?!q5I40U>MzEv+&-J{|G;|m8q4O6#;BO@=060Y%+!xb6#TxG>@M+qtQETXuVclvZs1~E?Zqq}2K^Zf z7g>pdgMQ2RmC|_fN2F#R{>=;G{^`ICJ)ZX1OO2?!j29xx&8w~$>72Ibj96~uE#5ou zPP>Y8OT>R9-v#T}eh!{QHC}dtnVi*jUT>`!hIBhjPgieBVlRf5(sSRbXrblk5)b5l z1wm2enhbwm`~Ab`5WT?W!zWHWXGv#_iiva{LLewmDJkWK zsY~@}NJym=25Y=-AZcP&p4;S3IT{xsw!g4*9vem-BIOBe6!NzS$Yo!E#g?RYLOMS) z?r_}kvQkGXky+kM94c<1GBt{2%nGPE1&)|CFTDvY`>EH#!VGtF9qM}&nRYdNRKK{^ z*yn5~H&=r)VQZy@ySord`_1MDhrh;}EJnN4y(^ncB z(s~@mqk5Qn%ctS)Z7rxZZ!Z*(nr`=kL!}1p5EPUGH zx@~b~-Wzf8N|x-4_1+h7M_dr-U6eUvto%$OQEOK|yNi{H-&YHdQw2}UDd_D6jhi<@ zd;{`n@mRrT>$*o)1YtrsHR>)Hr|y;Gxwur7rbRjL4OwGx+4*pzEZtu0c%S5_xvv(3 z7xszoF)?buIV1mK&4@vVUA`^5AL_Ws!i4L58gPikb=J8$cS)iiNnY%}tBL1e7Su1_ zb+0PFRY@3eiq#dwg{aMsxwKo#a3o}AA>l*)y*utP%AUG_+eR#Qi~%*Md$$R+RaT^1 z+aa4sSy@P|-qU8DVK}UK=U=(>RJN-e5ST{h?XyrzJpC=W58Vs+r#b$};sv4%Pf=Z_Emw0pL$JnkcA}cbitv8iy-J7UC z&Adw;SgfLy5X%n|C`Tx7QBpDfm1l-~sL)<^xtPjfE&~-7BBWYdjx4zm>$;FJ^P*xT zUe#gxJ_OZwzi48JI}qV$KUhnH&3DO1bZL^JK01#RE0o?xs5oD@r(2EI^2DaItD1q; za3b5)CHMzX85ZLo_TGw3Gn_uPj+)4}+_0Jw)9*%I$%h7P;G9**Z)>OmJ#>)Iw?Z{Z z$lD$4`IWZNF#q+4xZiq2!pF<6?IUXtkGx>z+1(39hL@#&Y1~ zbF1xk3PVkkgdCYY@&&2g#XWH@CxUQ`_YfqTdsqY0D_s$|ZUIXg-^y)c?#P*;DWh_N zSq;(W{AZX(Fc$s>r zj%a0@V!}`!NaON`LVc0{TeL-ag#F7{EM3mXuyka(_h6~}b-DPby+ZpFDgm{e2to)N zXW=8YHgT>*9&9FzP1Vawr{p!VM~=H+i<#5q~wE{;TxB1cyyz#0?{{MK@xm!6}&nIrB5gQOpf5KMU1D{1PF~>Nwrbu z>suhq?j+AMc|ckgm&mK)i*AQ8%E!fGCL0TmDREg)mnz)YJ)gvXXEwhzuRE{7ffpL9 zV~7I*_=za2OW=6B%S20E@hl7V;KZA+M`lpKICqX5FYhb-WIFuokF?-*U#xfEw$7vr-Tz^M1)C+dPJfiWU~zVG=6-mzW-NhYr)--6QbA>kIya z@-&Ow-Po)GBF=VQMx#Mn4L)632k;HtWd}b{gnmJK9j0!6gEid3V$+BMac;+2L`6*{ zma*-MYUT>o&S;>cqwkuw7=R0X0!v&z+8`{Hch4eQW0Ipi9x=k2@+*mY=ABzx;rFRx z-)>?mPqxPCjX5`bpRX0|lLM-aGghiT;`#&zVXhqdMEU3i2 znUtDuWcb3W<0n3?gr3`x?6qi5P4lk*dY#D(Xpt;5$B5?mimxKSS_d9mkeB z@A_Tr)N5QbO_aQd;@dfW%!0(&epY{=dEfGN$(yR@_r+9!?5cE*oMo-h;dZCOTQm0d zVzm!eMm~!-HVzj`wet5c9C9{@A^9w5aUVe%l|39b#`@ZS_4%RaNZ6`6m_hsCB$>tU$+BJaty ziv4BRngS~goi05aHIy?VvpV)`TczAvf-ko+e@dGi$?7VvBGXi2LCJ6MwJFa zZR3H{T}+5p+Dje>e7?Ypl!|xrgx@hQ-#y}difOLB_*aa0mY62sBSN!U_wRJw+^pb^ zALCL&sxB*w;`+_XDzF1}j&H~IePpQ}mC=hP@qQ{wSkmPe>vlrNiH$lwyt7X{;0zKI z)tHcMrKVAfS#=Sj{RCgh&$8l{9~;qlZ)duuP}uiEoh8Hppk%ZX0$d1WL~`3o--DpdO8q{`CL<4+AF0{pc3=*^`tha=VO7e+=l~iqkWlcXO8xqDM^%T86dS z#{Nc1k5C`?is@YS>*4NHu(k4%(#lHkAK+j>pVYy=y3F5wvTM|=Opx)ma~{zs8DFLR7gH?=btxi7 zvVqEnOs4hyX6xdn`g(9^kPxFJBEXiar}H3nh0l2SIKp$S7^H{##{OVsMa4ZVtOCKO z^^AD%dSc~pR)1wtx4Sf&F&uhk-|vi!WYgZ5hF!t*FLMo+>+5>)X{z<LnU*>}_*FEJC-iCWkW7!FwoDbEKt+nItFQH2$JdV# zV~ftlOiSK8O*sXpR|@Lp(eM3?1BmTLvfqqXTx}bHu*Noz#Nq?ze358zOHsD3d5m_; zuF)O;Qo)MqENGm57?)36B$$rXz^2E7Gkd;uP-0h8J#a8xFVpiKfo^s?( z_DW-Y8G>3%3cLZ4Tv~bF1-SU+@y%&DSVoTom~%qG8D7tlM0zTVry41W(I2%3Zb~{g zbu4KC3r%U~^BN50dx7_$dfUzfRc+*^PB6!70LsUh*9Er04HnxQ(C8XgD-7 zNu(Z>{q$(H|I9C~F?99q-Ni3l1)3?-N_LPX-Ex~6*@At=NiojPfvxd@yD6+n_$vmmr-RNiDR$lwK=+(?0|*U4l%* zk0A+t2IV2yZnAqE0Wa@zw#pj`pyUrlru`}y&#n~s>cN!?ENcp|_wNHf}oPwP&ci~GQ@h*-S(0nYoz@S zwx76VDrBVo-aETiIbTxoJ|f5|0gpHxHxudqBco|k2q}8UUqvnY6#EmetEMTBCNm%2 zbw@Mb$x*g?K;g&y)|!=AEQxv-7xpD@5HQDoR{ASMw!36TRfFYxeY^gJGt)#beC^Sz zYQ}kwyC4HA0V*>~1$(aZwXDy%NtM1S5q$GR66(dhp2{wWK>b*eR~qoEknZ*+rTkd* z&%h@K_PU$;SvIl&C&eVU9(EB1BKk;=!}$w0FF1@-o;M3D2X(MHdXqIx_9L?bWE5hB zGGLEPCu~NxIUJaa`u??x z_&&*c+b`7|WFI@K-A9QbafZ5@qXE7SlU$j(2hPH3@$2`XoF|54eb{Xbb~^}~vFD_I zdaVTFoJ8k*bL-BZ2QF&N7y$mZ8MUFUl<{AY!5D;G^j@4!+`QZFJ8my}*gZN{_qOSl z2))4)L3*bGGC}esfA-{%O{nxx_+Swk6}O#V`S`f++0kfkNxAVR z2JJxc@>h;Th2LNjgSb0|8>HElLjp7=ms9(O%7l<I>!<9+|b(mej?!7rE08D zYe=T;^+`A@!O}Hovs0IiAfuQl;EHHvYm-&9Y^O^JvzHkYf{L@SLvP`p^k|3|$q|Tr zY0Kbx6p`vx!g6v8Z{?@#OW)FWs6(8z{fY!obWN0muZGN1lf@EIuxaVD%ZDD9pe{r{cB^IQFYh*H|{^nXo}x>HB0$eE%4v|_Y}OcPwLc| z#F5g#eBsgN_59!_fk?6Lm2)0LbjymTTA8J(#L~PgnDsp3u5aJ{JOc08Lj1+(NS=AS zt9vzhRkvQUXTP%J+2>G{aBy?Tlz-V!z2X`~(WzSs7c#0u!S5yxtgwoH-H|>Zu%yI* z0?mrE@pneuXkao^y1DqO+rj_} zjCQKi?|s41y35FMnZ4)mZ(~h<;KpBEqpQ7ETju}T`|^LNyZ8S=T8Qr35>ct_WX+OY z$ujnJFxJ8?qmq%zI#jrAS;mrmXY5P%FiQ5AFxG@@6SD6!%zVz=-#_B}%iH7OVP4Mi zI_Em)y3X@@Uf01i@=gTyRTFan;Rhftht#((sk49?MUQ@cxAl#ef}ejC_#kjL=+^8Q zSt^)Yx)9G-Rp}tGHF}WBTen7^WwI6*7Io%K7o1235xTaR24Dj&<4}uft_Gq%>HKP+l)}Yciz8;YT6U! zLah(14q6LG-5KR@{BXg)^Ykq;O0$n;Aq_sVl9YrX1qoe>BRD>rJE&Dy5 zCVd@)WREopU7uXYVv7JQ=TBd9p9jX{Kw0?xlC0eOGiR*PR-zJHc;l#1N=VGw1P-ND z`11bu)Z_b*C~=_UmHGb&grjBxi+pD&SA4oKkE2@niZdjhxm)q_8;VCIutg%2h>}#x zzxsM%O_vmyhhd||55AR*RS8Eg=I_{(H`IQX7N{K+!%k;2Oy`|<6s|Z)TOV;ltLyrd zih304rk_VM`}T(eBLDHDowHl<=K&iZo|+<*nRfbaIg9wWcg2c@gQH8NNkN#oLRXJf zUgIluAg>JI%bKa~-vmhH^~%EDImocfo$)^H#5h21azO&>zCkQa@+XABNe$=NDmId4 zr)=TBSY4~GAW}ZUfJOmZwu*V{y=O3M;-#lo5GI@YA)BV<7Lct?-ai6Sci(#-cH_)O zDmTofIE2ETQf_CmNPgqIf-{qr;p6DsN#&CLW#=)H@R6b!P5L9d<0dY0cz#Zb6~n;2 zxh?Vd=V=ULTXxp$FpE;*>}V{Ig&tVa4MAyT9fu~L_>Bp1DC}nh6xzQU;#edF7KF*A zen_Nw@;CM6rG)~~q-VsGX;+CPwE5N(xfq7&?>)tk>iC>DEup>!9{ zYds^g7ArTSfXlZj|+V|r3 ztPYjoMH%_q9w?`k1@0NO2`&(9Nt;~25)VGr{wcJEpMLtpc39m_(fsdZ3V1q@>Pd=D zZ6;i=JaHcm>E&pL0Y#}QM<8}Me{YUE;7;C6QFf|A6NKvJ(AxCR-fssq37O)56xnKr zr5?=cp;ccuzf|1C?`JKh+s^lCGtqL&8WtFyYOPuRyjx^}ZPn959hn^*Vc&g)`#^IQ zt-X0jBCW*GO-yxh@o)#%QxM0h2RJck-NcMlC!UWu!fz>bSkBXLWfbR86QJ83+NVNm zD`l=M!)KFh34n{+6afcY`@7Qg+5BrLm$0hb6Roq3KN|0fXN{SNoJ5LRx-Utfiuh;S z>^1a~gs*VM_7d)3yHRH11W|`5_P*lR2j^Z)^KaZR-M+SK2Jvc@j(}2owGRexv zmBH(6V={f}r>>_Jsq$L81&oL#T$H~UbxU*27vG4p=Q>hwZ^;b})!R8;`)}n&Nlh9ItH+Vf-4a={s1GDhH zosYrs6FB@i#bo7E9+>1~QJdO`<}Jj2vz0huNasJQKbPC7c>vtbEoGWW{EuH{pY z6~&n?xQ{_JZ`LaCnK{D~X@U~?3TCd5iDGxe-`CB%Hn>ePHI$$>{Fz#-^z8#Oo;ba= zWrIQo4C;PJaP+Wg$X+oDNX{tA!x0_JuWidm0ayAZaHS-5s#L ztF)7!X^kGHJuFKsGcgqs5f0tiQ5ZG&si_q3TZQW|iPaq2{fyU!)|L~R=@JQ%?(M8j zxSNt~o-YuR%b=H3yyl7>s{w)hFO2<(7nysPcWb%I{CvB_@tK_Mmm>7snJ_1^n(WwR2VEP;|2&W$~K7R zUNSj(kuSwR8=OZRhN|}3s3n%VmoLnHt&|vmr(ptu;f~sv-tbO^B9cdowMN#d(NgqK ztcMHl`)ObPF1P}L&ASraOOH@meqPtQeBQtx*NaG3a9S`bw1gmf^-laYe73US-XxyU z(;{p!AqUmh8Qu~~G#xUkI*Lf&?2Tao;mkJAaQppCGxzF#ill%BBrk#*UN`-S?%(N* z-0%6MR(Qck!W=ayvF)Ig~XK+8FD@=mHFv$pP7q#X1T#J zXZ;0|uQ$JFyOWzI*|AXv_x_Oqms+FX0yPLQvFG~uhP zFeEp=^uA7k8z?ShAST>Hk?5`%|29V6Ual)^+jpZ@7yJD7owd-)1%TFrRsheH<6h32ik`Si+W$t17g@2;x( zKV%qCUj0}1lAHD;BwdvT)f$M(DoyU~@BRKPahjZpLZ%VzZn=sv(Kcu{`D8O{0CTX^ zAXmCRk%ve21R~Tntm=6D79&vup@)+9Y5wR)9D0aB@c}rs+Jm;Rx&s`_64wzU3Lg=x zEHM4EQ&zB7>3+6q?I}8sC!V(R;5>g?$ zx}gz(l$^9RtQW65F=WMWhmIqrp@Joz%h&s&*J9;_zT0w^Q}*A{OmHo^oa(ZqIKGV) zhMNC|7Y7Sw4VBE2@40WZXlv*t_lHmehiSOpWn{cCk4NwBs9gANFtSr8_zwIuX?%6U z!_Iom+p)AJ32F>(lgU0B!u7gF2k&-T)mdHMVc{lcWOgPDd z#Ysqwx%*&%v|b*Xa>seMDb8Uvw-N7=-O>qp{Wn3Ew8JX6;PDqXpq@{HuasYSGBxK%^>4$N{w&MNg+7POX z5MWGnj>`um$V-EXWO`;bU)GDx9fdgMv|~RBkYX^S?drbXdg-wv*^9b~nUqhqfjIWZm@_c z?B^kL({BlODj0>b)8x527h^4?W%7Ybr)4Vnuw@6`RqC0gGK&0tZ}l!&7BT&*a^P|R zuXgnn7y~|mY35IV%WEDiAOAy4u#ZtG33pgR_>3w6DGUtbFWi5JxJZwo$MH98ECrk1 z6`baMTk=o&T72^*T){i{pmqC4YV+Z@VOcAZ^tL7~MiqD-B4Vs)W6n_G4`8H8gG`7k zw@VttW0!i`hb*667qArhW1Dk>0B}a~06l}E(rDnHID`tYz1NOKj-QI{C{5X$b0*bu z=twWkN^sLdiA?hAq7vP+k1t_$?-|wpQ_dg?E7^<_tUZ_^)opy%wy>Qlc>P=lnj_>C z^=&IKFkCB8NyjwHa>N)zlPsi4=ukYE9Yk-Ez!7cr@IH;P6c}NNrhq^mm1iFG;V50- zAmMplN927#)4ojj)kY)IN^^bX+FzYW6ToI6rci}&W)+=*N=5bkk)q;fea_S)Q(&U; zFHaL&-?z#wib3kxiWjlJB-Hv_gnB#;Lo_8G&#lmN!5ghwV+LWkw=syh6oQ_prgG(; zyRwa<$``K0=A-H?l(uJ^pnEx2$Ah1OR|{4sf$s}>svH3DhJCmTF{iFDB0l_raKtT@ zgHLLhEeS&J-Mst9&Z53_Q8Z@6tY7?H(46eom~6hQrWH=B=42_mQ5VK_ zIBUb?_eTKRv{Hb=#huRO7PUW~+B$Y-q&!(@3)~;bETXDuyBDME(Vtx|+bZKzFM{YF zYf)}|Z)t!3BC_E5zpW=uU5PPU?$1qc7foNP7z_3Yr1LnXaf|}C6eE|ClHfaDG2&oI z!A$Y^-j6r`c5KSYdM2viml$1FpJp65(|e8YMX(;1i!pS)5_#-lA{m&TdXQ0^#LH~b zvFBERKD)XjL9&UteNDcgp|4=mN@eR-hsSomT9Q{mImb+Y*73%?)#+{~45vZCf8UM7 z@Afs%)Gl!B>5@1O1nv%Kd&bppJFz?QBysrT_fnBnLUN1)+A zc{vC}TFvL4GnNGon|YqTfj(oFzpj@m*~cJtH_A8KdnfyVyGD~k~tl8}6kS)i9!v6+9sPd&1$?NvR`8r6%E z+$RnF3mD-gLR4G<@!1ig96}x(t9ys&_&DJM+U--3$5{8}zr!qit(?lc#2-W4 zz+sXNcR?UQrsO5^mf&jZ9MV7%=1ynCzzd@@A3jUpWYQFLOvI-m=jGMT&$%={K6>yby8EHCoH)P8HMS?s z53u;Bzcg)bMb$|t!8im(Dr^g0+r>nbj$tP1Ogq_2WsbhlGKs@;zr5+>di^YCK2x=~ zL3+#vi|m++Yr9z3@-^M%G?7r2NNcqIFxSOkAMt7HqvDCiCb~`nTatIy||VlqnWNv{2z#hgdFccrpdqg&Rmlb5G^ zU~l*?>e(>N4~Ns*YjpHn*#*$OCG;%7D*CELC+|G_j57JqxYED|VQFvgzL?E~*_m6F z1)70+AK%npo+LI`E2TKT7DmnM>g|%wCS1H&^1bf9r$-iG+CoIfg+ShF$dV{~xt%3( z>_?IFbDiyW$KsVo4sO;cX~`ReY2vNfL+3Y+r$YftE~E}oapP712Lga5~z_>5EhtDKaR*S$KZOoZv!Mf!PP+`g&mTT z*XTI9*BV&(!fji~-8JbsK4{*2{OXwQo-reg8 z|9CpvJh+-1sEkye-5NgKchFv08_^1&nJCRt^;9)(JvJSqfV#{;_HPE1hEgwr=%R_@dt#8taD7 zC2x``>0(vQWgPim=T#dg+6z2jqO>A7X3&Jdg*~k!65I0kYyS&_i zkGH}u1rikLEdz}9yAa=X!ae>Yn9gQv@hdpU=_Ys?R_C`JW|ZZLlkZ>VK6&~t7|AMi zBp>yS9Q85-40qK~;onp)VSc_|eRkgYi%#ebNV||{TVg)}FZWl;cqfLL-Si8--%ILB~ImZE6?|rAl7&{<{#lWJbHJL0_oS?+C@+1`WPVwM`uSXu+4g<@y6kN+A)C2dH8y;Dt@Z0 zGUVIG<#B9r^319-qTcmhkmwAhQO z<1GEriL6vJWyPH~9w-od_O;di;YOW@AVBCa2;R8}JJ{2R`5lsmDqiLpj7Q_Mb{FU0 zmD8w)D!3Q4o9RiJR7Nh5mt@xc&-$In-vg;$%Rs&u7yaklR(D-9mjftT-ZNWz{78|z zMPK32cxXIB40HSYWx2)~uP&iji%kMRAH(cVfHJ(v9_}cGfeA?mgT=cnwf$6!P1ASdm-ars zPHp(;$3{FKJNT1JutBcWv$PfD#fC>o9_1iEj+OLBddfFMj z#OVA5F%S9(gNny;XdUgJ#X>IWnsm|ga*+poTcx-qwj&B{{d(Ra3o( zcJI9V6^x~Uv?hS_3ua6PPu-<<2PkVopC3)0R8nap0d;TsEnwbil4M6 zH)(ogfRL?XE_gDQt-y0e897#pcg4r>MVXdadTa`UMabRi9aU!tl~qb4d00W)q!6*A z!(AznGPLGV&zv*lN*^&|y=z)lr{G4w-#S zhm6~}+tt~Ib+E1GT)7dlcVA;I4xzMB2pIxQpkAK2hEe$DN_BVpg_3I*TZ>~73Jz{76 zAdH$CoLb*?bsuka-6=%90QVN!F9X{f^t5A9fg#;1mC~*OKg0>v?#z&6w-2yF9#KTw z-1`+T;)8y32PJUM;3S+9B(xB6nW4716|6BP1kXxXcQDEAP=h(105awf<=J42aqrO3XD77rwLO~J84$9?%xYLu&sMV;r zxFN<$g$JJkmT!COr{#u^6q@eEh%=xd6IuYWRh@p&(YC(Y&Bev}J)%=jX7h_E-k0Je z!pvMUXyqf#kYSdv%g)`aC`e5mbaoEvKh}+kBk*B4g(bBNO|hPRgkAWnufbEK8b%qn zDf6Y(RUzW5ivWmBFoWJDeLon#isggW`h(#5{({Zo^|aD2=|!W}H8$ST3`_Xr-9>*R zkR#IZVU&iGeGtwcOwF6|EGv9ermm%dT)jRkj>4+A2pD`1bU29;_@ivyWt_I5e)5a9 zUFZgo@lf~I=C|c`AGH`a{ft|2x4$=Q^Nb(Oo>&`nmCU6RZObHidb|wZlW8V&JM>7b zI1!^)1WU*V0ld)Bxc*p&6oAU;myj-(b4dh}@S zfq-n_2J6(Z#kmqU+oG<`nmhf(?#rEOkSJ+jL?)^~ICoodhKevVx$W6)L#R{FinK-k`%`4g~@n9#~*@Z2c+aNW)64M0j zB^i!A+kE{|U{lqusacW!p7F{r*S~>@`E!H10&*Xoh<;TllDt^AxZ+y71+KBA?IwgK zRmRIxti73&23=$L*%L6v$!Km>mmBZh#6Osd6Z(A$a%cGrKO39u*X;0xAmMgALMc*q z$J^JW&wayZx@Hz`qXk9J(;jSFUPc{3s@?+2+CK4UxSZC6&26;C1x}pHvkUcag?FanNIzzC?jNFKvdaz5WHx30GhX$vg3-)cz$ z^a&!vyC^lv;#P03u*e}B=W;0aIPDLkk#3fTRn^mUzNZtFIp09)%$Q}QR=lP7Ub${R ziIjlZEIY*Pet$dK^g~AWWo>D3o*lSx@FSWUGoZ zC9iflFhk7GgBva8*I7OL?h?#zv(*;5hrpB@)Jst$?a2y@2nNlN(oaiCw=DuK8oJWd zp~scu#a9`0fbLFwm_3p=uoV{KY?CNDI^XdXo`Uqu;^cKIPd6tvNn=qu!PN}rh%Q$? zYpABee$#`uN=6A?uln5GVc(4_^K%R{Hhn+6oIyFrHq;9xXC%tGCf*5;8$a`+xmUAI@Ob*~oSdb(M>8AtMB&*8s-~;wYdAGoC(0k185L#i zu*eM!&*#rHtEK4ElfD^0vn|?Lf7&N6-|OLS(J`pxR;1#J$*GQWIz*fU8{eD;Xc$IS z|C_7`ltt4dKZi`SUjZ-Rd!P68z9X$>w9mQT;}3=QI1vvr>3_4Pho z_Olf4jP6sqSlc!g;b2>c<;yeu+?MleYW9h7yB@ctSBKu73Utjjcc!(l|4)77jvvl= z>?Hb}S!aTOW+7?^Coy-Wacn8dP2?y(ovrMzec|xXF zHIWsyvNxbCohD^fR(N9+_RAxaw`;y^VL4-BR{0pLL%nJxxhT`3_Hjy!|BdASUccW` zvT9iK94Uz=u6FCvc7^>7MeFUHbA9Zg&W{9TWlI7#uk5VuAU??4J5IWMeB82XW5c$H zEb&0Z*TDkCg}m|dT4QjZHVOCgC@HqX$xa7li1|&bpaS*rkl{C9cpxR5=jLeD_qhYJ z1Cfb5VUCwxq4g#t|ctKVCbzrgE&x4NI3&CSPH|W># zBehf_4Ak`n`U|nY43x)FuSx`hF$5h4iBX}_1pFP1x{C?us{R7C*zd2i5U00bf>l4M zt@A=8A)I#9+9rTChD`i@o-XLA#?U3m#6xPa(o`WmQ!u3JIz4#ZH(-w-U0kJfvEI}M z5jVi2Im<&x%^T32u_u^D5aKh82G7C}2vHxsNi7wnrOCel+Gej(A?ffNb;#(uH$0G7 zDu)tOd+V=utK7MB;O{blk!8c4RH*Z^wby4)oP*%`z)aP}z@ummcyvAsDu7IX`kKf` zptT!l4QYAtP)9P>kc|kE8mPq(iD<{{W|QA^kSccS&?kH=&0n+p6~)Zp53a2KO|6mV zp$9Vk!p^WAf-7rM;0+KFCmY5&1S7N1A`L|60`)h;A#b=rkE}K;)D~cr*MOV%--nAg z{@;iH){pwJ2SRNCzNCZx`vD31Ulpn#{wEiz6#lmvs8aafX82DE|Jw}zzc#}hg-)-4 V!zj{wQHMGNsHWkAiu;al{txy&3PS(@ literal 0 HcmV?d00001 diff --git a/src/assets/images/doormile-logo.png b/src/assets/images/doormile-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1cbe324359d358f60159f03d6dcd1ed4531b38b4 GIT binary patch literal 13651 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 literal 0 HcmV?d00001 diff --git a/src/assets/images/doormile-mark.png b/src/assets/images/doormile-mark.png new file mode 100644 index 0000000000000000000000000000000000000000..f64351844eb8674eeada1cad25774313d5258af3 GIT binary patch literal 83551 zcmeFZ^;=Zm7dA|Tgn%F+4T>TSQj!V?NC`+u!%)&79fKfU(hZVIcgN5T1JWQ}(hN1^ z%zMVq^FDvXbA9H_}KMDL#0{@f1|0MALrUXtDm(WN_N#|ddPZ7;Uvi*ybpy3y2B&4KlQMJmO4b%00 z4EPU$=;_AyFGiYGsI$UFe*; zH`^Y1d8Wn#MBG!tf6>+|*T|Zj5D2hhn5gO0?Ox4S7(&l$v|j+vXiB4E??*o6%dP7d znUIww=@@bLdhla|_s-x7f^`6XcK2;FutHB8TWiE$$D}g}y>X|Y*9`Mk- z8tNhIf(MWUGHeWH<~T2WG#QzvoUhiB-{(pJ6RQ9BMjY$CI;SAQK${A9{Mo-xxd|%Y z6fCe20^g4N7yU)kSnrsjDtg#5GN7OT<`oTKy=Pv0R{|gkP{_X#Gue2**s1~^VCKFA zlsM+;)6OFfXz@{xw6FoPj4dlrfXG|_o7XNEwWs=Tz%0A@sCVj9Q15thrCmiN#Ki7v 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

    -
    - ₹{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==