@burdenoff/microfe-movethewheels 2026.924.2 → 2026.925.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/components/OrderItemRow.js +195 -109
  2. package/dist/components/StatusBadge.js +31 -31
  3. package/dist/constants/index.js +11 -3
  4. package/dist/generated/wspace-operations.d.ts +7 -0
  5. package/dist/hooks/useFleet.js +85 -76
  6. package/dist/hooks/useOrders.js +8 -4
  7. package/dist/hooks/useTracking.js +127 -66
  8. package/dist/movethewheels/components/OrderItemRow.d.ts +1 -2
  9. package/dist/movethewheels/components/StatusBadge.d.ts +3 -1
  10. package/dist/movethewheels/constants/index.d.ts +1 -0
  11. package/dist/movethewheels/hooks/useFleet.d.ts +41 -1
  12. package/dist/movethewheels/hooks/useOrders.d.ts +1 -0
  13. package/dist/movethewheels/hooks/useTracking.d.ts +2 -2
  14. package/dist/movethewheels/types/index.d.ts +17 -39
  15. package/dist/node_modules/lucide-react/dist/esm/icons/sticky-note.js +11 -0
  16. package/dist/node_modules/lucide-react/dist/esm/icons/user-x.js +30 -0
  17. package/dist/pages/CreateOrderPage.js +597 -420
  18. package/dist/pages/DriverDetailsPage.js +702 -421
  19. package/dist/pages/DriversPage.js +106 -117
  20. package/dist/pages/FleetPage.js +4 -10
  21. package/dist/pages/OrderDetailsPage.js +290 -262
  22. package/dist/pages/OrdersListPage.js +4 -0
  23. package/dist/pages/RouteDetailsPage.js +264 -252
  24. package/dist/pages/TrackingPage.js +205 -195
  25. package/dist/pages/VehicleDetailsPage.js +403 -243
  26. package/dist/utils/mapMarkers.js +2 -1
  27. package/package.json +1 -1
  28. package/src/generated/wspace-operations.ts +10 -3
  29. package/src/movethewheels/components/OrderItemRow.tsx +162 -99
  30. package/src/movethewheels/components/StatusBadge.tsx +11 -2
  31. package/src/movethewheels/constants/index.ts +13 -0
  32. package/src/movethewheels/hooks/useFleet.ts +163 -44
  33. package/src/movethewheels/hooks/useOrders.ts +17 -3
  34. package/src/movethewheels/hooks/useTracking.ts +208 -27
  35. package/src/movethewheels/pages/CreateOrderPage.tsx +372 -263
  36. package/src/movethewheels/pages/DriverDetailsPage.tsx +516 -156
  37. package/src/movethewheels/pages/DriversPage.tsx +28 -11
  38. package/src/movethewheels/pages/FleetPage.tsx +7 -10
  39. package/src/movethewheels/pages/OrderDetailsPage.tsx +67 -30
  40. package/src/movethewheels/pages/OrdersListPage.tsx +1 -0
  41. package/src/movethewheels/pages/RouteDetailsPage.tsx +15 -7
  42. package/src/movethewheels/pages/TrackingPage.tsx +44 -14
  43. package/src/movethewheels/pages/VehicleDetailsPage.tsx +137 -15
  44. package/src/movethewheels/types/index.ts +17 -38
  45. package/src/movethewheels/utils/mapMarkers.ts +1 -0
  46. package/src/operations/wspace/movethewheels/queries/dashboard.graphql +5 -0
  47. package/src/operations/wspace/movethewheels/queries/logistics.graphql +2 -2
