@burdenoff/microfe-movethewheels 2026.924.1 → 2026.925.1

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.
@@ -79,10 +79,13 @@ const STATUS_LABELS: Record<OrderStatus, string> = {
79
79
  in_transit: 'In Transit',
80
80
  out_for_delivery: 'Out for Delivery',
81
81
  delivered: 'Delivered',
82
+ returned: 'Returned',
82
83
  cancelled: 'Cancelled',
83
84
  failed: 'Failed',
84
85
  };
85
86
 
87
+ const TERMINAL_STATUSES = new Set<OrderStatus>(['delivered', 'returned', 'cancelled', 'failed']);
88
+
86
89
  function nextStatus(current: OrderStatus): OrderStatus | null {
87
90
  const index = STATUS_FLOW.indexOf(current);
88
91
  if (index === -1 || index >= STATUS_FLOW.length - 1) return null;
@@ -149,18 +152,31 @@ export default function OrderDetailsPage() {
149
152
  }, [selectedVehicle, vehicles, vehiclesLoading]);
150
153
 
151
154
  const handleAssign = async () => {
152
- if (!orderId || (!selectedVehicle && !selectedDriver)) {
153
- setActionError('Pick a vehicle and/or a driver to assign.');
155
+ if (!orderId || !order) {
156
+ setActionError('This order is no longer available. Refresh and try again.');
154
157
  return;
155
158
  }
159
+
160
+ const vehicleId = selectedVehicle || order.vehicleId;
161
+ const driverId = selectedDriver || order.driverId;
162
+ if (!vehicleId || !driverId) {
163
+ setActionError('Select both a compatible vehicle and driver before dispatching.');
164
+ return;
165
+ }
166
+
167
+ const wasFullyAssigned = Boolean(order.vehicleId && order.driverId);
156
168
  setAssigning(true);
157
169
  setActionError(null);
158
170
  try {
159
- await updateOrder(orderId, {
160
- vehicleId: selectedVehicle || undefined,
161
- driverId: selectedDriver || undefined,
162
- });
163
- notify('Assignment updated.');
171
+ await updateOrder(orderId, { vehicleId, driverId });
172
+ if (!wasFullyAssigned && order.status === 'confirmed') {
173
+ await updateOrderStatus(orderId, 'assigned');
174
+ }
175
+ notify(
176
+ !wasFullyAssigned && order.status === 'confirmed'
177
+ ? 'Fleet assigned and order advanced to Assigned.'
178
+ : 'Assignment updated.'
179
+ );
164
180
  setSelectedVehicle('');
165
181
  setSelectedDriver('');
166
182
  refetch();
@@ -172,7 +188,13 @@ export default function OrderDetailsPage() {
172
188
  };
173
189
 
174
190
  const handleStatusChange = async (status: OrderStatus, successMessage: string) => {
175
- if (!orderId) return;
191
+ if (!orderId || !order) return;
192
+ if (status === 'assigned' && (!order.vehicleId || !order.driverId)) {
193
+ setActionError('Assign both a compatible vehicle and driver before advancing to Assigned.');
194
+ return;
195
+ }
196
+ if (TERMINAL_STATUSES.has(order.status)) return;
197
+
176
198
  setAdvancing(true);
177
199
  setActionError(null);
178
200
  try {
@@ -635,24 +657,34 @@ export default function OrderDetailsPage() {
635
657
  {(() => {
636
658
  const upcoming = nextStatus(order.status);
637
659
  if (!upcoming) return null;
660
+ const missingDispatchPair =
661
+ upcoming === 'assigned' && (!order.vehicleId || !order.driverId);
638
662
  return (
639
- <Button
640
- variant="outline"
641
- className="w-full"
642
- onClick={() =>
643
- handleStatusChange(
644
- upcoming,
645
- `Order advanced to ${STATUS_LABELS[upcoming]}.`
646
- )
647
- }
648
- disabled={advancing}
649
- >
650
- <ArrowRight size={16} />
651
- {advancing ? 'Updating…' : `Advance to ${STATUS_LABELS[upcoming]}`}
652
- </Button>
663
+ <>
664
+ <Button
665
+ variant="outline"
666
+ className="w-full"
667
+ onClick={() =>
668
+ handleStatusChange(
669
+ upcoming,
670
+ `Order advanced to ${STATUS_LABELS[upcoming]}.`
671
+ )
672
+ }
673
+ disabled={advancing || missingDispatchPair}
674
+ >
675
+ <ArrowRight size={16} />
676
+ {advancing ? 'Updating…' : `Advance to ${STATUS_LABELS[upcoming]}`}
677
+ </Button>
678
+ {missingDispatchPair && (
679
+ <p className="text-xs text-text-secondary">
680
+ Assign both a compatible vehicle and driver before dispatching this
681
+ order.
682
+ </p>
683
+ )}
684
+ </>
653
685
  );
654
686
  })()}
655
- {order.status !== 'cancelled' && order.status !== 'delivered' && (
687
+ {order.status !== 'cancelled' && !TERMINAL_STATUSES.has(order.status) && (
656
688
  <Button
657
689
  variant="outline"
658
690
  className="w-full text-status-error-text"
@@ -195,6 +195,7 @@ export default function OrdersListPage() {
195
195
  label: t('movethewheels.orders.status.outForDelivery', 'Out for Delivery'),
196
196
  },
197
197
  { value: 'delivered', label: t('movethewheels.orders.status.delivered', 'Delivered') },
198
+ { value: 'returned', label: t('movethewheels.orders.status.returned', 'Returned') },
198
199
  { value: 'cancelled', label: t('movethewheels.orders.status.cancelled', 'Cancelled') },
199
200
  { value: 'failed', label: t('movethewheels.orders.status.failed', 'Failed') },
200
201
  ];
@@ -60,7 +60,20 @@ export default function TrackingPage() {
60
60
  ? { ...destCoord, label: `Delivery · ${order.deliveryAddress.city || 'Destination'}` }
61
61
  : null;
62
62
 
63
- const trail: TrackPoint[] = order.timeline
63
+ const trackingTrail: TrackPoint[] = (tracking?.locationHistory ?? [])
64
+ .map((location): TrackPoint | null => {
65
+ const coord = firstRealLatLng(location);
66
+ return coord
67
+ ? {
68
+ lat: coord.lat,
69
+ lng: coord.lng,
70
+ timestamp: location.timestamp?.toISOString() ?? new Date().toISOString(),
71
+ }
72
+ : null;
73
+ })
74
+ .filter((point): point is TrackPoint => point !== null);
75
+
76
+ const statusTrail: TrackPoint[] = order.timeline
64
77
  .map((event): TrackPoint | null => {
65
78
  const coord = event.location ? firstRealLatLng(event.location) : null;
66
79
  return coord
@@ -69,6 +82,7 @@ export default function TrackingPage() {
69
82
  })
70
83
  .filter((point): point is TrackPoint => point !== null);
71
84
 
85
+ const trail = trackingTrail.length > 0 ? trackingTrail : statusTrail;
72
86
  const currentCoord = tracking ? firstRealLatLng(tracking.currentLocation) : null;
73
87
  const current: TrackEndpoint | null = currentCoord
74
88
  ? {
@@ -163,7 +177,7 @@ export default function TrackingPage() {
163
177
  )}
164
178
 
165
179
  {/* Tracking Results */}
166
- {order && tracking && (
180
+ {order && (
167
181
  <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
168
182
  {/* Main Tracking Info */}
169
183
  <div className="lg:col-span-2 space-y-6">
@@ -181,14 +195,14 @@ export default function TrackingPage() {
181
195
  <p className="text-sm text-text-secondary">
182
196
  {t('movethewheels.tracking.details.trackingNumber', 'Tracking Number')}
183
197
  </p>
184
- <p className="font-mono font-medium">{order.trackingNumber}</p>
198
+ <p className="font-mono font-medium">{order.trackingNumber || '—'}</p>
185
199
  </div>
186
200
  <div>
187
201
  <p className="text-sm text-text-secondary">
188
202
  {t('movethewheels.tracking.details.estimatedDelivery', 'Estimated Delivery')}
189
203
  </p>
190
204
  <p className="font-medium">
191
- {tracking.estimatedArrival
205
+ {tracking?.estimatedArrival
192
206
  ? formatDate(tracking.estimatedArrival, 'full')
193
207
  : order.scheduledDelivery
194
208
  ? formatDate(order.scheduledDelivery, 'full')
@@ -207,9 +221,21 @@ export default function TrackingPage() {
207
221
  <p className="text-sm text-text-secondary">
208
222
  {t('movethewheels.tracking.details.lastUpdated', 'Last Updated')}
209
223
  </p>
210
- <p className="font-medium">{formatDate(tracking.lastUpdated, 'relative')}</p>
224
+ <p className="font-medium">
225
+ {tracking
226
+ ? formatDate(tracking.lastUpdated, 'relative')
227
+ : t('movethewheels.tracking.details.noUpdates', 'No tracking updates yet')}
228
+ </p>
211
229
  </div>
212
230
  </div>
231
+ {!tracking && (
232
+ <p className="mt-4 rounded-card border border-border-subtle bg-bg-sunken p-3 text-sm text-text-secondary">
233
+ {t(
234
+ 'movethewheels.tracking.details.noUpdatesDescription',
235
+ 'The delivery was found, but no tracking updates have been recorded yet.'
236
+ )}
237
+ </p>
238
+ )}
213
239
  </EmphasisPanel>
214
240
 
215
241
  {/* Route Map — real origin/destination + tracking trail + live position */}
@@ -356,7 +382,7 @@ export default function TrackingPage() {
356
382
  </Card>
357
383
 
358
384
  {/* Alerts */}
359
- {tracking.alerts && tracking.alerts.length > 0 && (
385
+ {tracking?.alerts && tracking.alerts.length > 0 && (
360
386
  <Card>
361
387
  <CardHeader>
362
388
  <CardTitle className="flex items-center gap-2">
@@ -430,14 +456,18 @@ export default function TrackingPage() {
430
456
  </p>
431
457
  </div>
432
458
  )}
433
- <p className="text-sm">
434
- {tracking.currentLocation.address ||
435
- t('movethewheels.tracking.location.updating', 'Location updating...')}
436
- </p>
437
- <p className="text-xs text-text-secondary mt-1">
438
- {t('movethewheels.tracking.location.updated', 'Updated')}{' '}
439
- {formatDate(tracking.currentLocation.timestamp || new Date(), 'relative')}
440
- </p>
459
+ {tracking && (
460
+ <>
461
+ <p className="text-sm">
462
+ {tracking.currentLocation.address ||
463
+ t('movethewheels.tracking.location.updating', 'Location updating...')}
464
+ </p>
465
+ <p className="text-xs text-text-secondary mt-1">
466
+ {t('movethewheels.tracking.location.updated', 'Updated')}{' '}
467
+ {formatDate(tracking.currentLocation.timestamp || new Date(), 'relative')}
468
+ </p>
469
+ </>
470
+ )}
441
471
  </CardContent>
442
472
  </Card>
443
473
 
@@ -253,6 +253,7 @@ export type OrderStatus =
253
253
  | 'in_transit'
254
254
  | 'out_for_delivery'
255
255
  | 'delivered'
256
+ | 'returned'
256
257
  | 'cancelled'
257
258
  | 'failed';
258
259
 
@@ -20,6 +20,7 @@ export function deliveryMarkerStatus(status: string | undefined): MapMarkerStatu
20
20
  case 'failed':
21
21
  case 'delayed':
22
22
  case 'cancelled':
23
+ case 'returned':
23
24
  return 'delayed';
24
25
  case 'assigned':
25
26
  case 'picked_up':
@@ -13,10 +13,15 @@ query GetMovethewheelsDashboard {
13
13
  deliveryJobStats {
14
14
  total
15
15
  pending
16
+ confirmed
17
+ assigned
18
+ pickedUp
16
19
  inTransit
20
+ outForDelivery
17
21
  delivered
18
22
  cancelled
19
23
  failed
24
+ returned
20
25
  }
21
26
  fleetStats {
22
27
  totalVehicles
@@ -57,8 +57,8 @@ query GetShipment($id: ID!) {
57
57
  }
58
58
  }
59
59
 
60
- query GetDeliveryJobTracking($deliveryJobId: ID!) {
61
- deliveryJobTracking(deliveryJobId: $deliveryJobId) {
60
+ query GetDeliveryJobTracking($deliveryJobId: ID!, $pagination: PaginationInput) {
61
+ deliveryJobTracking(deliveryJobId: $deliveryJobId, pagination: $pagination) {
62
62
  id
63
63
  shipmentId
64
64
  eventType