updates on the active section and the dispatch page section regarding the active order details

This commit is contained in:
2026-06-26 16:43:19 +05:30
parent 76c8e5062d
commit 848d1874fa
2 changed files with 193 additions and 178 deletions

View File

@@ -26,9 +26,7 @@ const ActiveSection = ({
}) => {
// Sort by live distance ascending (closest drop-off first; null/unknown last)
const activeDeliveries = visibleRiders
.map((r) => getActiveOrder(r.orders))
.filter(Boolean)
.filter((o) => String(o.orderstatus || '').toLowerCase() === 'active')
.flatMap((r) => (r.orders || []).filter((o) => String(o.orderstatus || '').toLowerCase() === 'active'))
.sort((a, b) => {
const mA = calculateEstMeters(a.rider_id || a.userid, a) ?? Infinity;
const mB = calculateEstMeters(b.rider_id || b.userid, b) ?? Infinity;

View File

@@ -1304,7 +1304,7 @@ const Dispatch = ({
hasNextPage: liveHasNextPage,
isFetchingNextPage: liveIsFetchingNextPage
} = useInfiniteQuery({
queryKey: ['dispatchDeliveries', selectedAppLocationId, liveUserid, 'all', selectedDate, selectedDate, 50, '', 0, 0, 0],
queryKey: ['dispatchDeliveries', selectedAppLocationId, liveUserid, 'all', selectedDate, selectedDate, 1000, '', 0, 0, 0],
queryFn: fetchDeliveries,
getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined,
enabled: shouldFetchLive,
@@ -1578,10 +1578,8 @@ const Dispatch = ({
// excluded here. Every other view (By Location / By Zone / By Rider) is
// unchanged.
const isAllActiveView = viewMode === 'all';
// Orders belonging to GPS-active riders — the candidate pool for this view's
// list, drop set and auto-fit. Narrowed to in-progress orders below.
const allViewOrders = useMemo(
() => (isAllActiveView ? allOrders.filter((o) => activeRiderIdSet.has(String(o.rider_id))) : allOrders),
() => (isAllActiveView ? allOrders.filter((o) => activeRiderIdSet.has(String(o.rider_id || o.userid || ''))) : allOrders),
[isAllActiveView, allOrders, activeRiderIdSet]
);
// The single gate for the whole Active view: rider ids that are GPS-active AND
@@ -1592,8 +1590,8 @@ const Dispatch = ({
() =>
new Set(
(isAllActiveView ? allViewOrders : [])
.filter(isActiveDelivery)
.map((o) => String(o.rider_id))
.filter((o) => String(o?.orderstatus || '').toLowerCase() === 'active')
.map((o) => String(o.rider_id || o.userid || ''))
),
[isAllActiveView, allViewOrders]
);
@@ -2305,8 +2303,12 @@ const Dispatch = ({
if (focusedRider && focusedRider.id !== r.id) return;
if (focusedKitchen && !focusedKitchen.riders.has(r.id)) return;
const activeOrder = isAllActiveView ? getActiveOrder(r.orders) : null;
const isActiveAndShown = activeOrder && String(activeOrder.orderstatus || '').toLowerCase() === 'active';
const rOrders = isAllActiveView ? (isActiveAndShown ? [activeOrder] : []) : r.orders;
const trips = {};
r.orders.forEach(o => {
rOrders.forEach(o => {
const t = o.trip_number || 1;
if (!trips[t]) trips[t] = [];
trips[t].push(o);
@@ -2320,12 +2322,27 @@ const Dispatch = ({
if (filteredTOrders.length === 0) return;
const cacheKey = `${r.id}-${tNum}`;
const cacheKey = isAllActiveView ? `${r.id}-active-${filteredTOrders[0].orderid}` : `${r.id}-${tNum}`;
const roadPath = osrmRoutes[cacheKey];
const sorted = [...filteredTOrders].sort((a, b) => (a.step || 0) - (b.step || 0));
// Aerial fallback — NaN-safe build
const aerialPath = buildTripPoints(sorted);
// Aerial fallback
let activeStraightLeg = null;
if (isAllActiveView && filteredTOrders[0]) {
const o = filteredTOrders[0];
const lp = liveRiderLocations.find((l) => String(l.id) === String(r.id));
const start = (lp && Number.isFinite(lp.lat) && Number.isFinite(lp.lon))
? [lp.lat, lp.lon]
: (hasValidPickup(o)
? [toNum(pickupLat(o)), toNum(pickupLon(o))]
: null);
const dLat = toNum(o.droplat || o.deliverylat);
const dLon = toNum(o.droplon || o.deliverylong);
if (start && Number.isFinite(dLat) && Number.isFinite(dLon)) {
activeStraightLeg = [start, [dLat, dLon]];
}
}
const aerialPath = isAllActiveView ? (activeStraightLeg || []) : buildTripPoints(sorted);
const isKitchenAerial = (viewMode === 'kitchens' || focusedKitchen);
const path = roadPath || aerialPath;
@@ -3997,177 +4014,177 @@ const Dispatch = ({
})
: tOrders;
return (
<div key={tNum} className="trip-block">
<div className="trip-header" style={{ background: `${focusedRider.color}12`, borderColor: `${focusedRider.color}30` }}>
<span className="th-badge" style={{ background: focusedRider.color }}>Trip {tNum}</span>
<span className="trip-stats">
<span><Ico><MdLocationOn /></Ico>{tOrders.length} stops</span>
<span><Ico><MdStraighten /></Ico>{tOrders.reduce((s, o) => s + parseFloat(o.actualkms || o.kms || 0), 0).toFixed(1)} km</span>
</span>
{/* iOS-style segmented control: Planned (dispatched step
<div key={tNum} className="trip-block">
<div className="trip-header" style={{ background: `${focusedRider.color}12`, borderColor: `${focusedRider.color}30` }}>
<span className="th-badge" style={{ background: focusedRider.color }}>Trip {tNum}</span>
<span className="trip-stats">
<span><Ico><MdLocationOn /></Ico>{tOrders.length} stops</span>
<span><Ico><MdStraighten /></Ico>{tOrders.reduce((s, o) => s + parseFloat(o.actualkms || o.kms || 0), 0).toFixed(1)} km</span>
</span>
{/* iOS-style segmented control: Planned (dispatched step
order) vs By time (completion order). The active item is
a white "thumb"; the rider color stays on the Trip badge
so the pills don't compete with it. */}
<div
className="trip-sort-toggle"
role="group"
aria-label="Sort stops by"
data-mode={isTimeMode ? 'time' : 'planned'}
>
<button
type="button"
className={`trip-sort-pill ${!isTimeMode ? 'is-active' : ''}`}
aria-pressed={!isTimeMode}
onClick={() => setTripSortMode('planned')}
title="Sort stops by planned step (dispatched order)"
<div
className="trip-sort-toggle"
role="group"
aria-label="Sort stops by"
data-mode={isTimeMode ? 'time' : 'planned'}
>
<MdFormatListBulleted aria-hidden="true" />
<span>Planned</span>
</button>
<button
type="button"
className={`trip-sort-pill ${isTimeMode ? 'is-active' : ''}`}
aria-pressed={isTimeMode}
onClick={() => setTripSortMode('time')}
title="Sort stops by completion time (which delivery was done first)"
>
<MdAccessTime aria-hidden="true" />
<span>By time</span>
</button>
<button
type="button"
className={`trip-sort-pill ${!isTimeMode ? 'is-active' : ''}`}
aria-pressed={!isTimeMode}
onClick={() => setTripSortMode('planned')}
title="Sort stops by planned step (dispatched order)"
>
<MdFormatListBulleted aria-hidden="true" />
<span>Planned</span>
</button>
<button
type="button"
className={`trip-sort-pill ${isTimeMode ? 'is-active' : ''}`}
aria-pressed={isTimeMode}
onClick={() => setTripSortMode('time')}
title="Sort stops by completion time (which delivery was done first)"
>
<MdAccessTime aria-hidden="true" />
<span>By time</span>
</button>
</div>
</div>
<div className="zone-order-grid">
{displayOrders.map((o, idx) => {
const kitchenKey = (o.kitchen_key || o.pickupcustomer || 'Unknown').toLowerCase().trim();
const showTransition = prevKitchenKey !== null && kitchenKey !== prevKitchenKey;
prevKitchenKey = kitchenKey;
const isStopActive = focusedStop && focusedStop.orderid === o.orderid;
const isGoingOn = activeOrderId && o.orderid === activeOrderId;
const lat = parseFloat(o.droplat || o.deliverylat);
const lon = parseFloat(o.droplon || o.deliverylong);
const canFocus = Number.isFinite(lat) && Number.isFinite(lon);
const statusStyle = getStatusStyle(o.orderstatus);
const estMeters = calculateEstMeters(focusedRider.id, o);
// In "By time" mode, stops with no actual delivery time
// were sunk to the bottom — dim them so it's obvious
// they're not yet delivered without reading the status.
const isUndeliveredInTimeMode = isTimeMode && !o.deliverytime;
return (
<React.Fragment key={o.orderid}>
{showTransition && (
<div className="kitchen-transition"><span className="kt-ico"><MdSwapHoriz /></span> Switch to <strong>{o.pickupcustomer}</strong></div>
)}
<div
className={`zone-order-card ${canFocus ? 'clickable' : ''} ${isStopActive ? 'active' : ''} ${isGoingOn ? 'going-on' : ''} ${isUndeliveredInTimeMode ? 'is-pending-time' : ''}`}
role={canFocus ? 'button' : undefined}
tabIndex={canFocus ? 0 : undefined}
onClick={canFocus ? () => setFocusedStop(isStopActive ? null : { orderid: o.orderid, lat, lon }) : undefined}
onKeyDown={canFocus ? (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setFocusedStop(isStopActive ? null : { orderid: o.orderid, lat, lon });
}
} : undefined}
title={canFocus ? (isStopActive ? 'Click to show full trip' : `Show ${o.deliverycustomer || `order #${o.orderid}`} on map`) : undefined}
>
<div className="zone-order-card-head">
<div className="zone-order-num">{o.step || idx + 1}</div>
<div className="zone-order-id-block">
<div className="zone-order-id">Order #{o.orderid}</div>
</div>
{(() => {
// Stack the status pill and delivery time vertically on
// the right of the header so the operator sees the order
// outcome and the wall-clock time it landed at a glance.
const actual = formatTimeOnly(o.deliverytime);
const expected = formatTimeOnly(o.expecteddeliverytime);
const isDelivered = FINAL_STATUSES.has(String(o.orderstatus || '').toLowerCase());
const showEstDrop = !isDelivered && estMeters !== null;
if (!o.orderstatus && !actual && !expected && !showEstDrop) return null;
return (
<div className="zone-order-status-stack">
{o.orderstatus && (
<span
className="zone-order-status"
style={{ background: statusStyle.bg, color: statusStyle.fg }}
>
{statusStyle.label}
</span>
)}
{(actual || expected) && (
<span
className={`zone-order-time ${actual ? '' : 'is-expected'}`}
title={actual ? `Delivered at ${actual}` : `Expected at ${expected}`}
>
<MdAccessTime />{actual || expected}
</span>
)}
{showEstDrop && (
<span
className="zone-order-est-drop"
title="Estimated distance to drop location"
>
<MdMyLocation />{formatMeters(estMeters)}
</span>
)}
</div>
);
})()}
{onChangeRider && (
<button
type="button"
className="zone-order-change-rider"
title="Change rider"
onClick={(e) => {
e.stopPropagation();
onChangeRider(o, focusedRider);
}}
>
<MdSwapHoriz />
</button>
)}
</div>
<div className="zone-order-customer">
<Ico><MdMarkunreadMailbox /></Ico>{o.deliverycustomer || ''}
</div>
{o.pickupcustomer && (
<div className="zone-order-line" title={`Kitchen: ${o.pickupcustomer}`}>
<Ico><MdRestaurant /></Ico>{o.pickupcustomer}
</div>
)}
{(o.deliverysuburb || o.deliveryaddress) && (
<div className="zone-order-line" title={o.deliveryaddress || o.deliverysuburb}>
<Ico><MdLocationOn /></Ico>{o.deliverysuburb || extractArea(o.deliveryaddress)}
</div>
)}
{o.ordernotes && (
<div className="zone-order-line zone-order-notes" title={o.ordernotes}>
<Ico><MdNotes /></Ico>{o.ordernotes}
</div>
)}
<div className="zone-order-stats">
<span className="zone-order-chip" title="Distance">
<Ico><MdStraighten /></Ico>{o.actualkms || o.kms || 0} km
</span>
{o.ordertype && (
<span className={`zone-order-chip zone-order-type type-${String(o.ordertype).toLowerCase()}`}>
{o.ordertype}
</span>
)}
<span className="zone-order-chip zone-order-trip">
T{o.trip_number || tNum} · S{o.step || idx + 1}
</span>
</div>
</div>
</React.Fragment>
);
})}
</div>
</div>
<div className="zone-order-grid">
{displayOrders.map((o, idx) => {
const kitchenKey = (o.kitchen_key || o.pickupcustomer || 'Unknown').toLowerCase().trim();
const showTransition = prevKitchenKey !== null && kitchenKey !== prevKitchenKey;
prevKitchenKey = kitchenKey;
const isStopActive = focusedStop && focusedStop.orderid === o.orderid;
const isGoingOn = activeOrderId && o.orderid === activeOrderId;
const lat = parseFloat(o.droplat || o.deliverylat);
const lon = parseFloat(o.droplon || o.deliverylong);
const canFocus = Number.isFinite(lat) && Number.isFinite(lon);
const statusStyle = getStatusStyle(o.orderstatus);
const estMeters = calculateEstMeters(focusedRider.id, o);
// In "By time" mode, stops with no actual delivery time
// were sunk to the bottom — dim them so it's obvious
// they're not yet delivered without reading the status.
const isUndeliveredInTimeMode = isTimeMode && !o.deliverytime;
return (
<React.Fragment key={o.orderid}>
{showTransition && (
<div className="kitchen-transition"><span className="kt-ico"><MdSwapHoriz /></span> Switch to <strong>{o.pickupcustomer}</strong></div>
)}
<div
className={`zone-order-card ${canFocus ? 'clickable' : ''} ${isStopActive ? 'active' : ''} ${isGoingOn ? 'going-on' : ''} ${isUndeliveredInTimeMode ? 'is-pending-time' : ''}`}
role={canFocus ? 'button' : undefined}
tabIndex={canFocus ? 0 : undefined}
onClick={canFocus ? () => setFocusedStop(isStopActive ? null : { orderid: o.orderid, lat, lon }) : undefined}
onKeyDown={canFocus ? (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setFocusedStop(isStopActive ? null : { orderid: o.orderid, lat, lon });
}
} : undefined}
title={canFocus ? (isStopActive ? 'Click to show full trip' : `Show ${o.deliverycustomer || `order #${o.orderid}`} on map`) : undefined}
>
<div className="zone-order-card-head">
<div className="zone-order-num">{o.step || idx + 1}</div>
<div className="zone-order-id-block">
<div className="zone-order-id">Order #{o.orderid}</div>
</div>
{(() => {
// Stack the status pill and delivery time vertically on
// the right of the header so the operator sees the order
// outcome and the wall-clock time it landed at a glance.
const actual = formatTimeOnly(o.deliverytime);
const expected = formatTimeOnly(o.expecteddeliverytime);
const isDelivered = FINAL_STATUSES.has(String(o.orderstatus || '').toLowerCase());
const showEstDrop = !isDelivered && estMeters !== null;
if (!o.orderstatus && !actual && !expected && !showEstDrop) return null;
return (
<div className="zone-order-status-stack">
{o.orderstatus && (
<span
className="zone-order-status"
style={{ background: statusStyle.bg, color: statusStyle.fg }}
>
{statusStyle.label}
</span>
)}
{(actual || expected) && (
<span
className={`zone-order-time ${actual ? '' : 'is-expected'}`}
title={actual ? `Delivered at ${actual}` : `Expected at ${expected}`}
>
<MdAccessTime />{actual || expected}
</span>
)}
{showEstDrop && (
<span
className="zone-order-est-drop"
title="Estimated distance to drop location"
>
<MdMyLocation />{formatMeters(estMeters)}
</span>
)}
</div>
);
})()}
{onChangeRider && (
<button
type="button"
className="zone-order-change-rider"
title="Change rider"
onClick={(e) => {
e.stopPropagation();
onChangeRider(o, focusedRider);
}}
>
<MdSwapHoriz />
</button>
)}
</div>
<div className="zone-order-customer">
<Ico><MdMarkunreadMailbox /></Ico>{o.deliverycustomer || ''}
</div>
{o.pickupcustomer && (
<div className="zone-order-line" title={`Kitchen: ${o.pickupcustomer}`}>
<Ico><MdRestaurant /></Ico>{o.pickupcustomer}
</div>
)}
{(o.deliverysuburb || o.deliveryaddress) && (
<div className="zone-order-line" title={o.deliveryaddress || o.deliverysuburb}>
<Ico><MdLocationOn /></Ico>{o.deliverysuburb || extractArea(o.deliveryaddress)}
</div>
)}
{o.ordernotes && (
<div className="zone-order-line zone-order-notes" title={o.ordernotes}>
<Ico><MdNotes /></Ico>{o.ordernotes}
</div>
)}
<div className="zone-order-stats">
<span className="zone-order-chip" title="Distance">
<Ico><MdStraighten /></Ico>{o.actualkms || o.kms || 0} km
</span>
{o.ordertype && (
<span className={`zone-order-chip zone-order-type type-${String(o.ordertype).toLowerCase()}`}>
{o.ordertype}
</span>
)}
<span className="zone-order-chip zone-order-trip">
T{o.trip_number || tNum} · S{o.step || idx + 1}
</span>
</div>
</div>
</React.Fragment>
);
})}
</div>
</div>
);
});
})()}