@@ -6,7 +6,8 @@ function e(e) {
6
6
  case "delivered": return "delivered";
7
7
  case "failed":
8
8
  case "delayed":
9
- case "cancelled": return "delayed";
9
+ case "cancelled":
10
+ case "returned": return "delayed";
10
11
  case "assigned":
11
12
  case "picked_up":
12
13
  case "confirmed": return "active";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-movethewheels",
3
- "version": "2026.924.2",
3
+ "version": "2026.925.2",
4
4
  "description": "Burdenoff MoveTheWheels Microfrontend - Logistics & Supply Chain Management Platform",
5
5
  "type": "module",
6
6
  "files": [
@@ -292,7 +292,7 @@ export type DeleteMovethewheelsOrderMutation = { __typename?: 'Mutation', delete
292
292
  export type GetMovethewheelsDashboardQueryVariables = Types.Exact<{ [key: string]: never; }>;
293
293
 
294
294
 
295
- export type GetMovethewheelsDashboardQuery = { __typename?: 'Query', dashboardMetrics: { __typename?: 'MtwDashboardMetrics', totalDeliveryJobs: number, activeDeliveries: number, availableDrivers: number, activeVehicles: number, lowStockItems: number, totalRevenue: number, recentDeliveryJobs: Array<{ __typename?: 'MtwDeliveryJob', id: string, deliveryJobNumber: string, status: Types.DeliveryJobStatus, priority: Types.DeliveryPriority, customerId: string, trackingNumber?: string | null, pickupAddress: Record<string, unknown>, deliveryAddress: Record<string, unknown>, cargoItems: Record<string, unknown>, pricing?: Record<string, unknown> | null, scheduledDelivery?: string | null, vehicleId?: string | null, driverId?: string | null, createdAt: string, updatedAt: string }> }, deliveryJobStats: { __typename?: 'MtwDeliveryJobStats', total: number, pending: number, inTransit: number, delivered: number, cancelled: number, failed: number }, fleetStats: { __typename?: 'MtwFleetStats', totalVehicles: number, activeVehicles: number, inTransitVehicles: number, maintenanceVehicles: number, totalDrivers: number, availableDrivers: number, onDeliveryDrivers: number }, financeMetrics: { __typename?: 'MtwFinanceMetrics', fuelCost: number, openInvoices: number, revenue: { __typename?: 'MtwRevenueMetrics', totalRevenue: number, averageOrderValue: number, deliveredRevenue: number } }, analytics: { __typename?: 'MtwAnalyticsSeries', deliveryTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }>, orderTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }>, revenueTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }> } };
295
+ export type GetMovethewheelsDashboardQuery = { __typename?: 'Query', dashboardMetrics: { __typename?: 'MtwDashboardMetrics', totalDeliveryJobs: number, activeDeliveries: number, availableDrivers: number, activeVehicles: number, lowStockItems: number, totalRevenue: number, recentDeliveryJobs: Array<{ __typename?: 'MtwDeliveryJob', id: string, deliveryJobNumber: string, status: Types.DeliveryJobStatus, priority: Types.DeliveryPriority, customerId: string, trackingNumber?: string | null, pickupAddress: Record<string, unknown>, deliveryAddress: Record<string, unknown>, cargoItems: Record<string, unknown>, pricing?: Record<string, unknown> | null, scheduledDelivery?: string | null, vehicleId?: string | null, driverId?: string | null, createdAt: string, updatedAt: string }> }, deliveryJobStats: { __typename?: 'MtwDeliveryJobStats', total: number, pending: number, confirmed: number, assigned: number, pickedUp: number, inTransit: number, outForDelivery: number, delivered: number, cancelled: number, failed: number, returned: number }, fleetStats: { __typename?: 'MtwFleetStats', totalVehicles: number, activeVehicles: number, inTransitVehicles: number, maintenanceVehicles: number, totalDrivers: number, availableDrivers: number, onDeliveryDrivers: number }, financeMetrics: { __typename?: 'MtwFinanceMetrics', fuelCost: number, openInvoices: number, revenue: { __typename?: 'MtwRevenueMetrics', totalRevenue: number, averageOrderValue: number, deliveredRevenue: number } }, analytics: { __typename?: 'MtwAnalyticsSeries', deliveryTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }>, orderTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }>, revenueTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }> } };
296
296
 
