// ============================================================================ // Canonical lifecycle-status registry. // // Five pages (deliveries, orders, ordersDetails, Tenants, and the dispatch // panels) each carried their own private `STATUS_META` map. They agreed on the // hexes but disagreed on everything else — orders.js keys off the raw // `GET /admin/bookings` enum (`pending_pickup`, `miler_assigned`, …) while // deliveries.js keys off api.js's generic `pending`/`accepted`/`delivered` // mapping, so the same order rendered under two different labels depending on // which page you were looking at. // // This module is the one place that knows what a status LOOKS like. It does // not know what a status MEANS to a given page — the pending/accepted bucket // rules stay in each page's query layer, because those genuinely differ (see // the long comment above orders.js's STATUS_TABS). // // Each entry carries four renderings of the same state so a page never has to // pick a colour by hand: // color — hex, for the surfaces that take a raw accent (StatCard, // AccentAvatar, chart series). Matches CLAUDE.md's status palette. // badge — Astryx . Non-semantic tinted variants on purpose: // per Astryx's Badge guidance the solid semantic variants // (success/warning/error) are for states that demand attention, and // a table where every row shouts is a table where nothing does. // `cancelled` is the one state that keeps a loud variant. // dot — Astryx , which only has five values, so // several lifecycle states collapse onto `accent` here. // icon — react-icons component (not an element) so callers size it. // hint — a two-or-three word definition of the state, for surfaces that // show the name and its meaning together (SegmentCard's `range`). // Deliberately terse: it sits beside the label, not under it. // ============================================================================ import { MdHourglassEmpty, MdPersonPin, MdLocationOn, MdInventory2, MdRoute, MdSkipNext, MdCheckCircle, MdCancel, MdList, MdHelpOutline } from 'react-icons/md'; export const STATUS_META = { all: { label: 'All', hint: 'every status', color: '#000000', badge: 'neutral', dot: 'neutral', icon: MdList }, pending: { label: 'Pending', hint: 'awaiting rider', color: '#f59e0b', badge: 'yellow', dot: 'warning', icon: MdHourglassEmpty }, accepted: { label: 'Accepted', hint: 'rider assigned', color: '#6366f1', badge: 'blue', dot: 'accent', icon: MdPersonPin }, arrived: { label: 'Arrived', hint: 'at pickup', color: '#06b6d4', badge: 'cyan', dot: 'accent', icon: MdLocationOn }, picked: { label: 'Picked', hint: 'parcel collected', color: '#8b5cf6', badge: 'purple', dot: 'accent', icon: MdInventory2 }, active: { label: 'Active', hint: 'in transit', color: '#14b8a6', badge: 'teal', dot: 'success', icon: MdRoute }, skipped: { label: 'Skipped', hint: 'attempt failed', color: '#f97316', badge: 'orange', dot: 'warning', icon: MdSkipNext }, delivered: { label: 'Delivered', hint: 'completed', color: '#10b981', badge: 'green', dot: 'success', icon: MdCheckCircle }, cancelled: { label: 'Cancelled', hint: 'not fulfilled', color: '#ef4444', badge: 'error', dot: 'error', icon: MdCancel }, inactive: { label: 'Inactive', hint: 'not in service', color: '#ef4444', badge: 'red', dot: 'error', icon: MdCancel } }; // Raw backend enums that render as one of the states above. Kept separate from // STATUS_META so the canonical list stays readable and so a page can still ask // "is this a known alias?" rather than silently falling back. // // The booking enums come from `GET /admin/bookings` (confirmed live — see // orders.js). `converted_to_consignment` stays `accepted` HERE and only here: // this registry renders the badge on the Orders page, whose operator workflow // treats everything before hand-off as assigned. // // The DELIVERIES page classifies the same status as `picked`, in // `mapBookingStatusToDeliveryStatus` (api.js) — deliberately, and the two are // not in conflict. Deliveries tracks the rider's engagement, and // doormile-flow.md §5 is explicit that `pickup-complete` is what converts a // booking into a consignment, so on that page the parcel is in the rider's // hands. Orders tracks the operator's action, where it is still just assigned. // Same booking, two questions, two answers. Don't "fix" one to match the other // without re-reading root CLAUDE.md on why the two taxonomies exist. export const STATUS_ALIASES = { pending_pickup: 'pending', pending_assignment: 'pending', miler_assigned: 'accepted', pickup_scheduled: 'accepted', converted_to_consignment: 'accepted', // `Out_for_Delivery` is a confirmed live booking status (named in // express-console-api.md's CityGate note, and mapped to 'active' by // api.js's BOOKING_STATUS_TO_DELIVERY_STATUS). Without this entry it fell // through to the neutral "unknown" badge showing the raw enum string. out_for_delivery: 'active', in_transit: 'active', intransit: 'active', completed: 'delivered', cancel: 'cancelled', canceled: 'cancelled' }; // Fallback for a status the backend invents that nobody has mapped yet. Renders // as a neutral badge with the raw string as its label, so an unknown state is // visible and debuggable rather than blank. const unknownStatus = (status) => ({ label: String(status || 'Unknown'), color: '#94a3b8', badge: 'neutral', dot: 'neutral', icon: MdHelpOutline }); // Resolve any status string — canonical key, backend alias, or arbitrary // casing — to its visual meta. Always returns an object; never throws. export function getStatusMeta(status) { if (!status) return unknownStatus(status); const raw = String(status).trim(); const key = raw.toLowerCase(); return STATUS_META[key] || STATUS_META[STATUS_ALIASES[raw]] || STATUS_META[STATUS_ALIASES[key]] || unknownStatus(raw); }