updates on the redesign page for all the pages

This commit is contained in:
2026-05-30 17:54:07 +05:30
parent ba88501bc4
commit b8097efbcf
20 changed files with 8664 additions and 3331 deletions

View File

@@ -0,0 +1,85 @@
# CLAUDE.md — `src/pages/nearle/dispatch/`
Rules for editing `Dispatch.js`, `Preview.js`, `CompareDataPanel.js`, and `dispatchShared.js`.
**This is the most complex area of the codebase.** Dispatch.js alone is ~2500 lines, mixes Leaflet imperative APIs with React state, and shares its batch / time-bucket model with `deliveries.js`. Read this before touching anything here.
---
## 1. Batch / wave model
Dispatch.js defines the canonical batch hour ranges. `deliveries.js` mirrors them — **the two pages must agree on which batch a given row belongs to**, otherwise the same delivery shows up in one batch on one page and a different batch on the other.
```js
// BATCH_OPTIONS — half-open [startHour, endHour) in LOCAL time, not UTC
[
{ id: 'morning', startHour: 0, endHour: 8 }, // 12 AM 8 AM
{ id: 'afternoon', startHour: 9, endHour: 12 }, // 9 AM 12 PM
{ id: 'evening', startHour: 16, endHour: 19 } // 4 PM 7 PM
]
```
**Gaps are intentional** (89 AM, 12 PM4 PM, after 7 PM). Rows that fall in a gap belong to no batch — *not* to the nearest one.
### Time-field selection (`selectedTimeField`)
Default `'assigned'` → bucket key is `['assigntime']`. Other options use other timestamp fields (`pickedtime`, `deliverytime`). If you add a new time field option, make sure `deliveries.js` is updated too — they read each other's bucketing.
### Don'ts
- Don't bucket in UTC. Use `dayjs(t)` (local), not `dayjs(t).utc()`. The original deliveries page had a UTC bucketing bug that hid orders mid-day; the multi-line comment above `getRowBatchId` in `deliveries.js` (search for `getRowBatchId`) explains it. Don't reintroduce.
- Don't bucket bare `YYYY-MM-DD` strings — they parse to midnight and mis-bucket into Morning. Skip them.
- Don't add a 4th batch without updating both pages and confirming with the backend what the new boundary means for assignments.
---
## 2. Leaflet integration
Dispatch.js uses `react-leaflet` for declarative tile/marker rendering, BUT a lot of marker behaviour is imperative:
- **Marker icons:** The default leaflet marker icons are loaded from a CDN at module top (line ~454) via `L.Icon.Default.mergeOptions`. Webpack can't bundle the default sprite, so don't remove this shim.
- **Polyline offset:** `import '../../../utils/leafletPolylineOffset'` adds a polyline-offset method to leaflet. Required for showing two riders on the same road segment with parallel polylines. Don't remove the import; the symbol it adds is used at the prototype level.
- **Imperative marker registry:** A `useRef` map holds Leaflet marker instances keyed by `orderid`. Opening/closing popups is done by calling `marker.openPopup()` / `closePopup()` directly — *not* by setting React state. This is intentional; flipping state caused full-list re-renders and dropped frames.
- **Popup overlay:** The "centered" popup the operator sees on hover is rendered outside Leaflet (it's a plain MUI dialog absolutely-positioned over the map). Leaflet's `<Popup>` attached to the marker is for click context only. Don't try to consolidate them.
---
## 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`.
Skipping reconcile corrupts route sequences in the database. This is the single biggest production bug to avoid in this area.
---
## 4. State that drives the live map
- `riders` — array of rider objects with latest GPS. Updated by polling `/utils/getriderperiodiclogs`.
- `selectedRider` — currently focused rider. Triggers polyline highlight + popup reveal.
- `batchCounts` — derived (via `useMemo`) from the loaded order list filtered by the active batch. Mirror this shape on `deliveries.js`.
- `selectedTimeField` — which timestamp drives bucketing. See §1.
---
## 5. Performance constraints
The dispatch page renders 100+ markers and polylines on every render. Watch for these regressions:
- Don't pass new object literals to memoised child components (`{ size: 12 }`-style props re-trigger re-render). Lift to refs or `useMemo`.
- Don't put `setSelectedRider` inside a `useEffect` that depends on `riders` — that causes polling-loop re-renders.
- Don't `console.log` inside a marker click handler in production paths.
---
## 6. Editing Preview.js specifically
`Preview.js` is the post-solver staging page. Receives the solver output, lets the operator drag-and-drop, then commits.
- 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.
---
## 7. File expectations for new work
- New solver mode? Add a new constant in `dispatchShared.js`, wire it through `orders.js` (the mode selector), and add the corresponding endpoint to `api.js`. Don't hardcode a new URL inside Dispatch.js or Preview.js.
- New rider attribute on the live map? Add it to the rider-popup component in `Dispatch.js`, not to a new component — Leaflet popup mounting is fragile across React tree changes.
- Don't break `CompareDataPanel.js` — it's used by the QA team to A/B different solver runs.