297
297
  export type ListCustomersQueryVariables = Types.Exact<{
298
298
  filter?: Types.InputMaybe<Types.CustomerFilterInput>;
@@ -436,6 +436,7 @@ export type GetShipmentQuery = { __typename?: 'Query', shipment?: { __typename?:
436
436
 
437
437
  export type GetDeliveryJobTrackingQueryVariables = Types.Exact<{
438
438
  deliveryJobId: Types.Scalars['ID']['input'];
439
+ pagination?: Types.InputMaybe<Types.PaginationInput>;
439
440
  }>;
440
441
 
441
442
 
@@ -1837,10 +1838,15 @@ export const GetMovethewheelsDashboardDocument = gql`
1837
1838
  deliveryJobStats {
1838
1839
  total
1839
1840
  pending
1841
+ confirmed
1842
+ assigned
1843
+ pickedUp
1840
1844
  inTransit
1845
+ outForDelivery
1841
1846
  delivered
1842
1847
  cancelled
1843
1848
  failed
1849
+ returned
1844
1850
  }
1845
1851
  fleetStats {
1846
1852
  totalVehicles
@@ -2809,8 +2815,8 @@ export type GetShipmentLazyQueryHookResult = ReturnType<typeof useGetShipmentLaz
2809
2815
  export type GetShipmentSuspenseQueryHookResult = ReturnType<typeof useGetShipmentSuspenseQuery>;
2810
2816
  export type GetShipmentQueryResult = Apollo.QueryResult<Types.GetShipmentQuery, Types.GetShipmentQueryVariables>;
2811
2817
  export const GetDeliveryJobTrackingDocument = gql`
2812
- query GetDeliveryJobTracking($deliveryJobId: ID!) {
2813
- deliveryJobTracking(deliveryJobId: $deliveryJobId) {
2818
+ query GetDeliveryJobTracking($deliveryJobId: ID!, $pagination: PaginationInput) {
2819
+ deliveryJobTracking(deliveryJobId: $deliveryJobId, pagination: $pagination) {
2814
2820
  id
2815
2821
  shipmentId
2816
2822
  eventType
@@ -2834,6 +2840,7 @@ export const GetDeliveryJobTrackingDocument = gql`
2834
2840
  * const { data, loading, error } = useGetDeliveryJobTrackingQuery({
2835
2841
  * variables: {
2836
2842
  * deliveryJobId: // value for 'deliveryJobId'
2843
+ * pagination: // value for 'pagination'
2837
2844
  * },
2838
2845
  * });
2839
2846
  */
@@ -1,23 +1,21 @@
1
1
  /**
2
- * OrderItemRow — one order-item line in CreateOrderPage's "Order Items" card.
2
+ * OrderItemRow — one order-item line in CreateOrderPage's "Shipment contents" section.
3
3
  *
4
4
  * Its own component (not inlined in a `.map()`) because it needs one
5
- * `useProduct`/`useProductStockLevels` hook call per row to resolve the
6
- * selected catalog item's label and stock hint independently of the shared,
7
- * search-filtered `products` list passed down from the page — the Rules of
8
- * Hooks don't allow that from inside a loop in the parent.
5
+ * `useProduct`/`useProductStockLevels` hook call per row to resolve the selected
6
+ * catalog item's label and stock hint independently of the shared, search-filtered
7
+ * products list passed down from the page.
9
8
  */
10
9
  import type { ChangeEvent } from 'react';
11
10
  import { Trash2 } from 'lucide-react';
12
- import { Button, Input, Label, Separator } from './ui';
11
+ import { Button, Input, Label } from './ui';
13
12
  import { EntityCombobox } from './EntityCombobox';
14
13
  import { useProduct, useProductStockLevels } from '../hooks/useInventory';
15
- import type { OrderItem, Product } from '../types';
14
+ import type { Dimensions, OrderItem, Product } from '../types';
16
15
 
17
16
  interface OrderItemRowProps {
18
17
  item: OrderItem;
19
18
  index: number;
20
- isFirst: boolean;
21
19
  products: Product[];
22
20
  productsLoading: boolean;
23
21
  onUpdate: (index: number, field: string, value: unknown) => void;
@@ -37,7 +35,6 @@ function stockHintText(totalAvailable: number, levels: { warehouseName: string }
37
35
  export function OrderItemRow({
38
36
  item,
39
37
  index,
40
- isFirst,
41
38
  products,
42
39
  productsLoading,
43
40
  onUpdate,
@@ -55,6 +52,9 @@ export function OrderItemRow({
55
52
  isLoading: stockLoading,
56
53
  } = useProductStockLevels(item.productId || undefined);
57
54
 
55
+ const dimensions =
56
+ item.dimensions ?? ({ length: 0, width: 0, height: 0, unit: 'cm' } satisfies Dimensions);
57
+
58
58
  const selectProduct = (productId: string) => {
59
59
  if (!productId) {
60
60
  onUpdate(index, 'productId', '');
@@ -69,103 +69,166 @@ export function OrderItemRow({
69
69
  onUpdate(index, 'weight', product.weight);
70
70
  onUpdate(index, 'value', product.value);
71
71
  onUpdate(index, 'dimensions', product.dimensions);
72
- // Quantity is never set from the catalog — there's no per-unit default
73
- // quantity on a cargo item, it's always this specific order's own call.
72
+ // Quantity is never set from the catalog — there is no per-unit default
73
+ // quantity on a cargo item; it belongs to this specific order line.
74
+ };
75
+
76
+ const updateDimension = (field: keyof Omit<Dimensions, 'unit'>, value: string) => {
77
+ onUpdate(index, 'dimensions', {
78
+ ...dimensions,
79
+ [field]: Number.parseFloat(value) || 0,
80
+ });
74
81
  };
75
82
 
76
83
  return (
77
- <div>
78
- {!isFirst && <Separator className="my-4" />}
79
- <div className="mb-3 space-y-2">
80
- <Label htmlFor={`item-product-${item.id}`}>Catalog item (optional)</Label>
81
- <EntityCombobox
82
- id={`item-product-${item.id}`}
83
- value={item.productId}
84
- onChange={selectProduct}
85
- options={products}
86
- loading={productsLoading}
87
- getId={(product) => product.id}
88
- getLabel={(product) => `${product.name} (${product.sku})`}
89
- renderOption={(product) => (
90
- <>
91
- {product.name} <span className="text-text-tertiary">({product.sku})</span>
92
- </>
93
- )}
94
- selectedOption={selectedProduct}
95
- selectedLoading={selectedProductLoading}
96
- placeholder="Pick from your product catalog"
97
- emptyText="No products found."
98
- noneLabel="Enter details manually"
99
- // Search is driven by the shared "Search product catalog" box above
100
- // this list — no second input inside the popover.
101
- />
102
- {item.productId && !stockLoading && (
103
- <p className="text-xs text-text-secondary">{stockHintText(totalAvailable, levels)}</p>
104
- )}
105
- </div>
106
- <div className="grid grid-cols-1 gap-4 md:grid-cols-7">
107
- <div className="space-y-2 md:col-span-2">
108
- <Label>Item Name</Label>
109
- <Input
110
- value={item.name}
111
- onChange={(e: ChangeEvent<HTMLInputElement>) => onUpdate(index, 'name', e.target.value)}
112
- placeholder="Product name"
113
- />
114
- </div>
115
- <div className="space-y-2">
116
- <Label>SKU</Label>
117
- <Input
118
- value={item.sku}
119
- onChange={(e: ChangeEvent<HTMLInputElement>) => onUpdate(index, 'sku', e.target.value)}
120
- placeholder="SKU-001"
121
- />
122
- </div>
123
- <div className="space-y-2">
124
- <Label>Quantity</Label>
125
- <Input
126
- type="number"
127
- min="1"
128
- value={item.quantity}
129
- onChange={(e: ChangeEvent<HTMLInputElement>) =>
130
- onUpdate(index, 'quantity', parseInt(e.target.value) || 1)
131
- }
132
- />
133
- </div>
134
- <div className="space-y-2">
135
- <Label>Weight (kg)</Label>
136
- <Input
137
- type="number"
138
- min="0"
139
- step="0.1"
140
- value={item.weight}
141
- onChange={(e: ChangeEvent<HTMLInputElement>) =>
142
- onUpdate(index, 'weight', parseFloat(e.target.value) || 0)
143
- }
144
- />
84
+ <div
85
+ className="rounded-card border border-border-subtle bg-bg-surface p-4 shadow-elevation-1 sm:p-5"
86
+ aria-labelledby={`order-item-${item.id}-title`}
87
+ >
88
+ <div className="mb-4 flex items-start justify-between gap-4">
89
+ <div>
90
+ <h3
91
+ id={`order-item-${item.id}-title`}
92
+ className="text-sm font-semibold text-text-primary"
93
+ >
94
+ Item {index + 1}
95
+ </h3>
96
+ <p className="mt-1 text-xs text-text-secondary">
97
+ Select a catalog item to prefill details, or enter a one-off shipment manually.
98
+ </p>
145
99
  </div>
146
- <div className="space-y-2">
147
- <Label>Value ($)</Label>
148
- <Input
149
- type="number"
150
- min="0"
151
- step="0.01"
152
- value={item.value}
153
- onChange={(e: ChangeEvent<HTMLInputElement>) =>
154
- onUpdate(index, 'value', parseFloat(e.target.value) || 0)
155
- }
100
+ <Button
101
+ type="button"
102
+ variant="ghost"
103
+ size="icon"
104
+ className="shrink-0 text-status-error-text"
105
+ onClick={() => onRemove(index)}
106
+ aria-label={`Remove item ${index + 1}`}
107
+ title={`Remove item ${index + 1}`}
108
+ >
109
+ <Trash2 size={16} aria-hidden="true" />
110
+ </Button>
111
+ </div>
112
+
113
+ <div className="space-y-4">
114
+ <div className="rounded-card border border-border-subtle bg-bg-sunken/40 p-3 sm:p-4">
115
+ <Label htmlFor={`item-product-${item.id}`}>Catalog item (optional)</Label>
116
+ <EntityCombobox
117
+ id={`item-product-${item.id}`}
118
+ value={item.productId}
119
+ onChange={selectProduct}
120
+ options={products}
121
+ loading={productsLoading}
122
+ getId={(product) => product.id}
123
+ getLabel={(product) => `${product.name} (${product.sku})`}
124
+ renderOption={(product) => (
125
+ <>
126
+ {product.name} <span className="text-text-tertiary">({product.sku})</span>
127
+ </>
128
+ )}
129
+ selectedOption={selectedProduct}
130
+ selectedLoading={selectedProductLoading}
131
+ placeholder="Pick from your product catalog"
132
+ emptyText="No products found."
133
+ noneLabel="Enter details manually"
156
134
  />
135
+ {item.productId && !stockLoading && (
136
+ <p className="mt-2 text-xs text-text-secondary" role="status">
137
+ {stockHintText(totalAvailable, levels)}
138
+ </p>
139
+ )}
157
140
  </div>
158
- <div className="flex items-end">
159
- <Button
160
- type="button"
161
- variant="ghost"
162
- size="icon"
163
- className="text-status-error-text"
164
- onClick={() => onRemove(index)}
165
- >
166
- <Trash2 size={16} />
167
- </Button>
141
+
142
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-12">
143
+ <div className="space-y-2 lg:col-span-4">
144
+ <Label htmlFor={`item-name-${item.id}`}>Item name</Label>
145
+ <Input
146
+ id={`item-name-${item.id}`}
147
+ value={item.name}
148
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
149
+ onUpdate(index, 'name', e.target.value)
150
+ }
151
+ placeholder="Product name"
152
+ />
153
+ </div>
154
+ <div className="space-y-2 lg:col-span-3">
155
+ <Label htmlFor={`item-sku-${item.id}`}>SKU</Label>
156
+ <Input
157
+ id={`item-sku-${item.id}`}
158
+ value={item.sku}
159
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
160
+ onUpdate(index, 'sku', e.target.value)
161
+ }
162
+ placeholder="SKU-001"
163
+ />
164
+ </div>
165
+ <div className="space-y-2 lg:col-span-2">
166
+ <Label htmlFor={`item-quantity-${item.id}`}>Quantity</Label>
167
+ <Input
168
+ id={`item-quantity-${item.id}`}
169
+ type="number"
170
+ min="1"
171
+ value={item.quantity}
172
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
173
+ onUpdate(index, 'quantity', parseInt(e.target.value, 10) || 1)
174
+ }
175
+ />
176
+ </div>
177
+ <div className="space-y-2 lg:col-span-2">
178
+ <Label htmlFor={`item-weight-${item.id}`}>Weight (kg)</Label>
179
+ <Input
180
+ id={`item-weight-${item.id}`}
181
+ type="number"
182
+ min="0"
183
+ step="0.1"
184
+ value={item.weight}
185
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
186
+ onUpdate(index, 'weight', parseFloat(e.target.value) || 0)
187
+ }
188
+ />
189
+ </div>
190
+ <div className="space-y-2 lg:col-span-1">
191
+ <Label htmlFor={`item-value-${item.id}`}>Value ($)</Label>
192
+ <Input
193
+ id={`item-value-${item.id}`}
194
+ type="number"
195
+ min="0"
196
+ step="0.01"
197
+ value={item.value}
198
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
199
+ onUpdate(index, 'value', parseFloat(e.target.value) || 0)
200
+ }
201
+ />
202
+ </div>
168
203
  </div>
204
+
205
+ <fieldset className="rounded-card border border-border-subtle bg-bg-sunken/40 p-3 sm:p-4">
206
+ <legend className="px-1 text-xs font-medium text-text-secondary">
207
+ Dimensions ({dimensions.unit})
208
+ </legend>
209
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
210
+ {(['length', 'width', 'height'] as const).map((field) => (
211
+ <div key={field} className="space-y-2">
212
+ <Label htmlFor={`item-${field}-${item.id}`}>
213
+ {field.charAt(0).toUpperCase() + field.slice(1)}
214
+ </Label>
215
+ <Input
216
+ id={`item-${field}-${item.id}`}
217
+ type="number"
218
+ min="0"
219
+ step="0.1"
220
+ value={dimensions[field]}
221
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
222
+ updateDimension(field, e.target.value)
223
+ }
224
+ />
225
+ </div>
226
+ ))}
227
+ </div>
228
+ <p className="mt-2 text-xs text-text-secondary">
229
+ Catalog selections prefill these measurements, and you can adjust them for this order.
230
+ </p>
231
+ </fieldset>
169
232
  </div>
170
233
  </div>
171
234
  );
@@ -5,6 +5,7 @@ import {
5
5
  ORDER_STATUS_COLORS,
6
6
  VEHICLE_STATUS_COLORS,
7
7
  DRIVER_STATUS_COLORS,
8
+ DRIVER_STATUS_LABELS,
8
9
  PRIORITY_COLORS,
9
10
  } from '../constants';
10
11
 
@@ -17,6 +18,8 @@ interface StatusBadgeProps {
17
18
  bg: string;
18
19
  text: string;
19
20
  };
21
+ /** Override the auto-derived display text (title-cased raw status). */
22
+ label?: string;
20
23
  size?: 'sm' | 'md' | 'lg';
21
24
  showDot?: boolean;
22
25
  className?: string;
@@ -35,6 +38,7 @@ export function StatusBadge({
35
38
  status,
36
39
  type = 'custom',
37
40
  customColors,
41
+ label,
38
42
  size = 'md',
39
43
  showDot = true,
40
44
  className,
@@ -59,8 +63,13 @@ export function StatusBadge({
59
63
  }
60
64
  }
61
65
 
62
- // Format status for display
63
- const displayStatus = status.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
66
+ // Format status for display — an explicit label always wins; driver status
67
+ // otherwise renders from the same shared label map the filters use, so the
68
+ // table and the filter dropdown can never drift apart again (BOFF-7909).
69
+ const displayStatus =
70
+ label ??
71
+ (type === 'driver' ? DRIVER_STATUS_LABELS[status] : undefined) ??
72
+ status.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
64
73
 
65
74
  return (
66
75
  <Badge
@@ -46,6 +46,7 @@ export const ORDER_STATUS_COLORS: Record<string, StatusColor> = {
46
46
  in_transit: INFO,
47
47
  out_for_delivery: WARNING,
48
48
  delivered: SUCCESS,
49
+ returned: WARNING,
49
50
  cancelled: ERROR,
50
51
  failed: ERROR,
51
52
  };
@@ -64,6 +65,18 @@ export const DRIVER_STATUS_COLORS: Record<string, StatusColor> = {
64
65
  busy: INFO,
65
66
  offline: NEUTRAL,
66
67
  on_break: WARNING,
68
+ inactive: ERROR,
69
+ };
70
+
71
+ // Single source of truth for driver status display text — used by both the
72
+ // DriversPage status filter and the roster table's StatusBadge, so the two
73
+ // can never drift apart again (BOFF-7909).
74
+ export const DRIVER_STATUS_LABELS: Record<string, string> = {
75
+ available: 'Available',
76
+ busy: 'On Delivery',
77
+ offline: 'Off Duty',
78
+ on_break: 'On Break',
79
+ inactive: 'Inactive',
67
80
  };
68
81
 
69
82
  // Priority colors