diff --git a/src/pages/nearle/dispatch/Dispatch.css b/src/pages/nearle/dispatch/Dispatch.css index 47bdfd8..25247c2 100644 --- a/src/pages/nearle/dispatch/Dispatch.css +++ b/src/pages/nearle/dispatch/Dispatch.css @@ -5669,6 +5669,47 @@ letter-spacing: 0.04em; } +/* Rider/customer/delivery-id stack on the left, Step badge pinned to the + right so operators can read the sequence position without opening the + Details section. */ +.dispatch-container .dispatch-popup .pu-header-main { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; +} + +.dispatch-container .dispatch-popup .pu-header-left { + min-width: 0; + flex: 1; +} + +.dispatch-container .dispatch-popup .pu-header-step { + flex-shrink: 0; + display: flex; + flex-direction: column; + align-items: center; + padding: 4px 10px; + border-radius: 10px; + background: #fff; + border: 1px solid rgba(255, 255, 255, 0.3); +} + +.dispatch-container .dispatch-popup .pu-header-step-label { + font-size: 9px; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; + color: #7b1fa2; +} + +.dispatch-container .dispatch-popup .pu-header-step-value { + font-size: 15px; + font-weight: 800; + color: #7b1fa2; + white-space: nowrap; +} + /* --- Body sections --- No scroll: the popup expands to fit its content. Width is the dimension we constrain (via leaflet's maxWidth prop) so the body grows downward as diff --git a/src/pages/nearle/dispatch/Dispatch.js b/src/pages/nearle/dispatch/Dispatch.js index 4399108..315c8b2 100644 --- a/src/pages/nearle/dispatch/Dispatch.js +++ b/src/pages/nearle/dispatch/Dispatch.js @@ -1987,12 +1987,19 @@ const Dispatch = ({ : riders, [isAllActiveView, riders, activeOrderRiderIdSet] ); - // Active section sidebar riders — built from the FULL day's unfiltered rows - // (liveRows, all batches, no GPS-status gate). Uses a sticky cache so cards - // only disappear when the API returns an explicit terminal status (delivered / - // cancelled / skipped). Temporary row omissions during the 15-second poll - // cycle (single-page refetch, brief API inconsistency) no longer cause cards - // to flash away and reappear. + // Active section sidebar riders — built from filteredLiveRows (the SAME + // batch-filtered rows that drive activeStats' "Orders/Riders" header tiles + // and every other view), not the raw unfiltered liveRows. Building this off + // liveRows previously showed active orders from every batch regardless of + // the selected Morning/Afternoon/Evening pill, while the header tiles above + // it (sourced from allOrders → filteredLiveRows) reported the batch-filtered + // count — e.g. tiles reading "0 Orders" while afternoon-batch cards still + // rendered underneath. Uses a sticky cache so cards only disappear when the + // API returns an explicit terminal status (delivered / cancelled / skipped). + // Temporary row omissions during the 15-second poll cycle (single-page + // refetch, brief API inconsistency) no longer cause cards to flash away and + // reappear. The cache is reset on batch change (see the effect below) so + // switching slots doesn't leak a previous batch's sticky cards forward. const activeViewRiders = useMemo(() => { if (!isAllActiveView) { stickyActiveRef.current = {}; @@ -2001,7 +2008,7 @@ const Dispatch = ({ const TERMINAL = new Set(['delivered', 'cancelled', 'skipped', 'complete', 'completed']); - liveRows.forEach((o) => { + filteredLiveRows.forEach((o) => { const id = String(o.orderid); const s = String(o.orderstatus || '').toLowerCase(); if (s === 'active') { @@ -2035,7 +2042,7 @@ const Dispatch = ({ } }); return Object.values(byRider); - }, [isAllActiveView, liveRows, riders]); + }, [isAllActiveView, filteredLiveRows, riders]); // Live GPS coordinates of exactly the riders this view shows (active + has an // order), fed to MapController's auto-fit so the map frames what's rendered. @@ -2050,11 +2057,11 @@ const Dispatch = ({ ); // Clear the sticky active-order cache whenever the operator switches to a - // different date or zone — stale cards from the previous filter must not leak - // into the new view. + // different date, zone, or batch slot — stale cards from the previous filter + // must not leak into the new view. useEffect(() => { stickyActiveRef.current = {}; - }, [selectedDate, selectedAppLocationId]); + }, [selectedDate, selectedAppLocationId, selectedBatch]); // Count of in-progress deliveries shown in the Active view list. Derives from // the sticky cache (via activeViewRiders) so header and card list always agree. @@ -2979,18 +2986,30 @@ const Dispatch = ({ )} -
- {o.rider_name || o.ridername || 'Unassigned'} -
- {(o.deliverycustomer || o.customername) && ( -
- - {o.deliverycustomer || o.customername} +
+
+
+ {o.rider_name || o.ridername || 'Unassigned'} +
+ {(o.deliverycustomer || o.customername) && ( +
+ + {o.deliverycustomer || o.customername} +
+ )} + {o.deliveryid != null && ( +
Delivery #{o.deliveryid}
+ )}
- )} - {o.deliveryid != null && ( -
Delivery #{o.deliveryid}
- )} + {o.step != null && ( +
+ Step + + {o.trip_number != null && o.trip_number > 1 ? `T${o.trip_number}·` : ''}{o.step} + +
+ )} +
@@ -5019,6 +5038,15 @@ const Dispatch = ({ )) ) : isAllActiveView ? (