From dc9548e8c9fe847a3aa04ac1a06a00bbd2e72fed Mon Sep 17 00:00:00 2001 From: dharaneesh-r Date: Wed, 1 Jul 2026 12:17:54 +0530 Subject: [PATCH] updates on the dispatch page active section regarding the map sectoin --- src/pages/nearle/dispatch/Dispatch.js | 110 +++++++++++++++++--------- 1 file changed, 72 insertions(+), 38 deletions(-) diff --git a/src/pages/nearle/dispatch/Dispatch.js b/src/pages/nearle/dispatch/Dispatch.js index 795bac7..82531c9 100644 --- a/src/pages/nearle/dispatch/Dispatch.js +++ b/src/pages/nearle/dispatch/Dispatch.js @@ -1266,7 +1266,7 @@ const Dispatch = ({ // show ONLY currently-active riders — their cards, routes, drop markers and // live bike markers — and hide everyone who is offline/idle for the slot. const activeRiderIdSet = useMemo( - () => new Set(liveRiderLocations.filter((r) => r.status === 'active').map((r) => String(r.id))), + () => new Set(liveRiderLocations.filter((r) => r.status === 'active' || r.status === 'pending').map((r) => String(r.id))), [liveRiderLocations] ); // Default to the slot containing the current wall-clock time. Use a @@ -1319,6 +1319,12 @@ const Dispatch = ({ if (liveHasNextPage && !liveIsFetchingNextPage) liveFetchNextPage(); }, [shouldFetchLive, liveHasNextPage, liveIsFetchingNextPage, liveFetchNextPage]); + // Sticky cache for active orders in the Active Section sidebar. Keys are orderid strings. + // Orders are added/refreshed when orderstatus === 'active' and evicted only when the + // API explicitly returns a terminal status. This prevents cards from flashing away + // during the 15-second poll cycle when the API temporarily omits a row. + const stickyActiveRef = useRef({}); + const liveRows = useMemo(() => { // Flatten infinite-query pages, then dedupe by orderid. The deliveries API // can return the same orderid more than once (e.g. when page bookkeeping @@ -1586,7 +1592,7 @@ const Dispatch = ({ () => new Set( (isAllActiveView ? allViewOrders : []) - .filter((o) => String(o?.orderstatus || '').toLowerCase() === 'active') + .filter(isActiveDelivery) .map((o) => String(o.rider_id || o.userid || '')) ), [isAllActiveView, allViewOrders] @@ -1600,18 +1606,72 @@ 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. + const activeViewRiders = useMemo(() => { + if (!isAllActiveView) { + stickyActiveRef.current = {}; + return []; + } + + const TERMINAL = new Set(['delivered', 'cancelled', 'skipped', 'complete', 'completed']); + + liveRows.forEach((o) => { + const id = String(o.orderid); + const s = String(o.orderstatus || '').toLowerCase(); + if (s === 'active') { + stickyActiveRef.current[id] = o; + } else if (TERMINAL.has(s)) { + delete stickyActiveRef.current[id]; + } + // Non-terminal, non-active statuses for a known order are intentionally + // ignored — a brief status regression in the poll data must not evict a card. + }); + + const stableOrders = Object.values(stickyActiveRef.current); + if (!stableOrders.length) return []; + + const riderMeta = Object.fromEntries(riders.map((r) => [r.id, r])); + const byRider = {}; + stableOrders.forEach((o) => { + const key = String(o.rider_id || o.userid || ''); + if (!key || key === 'unassigned' || key === '0') return; + if (!byRider[key]) { + byRider[key] = { + id: key, + riderName: riderMeta[key]?.riderName || o.rider_name || o.ridername || o.username || `Rider ${key}`, + orders: [], + color: riderMeta[key]?.color || getStableRiderColor(key) + }; + } + if (!byRider[key].orders.some((ex) => String(ex.orderid) === String(o.orderid))) { + byRider[key].orders.push(o); + } + }); + return Object.values(byRider); + }, [isAllActiveView, liveRows, 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. const allViewLivePoints = useMemo( () => isAllActiveView ? liveRiderLocations - .filter((r) => r.status === 'active' && activeOrderRiderIdSet.has(String(r.id))) + .filter((r) => (r.status === 'active' || r.status === 'pending') && activeOrderRiderIdSet.has(String(r.id))) .map((r) => [r.lat, r.lon]) : [], [isAllActiveView, liveRiderLocations, activeOrderRiderIdSet] ); + // Clear the sticky Active-view cache when the operator switches date or hub — + // otherwise cards from a previously viewed day/location could linger. + useEffect(() => { + stickyActiveRef.current = {}; + }, [selectedDate, selectedAppLocationId]); + // Per-rider canvas renderer for the actual (right) map in Compare mode. // Single setter used by every interactive site in the UI. In uncontrolled mode it // updates local state; in controlled mode it only notifies the parent. @@ -1714,13 +1774,11 @@ const Dispatch = ({ }; }, [focusedRider, focusedKitchen, isAllActiveView, allViewOrders, visibleRiders, stats]); - // Count of in-progress deliveries shown in the Active view list. Drives the - // sidebar header visibility — when the active fleet has nothing in progress, - // the header (RIDER DISPATCH title + Active Fleet badge + order/rider tiles) - // is hidden so the "No active deliveries" empty state stands on its own. + // 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. const activeDeliveryCount = useMemo( - () => (isAllActiveView ? allViewOrders.filter((o) => String(o?.orderstatus || '').toLowerCase() === 'active').length : 0), - [isAllActiveView, allViewOrders] + () => (isAllActiveView ? activeViewRiders.reduce((sum, r) => sum + r.orders.length, 0) : 0), + [isAllActiveView, activeViewRiders] ); // List of deliveryids we want GPS logs for. Drives two pipelines: @@ -2719,36 +2777,12 @@ const Dispatch = ({ // duplicate, slightly-offset pins that clutter the view. if (compareOpen && focusedRider && compareViewMode === 'actual') return null; - // In "All Active Routes" view the base set is restricted to active riders' - // orders (allViewOrders); a focus selection still overrides as usual. - let ordersToRender = allViewOrders; + let ordersToRender = allOrders; if (focusedZone) ordersToRender = focusedZone.orders; if (focusedKitchen) ordersToRender = focusedKitchen.orders; if (focusedRider) ordersToRender = focusedRider.orders; ordersToRender = ordersToRender.filter(hasValidDrop); - // Active view drop pins: show ONE flag per active rider — the drop of the - // leg they're currently heading to (getActiveOrder) — so the flag marks - // "where the rider is going" and sits exactly at the end of that rider's - // route line. No other drops are flagged in this view. - // • Focused rider → just that rider's in-progress drop. - // • Overview (no focus) → each visible active rider's in-progress drop. - if (isAllActiveView) { - if (focusedRider) { - const active = getActiveOrder(focusedRider.orders); - const id = active ? String(active.orderid) : null; - ordersToRender = id ? ordersToRender.filter((o) => String(o.orderid) === id) : []; - } else { - const destOrderIds = new Set( - visibleRiders - .map((r) => getActiveOrder(r.orders)) - .filter(Boolean) - .map((o) => String(o.orderid)) - ); - ordersToRender = ordersToRender.filter((o) => destOrderIds.has(String(o.orderid))); - } - } - // Pre-build the deliveryid → sequenceStep lookup once per render so each // marker can resolve its step palette color without an O(N) scan. const compareDeliveryToStep = @@ -4568,7 +4602,7 @@ const Dispatch = ({ )) ) : isAllActiveView ? ( ) : ( visibleRiders.map(renderRiderCard) @@ -4651,12 +4685,12 @@ const Dispatch = ({ {liveRiderLocations .filter((r) => isAllActiveView - ? (r.status === 'active' && activeOrderRiderIdSet.has(String(r.id))) + ? ((r.status === 'active' || r.status === 'pending') && activeOrderRiderIdSet.has(String(r.id))) : riders.some((rd) => String(rd.id) === String(r.id)) ) .filter((r) => !focusedRider || String(focusedRider.id) === String(r.id)) .map((r) => { - const isActive = r.status === 'active'; + const isActive = r.status === 'active' || r.status === 'pending'; const pinColor = isActive ? '#16a34a' : '#dc2626'; // Look up the rider's in-progress order so the popup can show // where they're heading next (drop customer/area + originating