diff --git a/README.md b/README.md index fe216c9..f14da73 100644 --- a/README.md +++ b/README.md @@ -151,21 +151,34 @@ plan against their GPS trail, and what the day earned. Per-stop actual distance the trail by each stop's share of the plan — the feed carries no per-stop odometer, so it is an estimate and the panel says so. +## Status Lifecycle & Backend Status Pairs + +The platform manages two distinct layers of state: the initial **PickupBooking** (before pickup) and the subsequent **Consignment** (after pickup). `Converted_To_Consignment` acts solely as an internal backend handoff marker and is **never shown literally** in the UI. + +### Status Derivation Matrix + +| Phase | Backend State / Combination | Frontend Display Status | Deliveries Page Tab | Badge Tone | +| :--- | :--- | :--- | :--- | :--- | +| **Booking** | `status = Pending_Pickup` | **Pending** | `Pending` | `neutral` (grey) | +| **Booking** | `status = Miler_Assigned` | **Pending** (Deliveries) / **Assigned** (Orders) | `Pending` | `info` (blue) | +| **Booking** | `status = Pickup_Scheduled` + `reachedat == null` | **Accepted** | `Accepted` | `info` (blue) | +| **Booking** | `status = Pickup_Scheduled` + `reachedat != null` | **Arrived** | `Arrived` | `info` (cyan) | +| **Handoff** | `status = Converted_To_Consignment` | *(Switches to `consignmentstatus`)* | — | — | +| **Consignment** | `consignmentstatus = Collected_By_Miler` | **Picked** | `Picked` | `accent` (purple) | +| **Consignment** | `consignmentstatus = Out_for_Delivery` | **Active / Out for Delivery** | `Active` | `warning` (teal/yellow) | +| **Consignment** | `consignmentstatus = Inwarded_At_Hub` / `At_Hub` | **Inwarded At Hub** | `Active` | `info` (blue) | +| **Consignment** | `consignmentstatus = Outwarded_From_Hub` | **Outwarded From Hub** | `Active` | `warning` (teal) | +| **Consignment** | `consignmentstatus = In_Transit_To_Hub` | **In Transit To Hub** | `Active` | `warning` (teal) | +| **Consignment** | `consignmentstatus = Delivered` | **Delivered** | `Delivered` | `success` (green) | +| **Consignment** | `consignmentstatus = Cancelled` / `Returned` / `Failed` | **Cancelled** | `Cancelled` | `destructive` (red) | +| **Consignment** | `consignmentstatus = Skipped` / `RTO` | **Skipped** | `Skipped` | `destructive` (red) | + ## Things that look wrong but are not -- **A delivery can read "Active" while the booking says `Converted_To_Consignment`.** A booking's - status freezes the moment it is picked up; the lifecycle continues on the consignment. The - Deliveries table lets the consignment's status win and labels the row so you can tell where the - value came from. -- **The Picked tab is empty on hyperlocal traffic.** A matching 3-digit pickup/delivery pincode - prefix goes straight to `Out_for_Delivery` at pickup, so those parcels never rest in "picked". -- **Update Status refuses half its own options.** The write is consignment-scoped, and Pending, - Accepted, Arrived, Picked and Skipped describe states a consignment cannot be in. Those are - refused with the reason rather than guessed at. -- **Assigned means different things on Orders and Deliveries.** Orders tracks the *operator's* - workflow, so assigning a rider counts as Assigned. Deliveries tracks the *rider's* engagement, so - it waits for the rider to accept. This is deliberate, and the two taxonomies live in - `orderStatusGroups.js` and `queries.js` respectively. +- **`Converted_To_Consignment` is a handoff marker, not a display state.** A booking's status freezes the moment it is picked up; the lifecycle continues on the consignment. The frontend automatically reads `consignmentstatus` from that point on. +- **The Picked tab is empty on hyperlocal traffic when the collection flag is off.** A matching 3-digit pickup/delivery pincode prefix goes straight to `Out_for_Delivery` at pickup, so those parcels transition directly from Arrived to Active. +- **Update Status refuses half its own options.** The write is consignment-scoped, and Pending, Accepted, Arrived, Picked and Skipped describe states a consignment cannot be in. Those are refused with the reason rather than guessed at. +- **Assigned means different things on Orders and Deliveries.** Orders tracks the *operator's* workflow, so assigning a rider counts as Assigned. Deliveries tracks the *rider's* engagement, so it waits for the rider to accept. This is deliberate, and the two taxonomies live in `orderStatusGroups.js` and `queries.js` respectively. ## Batches, dates and the one rule about time diff --git a/src/api/doormile/queries.js b/src/api/doormile/queries.js index 19ade80..8c12922 100644 --- a/src/api/doormile/queries.js +++ b/src/api/doormile/queries.js @@ -91,12 +91,20 @@ const BOOKING_STATUS_TO_DELIVERY_STATUS = { out_for_delivery: 'active', in_transit: 'active', intransit: 'active', + inwarded_at_hub: 'active', + inwarded: 'active', + at_hub: 'active', + outwarded_from_hub: 'active', + in_transit_to_hub: 'active', active: 'active', delivered: 'delivered', completed: 'delivered', cancelled: 'cancelled', canceled: 'cancelled', - skipped: 'skipped' + skipped: 'skipped', + rto: 'skipped', + returned: 'cancelled', + failed: 'cancelled' }; // Exported (not just module-local) so anything else that needs to classify a @@ -104,34 +112,69 @@ const BOOKING_STATUS_TO_DELIVERY_STATUS = { // reuses this instead of growing a second copy of BOOKING_STATUS_TO_DELIVERY_STATUS // that can drift from it. // Supports both (status, reachedAt) and a booking/consignment row object. -export const mapBookingStatusToDeliveryStatus = (statusOrRow, maybeReachedAt) => { - if (statusOrRow && typeof statusOrRow === 'object') { - const b = statusOrRow; - const rawConsignmentStatus = b.consignmentstatus ?? b.consignment_status; - if (rawConsignmentStatus) { - return mapBookingStatusToDeliveryStatus(rawConsignmentStatus); +export const deriveDeliveryStatus = (bookingOrStatus, consignmentMapOrReachedAt) => { + if (!bookingOrStatus) return 'pending'; + + // If passed a raw status string + if (typeof bookingOrStatus === 'string') { + const raw = bookingOrStatus.trim().toLowerCase(); + if (raw === 'pickup_scheduled') { + const isReached = + consignmentMapOrReachedAt != null && + consignmentMapOrReachedAt !== false && + consignmentMapOrReachedAt !== ''; + return isReached ? 'arrived' : 'accepted'; } - const reached = b.reachedat ?? b.reached_at ?? b.reachedAt ?? b.reachedtime ?? b.reached_time; - const rawStatus = b.status ?? b.orderstatus ?? b.bookingstatus; - return mapBookingStatusToDeliveryStatus(rawStatus, reached); + return BOOKING_STATUS_TO_DELIVERY_STATUS[raw] || raw; } - const raw = String(statusOrRow || '').trim().toLowerCase(); - if (raw === 'pickup_scheduled') { - const isReached = maybeReachedAt != null && maybeReachedAt !== false && maybeReachedAt !== ''; + // If passed a booking row object + const b = bookingOrStatus; + const rawBookingStatus = String(b.status ?? b.bookingstatus ?? b.orderstatus ?? '').trim().toLowerCase(); + const cStatus = consignmentStatusFor(b, typeof consignmentMapOrReachedAt === 'object' && consignmentMapOrReachedAt instanceof Map ? consignmentMapOrReachedAt : undefined); + const reached = b.reachedat ?? b.reached_at ?? b.reachedAt ?? b.reachedtime ?? b.reached_time; + + // If booking is Converted_To_Consignment, switch directly to consignmentstatus: + // Converted_To_Consignment is a handoff marker, not a display state. + if (rawBookingStatus === 'converted_to_consignment') { + if (cStatus) { + return mapBookingStatusToDeliveryStatus(cStatus); + } + return 'picked'; + } + + // If consignment status is present, it wins: + if (cStatus) { + return mapBookingStatusToDeliveryStatus(cStatus); + } + + // Before pickup (booking lifecycle): + if (rawBookingStatus === 'pickup_scheduled') { + const isReached = reached != null && reached !== false && reached !== ''; return isReached ? 'arrived' : 'accepted'; } - return BOOKING_STATUS_TO_DELIVERY_STATUS[raw] || raw; + + return mapBookingStatusToDeliveryStatus(rawBookingStatus, reached); +}; + +export const mapBookingStatusToDeliveryStatus = (statusOrRow, maybeReachedAt) => { + return deriveDeliveryStatus(statusOrRow, maybeReachedAt); }; // A consignment's own status, when this booking has one and it looks like a -// status. `GET /admin/consignments` has no documented response schema, so this -// reads defensively: only a non-empty string on one of the plausible field -// names counts, and anything else returns undefined so the caller keeps using -// the booking's status. +// status. GetAdminBookings attaches consignmentstatus directly to each row, +// and GET /admin/consignments can also be joined. const consignmentStatusFor = (booking, consignmentMap) => { - if (!booking?.consignmentid || !consignmentMap?.size) return undefined; - const record = consignmentMap.get(String(booking.consignmentid)); + // 1. Direct field attached to booking row by GetAdminBookings + const attached = booking?.consignmentstatus ?? booking?.consignment_status; + if (typeof attached === 'string' && attached.trim()) { + return attached.trim(); + } + + // 2. Lookup in joined consignmentMap if available + const consignmentId = booking?.consignmentid ?? booking?.consignment_id; + if (!consignmentId || !consignmentMap?.size) return undefined; + const record = consignmentMap.get(String(consignmentId)); if (!record) return undefined; const raw = record.status ?? record.consignmentstatus ?? record.currentstatus ?? record.consignment_status; return typeof raw === 'string' && raw.trim() ? raw.trim() : undefined; @@ -648,97 +691,113 @@ export const fetchDeliveries = async ({ pageParam = 1, queryKey }) => { const milerMap = new Map((milers || []).map((m) => [m.userid ?? m.milerid, m])); const tenantMap = new Map((tenants || []).map((t) => [t.tenantid, t])); - const dispatched = (bookings || []).filter((b) => b.assignedmileruserid || b.consignmentid); + const isDispatched = (b) => { + if (!b) return false; + const riderId = b.assignedmileruserid ?? b.assigned_miler_user_id ?? b.mileruserid ?? b.milerid ?? b.assignedmilerid; + const consignmentId = b.consignmentid ?? b.consignment_id; + const rawStatus = String(b.status || b.orderstatus || '').trim().toLowerCase(); + if (riderId || consignmentId) return true; + if (rawStatus && !['pending_pickup', 'created', 'new', 'booked', 'order_placed', 'unassigned', ''].includes(rawStatus)) { + return true; + } + return false; + }; + + const dispatched = (bookings || []).filter(isDispatched); const rows = dispatched.map((b) => { - const customer = customerMap.get(b.appcustomerid); - const miler = milerMap.get(b.assignedmileruserid); + const riderId = b.assignedmileruserid ?? b.assigned_miler_user_id ?? b.mileruserid ?? b.milerid ?? b.assignedmilerid; + const consignmentId = b.consignmentid ?? b.consignment_id; + const customerId = b.appcustomerid ?? b.customerid ?? b.id; + const customer = customerMap.get(customerId); + const miler = milerMap.get(riderId) || (riderId != null ? milerMap.get(Number(riderId)) : null); const tenant = tenantMap.get(b.tenantid); const charge = b.serviceoptions?.[0]?.estimatedprice; - const cStatus = consignmentStatusFor(b, consignmentMap); - const reached = b.reachedat ?? b.reached_at ?? b.reachedAt ?? b.reachedtime ?? b.reached_time; - const effectiveDeliveryStatus = cStatus - ? mapBookingStatusToDeliveryStatus(cStatus) - : mapBookingStatusToDeliveryStatus(b.status, reached); + const cStatus = consignmentStatusFor(b, consignmentMap); + const reached = b.reachedat ?? b.reached_at ?? b.reachedAt ?? b.reachedtime ?? b.reached_time; + const effectiveDeliveryStatus = deriveDeliveryStatus(b, consignmentMap); - return { - orderheaderid: b.bookingid, - deliveryid: b.bookingid, - orderid: b.bookingno || `#${b.bookingid}`, - consignmentid: b.consignmentid, - tenantid: b.tenantid, - tenantname: tenant?.tenantname || '', - tenantsuburb: '', - applocation: '', - tenantadress: tenant?.primaryemail || '', - locationname: tenant?.tenantname || '', - locationsuburb: '', - // Was hardcoded '' — Dispatch.js's kitchen markers read this as the - // pickup business name (`o.pickupcustomer || o.kitchen_key || 'Unknown'`, - // Dispatch.js:1660), and no booking on this API carries a `kitchen_key` - // field at all, so every kitchen pin fell through to the literal string - // 'Unknown' — rendered as a "U" marker whose hover/popup then showed - // "Unknown". The tenant IS the kitchen for a B2B booking (same value - // already used for tenantname/locationname above), so reuse it here. - pickupcustomer: tenant?.tenantname || '', - pickupcontactno: '', - Pickupaddress: b.pickupaddress || '', - pickupaddress: b.pickupaddress || '', - pickuplocation: b.pickupaddress || '', - pickupsuburb: '', - deliverycustomer: customer?.firstname || customer?.name || (b.appcustomerid ? `Customer #${b.appcustomerid}` : ''), - deliverycontactno: customer?.phone || customer?.contactno || '', - deliveryaddress: b.deliveryaddress || '', - deliverylocation: b.deliveryaddress || '', - deliverysuburb: '', - ridername: miler?.displayname || (b.assignedmileruserid ? `Rider #${b.assignedmileruserid}` : ''), - userid: b.assignedmileruserid, - // GET /admin/milers/:id/notify (and block/assign-vehicle) key off - // milerprofileid, not the userid stored on the booking — confirmed - // live (a booking's assignedmileruserid matches a miler's `userid` - // field, which 404s against /admin/milers/:id; milerprofileid is the - // real primary key of that resource). - milerprofileid: miler?.milerprofileid, - ridercontact: miler?.phone || '', - expecteddeliverytime: b.serviceoptions?.[0]?.estimateddeliveryat, - // No route-plan data source (step order/transit time/cumulative km were - // computed by the old jupiter backend from the dispatch optimiser's - // output, not stored on a booking/consignment) — left undefined so the - // UI's own "—" fallbacks render instead of a fabricated number. - transitminutes: undefined, - cumulativekms: undefined, - step: undefined, - // No road-distance field on a booking — approximated as a straight - // line between pickup and delivery coordinates. - kms: haversineKm(b.pickuplatitude, b.pickuplongitude, b.deliverylatitude, b.deliverylongitude), - pickuplatitude: b.pickuplatitude, - pickuplongitude: b.pickuplongitude, - deliverylatitude: b.deliverylatitude, - deliverylongitude: b.deliverylongitude, - deliverycharges: charge, - deliveryamt: charge, - deliveryamount: charge, - Quantity: b.parcels?.length || 0, - quantity: b.parcels?.length || 0, - collectionamt: undefined, - notes: b.notes || '', - deliverytype: customer ? 'B' : 'C', - orderdate: b.createdat, - deliverydate: b.serviceoptions?.[0]?.estimateddeliveryat || b.updatedat, - reachedat: reached, - assigntime: b.updatedat, - // The consignment's status WINS when there is one (e.g. Collected_By_Miler -> Picked, - // Out_for_Delivery -> Active, Delivered -> Delivered). - // Otherwise derives from booking status + reachedat: - // Pickup_Scheduled + reachedat present -> Arrived - // Pickup_Scheduled + reachedat absent -> Accepted - orderstatus: effectiveDeliveryStatus, - consignmentstatus: cStatus, - statusfromconsignment: cStatus != null, - droplat: b.deliverylatitude, - droplon: b.deliverylongitude - }; - }); + return { + orderheaderid: b.bookingid ?? b.id, + deliveryid: b.bookingid ?? b.id, + bookingid: b.bookingid ?? b.id, + bookingno: b.bookingno, + orderid: b.bookingno || (b.bookingid ? `#${b.bookingid}` : (b.id ? `#${b.id}` : '')), + consignmentid: consignmentId, + tenantid: b.tenantid, + tenantname: tenant?.tenantname || '', + tenantsuburb: '', + applocation: '', + tenantadress: tenant?.primaryemail || '', + locationname: tenant?.tenantname || '', + locationsuburb: '', + // Was hardcoded '' — Dispatch.js's kitchen markers read this as the + // pickup business name (`o.pickupcustomer || o.kitchen_key || 'Unknown'`, + // Dispatch.js:1660), and no booking on this API carries a `kitchen_key` + // field at all, so every kitchen pin fell through to the literal string + // 'Unknown' — rendered as a "U" marker whose hover/popup then showed + // "Unknown". The tenant IS the kitchen for a B2B booking (same value + // already used for tenantname/locationname above), so reuse it here. + pickupcustomer: tenant?.tenantname || '', + pickupcontactno: '', + Pickupaddress: b.pickupaddress || '', + pickupaddress: b.pickupaddress || '', + pickuplocation: b.pickupaddress || '', + pickupsuburb: '', + deliverycustomer: customer?.firstname || customer?.name || (customerId ? `Customer #${customerId}` : ''), + deliverycontactno: customer?.phone || customer?.contactno || '', + deliveryaddress: b.deliveryaddress || '', + deliverylocation: b.deliveryaddress || '', + deliverysuburb: '', + ridername: miler?.displayname || miler?.name || (riderId ? `Rider #${riderId}` : ''), + userid: riderId, + // GET /admin/milers/:id/notify (and block/assign-vehicle) key off + // milerprofileid, not the userid stored on the booking — confirmed + // live (a booking's assignedmileruserid matches a miler's `userid` + // field, which 404s against /admin/milers/:id; milerprofileid is the + // real primary key of that resource). + milerprofileid: miler?.milerprofileid ?? miler?.id, + ridercontact: miler?.phone || miler?.contactno || '', + expecteddeliverytime: b.serviceoptions?.[0]?.estimateddeliveryat, + // No route-plan data source (step order/transit time/cumulative km were + // computed by the old jupiter backend from the dispatch optimiser's + // output, not stored on a booking/consignment) — left undefined so the + // UI's own "—" fallbacks render instead of a fabricated number. + transitminutes: undefined, + cumulativekms: undefined, + step: undefined, + // No road-distance field on a booking — approximated as a straight + // line between pickup and delivery coordinates. + kms: haversineKm(b.pickuplatitude, b.pickuplongitude, b.deliverylatitude, b.deliverylongitude), + pickuplatitude: b.pickuplatitude, + pickuplongitude: b.pickuplongitude, + deliverylatitude: b.deliverylatitude, + deliverylongitude: b.deliverylongitude, + deliverycharges: charge, + deliveryamt: charge, + deliveryamount: charge, + Quantity: b.parcels?.length || 0, + quantity: b.parcels?.length || 0, + collectionamt: undefined, + notes: b.notes || '', + deliverytype: customer ? 'B' : 'C', + orderdate: b.createdat, + deliverydate: b.serviceoptions?.[0]?.estimateddeliveryat || b.updatedat, + reachedat: reached, + assigntime: b.updatedat, + // The consignment's status WINS when there is one (e.g. Collected_By_Miler -> Picked, + // Out_for_Delivery -> Active, Delivered -> Delivered). + // If status == Converted_To_Consignment, switches to consignmentstatus. + // Otherwise derives from booking status + reachedat: + // Pickup_Scheduled + reachedat present -> Arrived + // Pickup_Scheduled + reachedat absent -> Accepted + orderstatus: effectiveDeliveryStatus, + consignmentstatus: cStatus, + statusfromconsignment: cStatus != null || String(b.status || '').toLowerCase() === 'converted_to_consignment', + droplat: b.deliverylatitude, + droplon: b.deliverylongitude + }; + }); // Apply the requested date range to the booking's CREATION day. This has to // agree with what the batch bucketing reads (Dispatch.js's @@ -783,14 +842,21 @@ const getDeliveryStatusCounts = async () => { const id = c?.consignmentid ?? c?.id; if (id != null) consignmentMap.set(String(id), c); }); - const dispatched = (bookings || []).filter((b) => b.assignedmileruserid || b.consignmentid); + const isDispatched = (b) => { + if (!b) return false; + const riderId = b.assignedmileruserid ?? b.assigned_miler_user_id ?? b.mileruserid ?? b.milerid ?? b.assignedmilerid; + const consignmentId = b.consignmentid ?? b.consignment_id; + const rawStatus = String(b.status || b.orderstatus || '').trim().toLowerCase(); + if (riderId || consignmentId) return true; + if (rawStatus && !['pending_pickup', 'created', 'new', 'booked', 'order_placed', 'unassigned', ''].includes(rawStatus)) { + return true; + } + return false; + }; + const dispatched = (bookings || []).filter(isDispatched); const counts = { total: dispatched.length }; dispatched.forEach((b) => { - const cStatus = consignmentStatusFor(b, consignmentMap); - const reached = b.reachedat ?? b.reached_at ?? b.reachedAt ?? b.reachedtime ?? b.reached_time; - const status = cStatus - ? mapBookingStatusToDeliveryStatus(cStatus) - : mapBookingStatusToDeliveryStatus(b.status, reached); + const status = deriveDeliveryStatus(b, consignmentMap); counts[status] = (counts[status] || 0) + 1; }); return counts; diff --git a/src/components/ds/StatusBadge.jsx b/src/components/ds/StatusBadge.jsx index 68ee745..85a449f 100644 --- a/src/components/ds/StatusBadge.jsx +++ b/src/components/ds/StatusBadge.jsx @@ -39,7 +39,11 @@ export const STATUS_MAP = { /* Consignment states that have no booking equivalent. */ in_transit: { label: 'In Transit', tone: 'warning' }, + inwarded_at_hub: { label: 'Inwarded At Hub', tone: 'info' }, + inwarded: { label: 'Inwarded At Hub', tone: 'info' }, at_hub: { label: 'At Hub', tone: 'info' }, + outwarded_from_hub: { label: 'Outwarded From Hub', tone: 'warning' }, + in_transit_to_hub: { label: 'In Transit To Hub', tone: 'warning' }, rto: { label: 'RTO', tone: 'destructive' }, returned: { label: 'Returned', tone: 'destructive' }, failed: { label: 'Failed', tone: 'destructive' }, diff --git a/src/pages/doormile/deliveries/Deliveries.jsx b/src/pages/doormile/deliveries/Deliveries.jsx index e36b31e..df075fb 100644 --- a/src/pages/doormile/deliveries/Deliveries.jsx +++ b/src/pages/doormile/deliveries/Deliveries.jsx @@ -33,6 +33,7 @@ import { currency, exportRows, km as formatKm, matchesQuery, orDash, useDebounce /* `other` is not declared here — it is appended only when rows actually land in it, so the strip never carries a permanently empty tab. */ const STATUS_TABS = [ + { value: 'all', label: 'All', countKey: 'total' }, { value: 'pending', label: 'Pending', countKey: 'uncoveredLength' }, { value: 'accepted', label: 'Accepted', countKey: 'assignedLength' }, { value: 'arrived', label: 'Arrived', countKey: 'arrivedLength' }, @@ -60,26 +61,8 @@ const SETTABLE_STATUSES = [ ]; export default function Deliveries() { - // Unscoped by default. `fetchDeliveries` treats a blank from/to as "no - // bound" (see queries.js — `if (!startdate && !enddate) return true`), and - // it filters on `orderdate` (the booking's creation day), never on when a - // row was last touched. Defaulting this range to today meant the Pending/ - // Accepted/Arrived/Picked/Active tabs — an outstanding-work QUEUE, not a - // day's log — only showed a booking that was also *created* today. - // Reassign an order that came in yesterday and it's still correctly - // "Pending" work, but it dropped off the board the moment you did it, - // reading as "I just assigned it and it's not there." Same "state vs - // flow" distinction this app already draws elsewhere for orders. Delivered/ - // Cancelled stay meaningful to scope by date — the operator narrows via - // the date inputs same as before. const [range, setRange] = useState({ from: '', to: '' }); - const [tab, setTab] = useState('pending'); - // 'all' rather than the wave matching the current clock hour: a batch is a - // slice of ONE day, so "jump to the slot matching right now" only means - // something once the date range above is scoped to a single day. With the - // range unscoped by default, defaulting the batch too would silently hide - // almost everything — a booking created three days ago at 11am has no - // relationship to "the slot matching right now" on a different day. + const [tab, setTab] = useState('all'); const [batch, setBatch] = useState('all'); const [search, setSearch] = useState(''); const debouncedSearch = useDebouncedValue(search); @@ -144,7 +127,10 @@ export default function Deliveries() { const base = STATUS_TABS.map((entry) => ({ value: entry.value, label: entry.label, - count: batchRows.filter((row) => String(row.orderstatus || '').toLowerCase() === entry.value).length, + count: + entry.value === 'all' + ? batchRows.length + : batchRows.filter((row) => String(row.orderstatus || '').toLowerCase() === entry.value).length, })); /* A row whose status matches no tab used to be counted nowhere and shown nowhere. It gets its own tab, but only when one actually exists. */ @@ -154,7 +140,9 @@ export default function Deliveries() { const rows = useMemo(() => { const scoped = - tab === 'other' + tab === 'all' + ? batchRows + : tab === 'other' ? unmapped : batchRows.filter((row) => { const status = String(row.orderstatus || '').toLowerCase(); @@ -164,7 +152,17 @@ export default function Deliveries() { return scoped.filter((row) => matchesQuery( row, - ['orderid', 'deliverycustomer', 'ridername', 'pickupaddress', 'deliveryaddress', 'tenantname'], + [ + 'orderid', + 'bookingno', + 'bookingid', + 'consignmentid', + 'deliverycustomer', + 'ridername', + 'pickupaddress', + 'deliveryaddress', + 'tenantname', + ], debouncedSearch ) ); @@ -252,7 +250,7 @@ export default function Deliveries() { sortable: true, cell: (row) => (