@burdenoff/microfe-movethewheels 2026.920.2 → 2026.921.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.
Files changed (82) hide show
  1. package/dist/components/CarbonFootprintPanel.js +1 -1
  2. package/dist/components/CustomerPicker.js +271 -0
  3. package/dist/components/DataTable.js +140 -139
  4. package/dist/components/DocumentsCard.js +14 -8
  5. package/dist/generated/wspace-types.d.ts +1043 -5
  6. package/dist/hooks/useAnalytics.js +21 -1
  7. package/dist/hooks/useCustomers.js +94 -78
  8. package/dist/hooks/useDebouncedValue.js +11 -0
  9. package/dist/hooks/useEntityDocuments.js +28 -18
  10. package/dist/hooks/useEpod.js +24 -17
  11. package/dist/hooks/useFleet.js +146 -125
  12. package/dist/hooks/useInventory.js +255 -186
  13. package/dist/hooks/useMultiModalShipments.js +35 -27
  14. package/dist/hooks/useOrders.js +73 -73
  15. package/dist/hooks/useRoutes.js +97 -75
  16. package/dist/hooks/useTracking.js +100 -88
  17. package/dist/movethewheels/components/CustomerPicker.d.ts +19 -0
  18. package/dist/movethewheels/components/DataTable.d.ts +5 -1
  19. package/dist/movethewheels/hooks/useCustomers.d.ts +0 -1
  20. package/dist/movethewheels/hooks/useDebouncedValue.d.ts +1 -0
  21. package/dist/movethewheels/hooks/useEntityDocuments.d.ts +2 -0
  22. package/dist/movethewheels/hooks/useEpod.d.ts +6 -1
  23. package/dist/movethewheels/hooks/useFleet.d.ts +49 -5
  24. package/dist/movethewheels/hooks/useInventory.d.ts +24 -17
  25. package/dist/movethewheels/hooks/useMultiModalShipments.d.ts +6 -1
  26. package/dist/movethewheels/hooks/useOrders.d.ts +33 -5
  27. package/dist/movethewheels/hooks/useRoutes.d.ts +2 -1
  28. package/dist/movethewheels/hooks/useTracking.d.ts +12 -11
  29. package/dist/movethewheels/types/index.d.ts +7 -5
  30. package/dist/movethewheels/utils/navigation.d.ts +14 -0
  31. package/dist/pages/CreateOrderPage.js +228 -217
  32. package/dist/pages/CustomerDetailsPage.js +220 -220
  33. package/dist/pages/CustomersPage.js +61 -54
  34. package/dist/pages/DeliveryCapturePage.js +131 -109
  35. package/dist/pages/DriversPage.js +100 -92
  36. package/dist/pages/InventoryPage.js +239 -236
  37. package/dist/pages/MultiModalShipmentPage.js +163 -130
  38. package/dist/pages/OrderDetailsPage.js +224 -208
  39. package/dist/pages/OrdersListPage.js +66 -60
  40. package/dist/pages/ProductDetailsPage.js +241 -200
  41. package/dist/pages/ProductsPage.js +59 -52
  42. package/dist/pages/RouteDetailsPage.js +343 -321
  43. package/dist/pages/RoutesPage.js +55 -48
  44. package/dist/pages/VehiclesPage.js +63 -56
  45. package/dist/pages/WarehousesPage.js +60 -53
  46. package/dist/utils/navigation.js +22 -1
  47. package/package.json +1 -1
  48. package/src/generated/wspace-types.ts +1188 -5
  49. package/src/movethewheels/components/CarbonFootprintPanel.tsx +0 -1
  50. package/src/movethewheels/components/CustomerPicker.tsx +321 -0
  51. package/src/movethewheels/components/DataTable.tsx +34 -18
  52. package/src/movethewheels/components/DocumentsCard.tsx +6 -1
  53. package/src/movethewheels/components/maps/leaflet-setup.ts +1 -8
  54. package/src/movethewheels/hooks/useAnalytics.ts +27 -0
  55. package/src/movethewheels/hooks/useCustomers.ts +64 -24
  56. package/src/movethewheels/hooks/useDebouncedValue.ts +12 -0
  57. package/src/movethewheels/hooks/useEntityDocuments.ts +43 -25
  58. package/src/movethewheels/hooks/useEpod.ts +14 -7
  59. package/src/movethewheels/hooks/useFleet.ts +58 -37
  60. package/src/movethewheels/hooks/useFleetIntelligence.ts +1 -4
  61. package/src/movethewheels/hooks/useInventory.ts +211 -90
  62. package/src/movethewheels/hooks/useMultiModalShipments.ts +15 -11
  63. package/src/movethewheels/hooks/useOrders.ts +13 -14
  64. package/src/movethewheels/hooks/useRoutes.ts +80 -23
  65. package/src/movethewheels/hooks/useTracking.ts +130 -98
  66. package/src/movethewheels/pages/CreateOrderPage.tsx +92 -31
  67. package/src/movethewheels/pages/CustomerDetailsPage.tsx +15 -6
  68. package/src/movethewheels/pages/CustomersPage.tsx +11 -1
  69. package/src/movethewheels/pages/DeliveryCapturePage.tsx +31 -1
  70. package/src/movethewheels/pages/DriversPage.tsx +24 -12
  71. package/src/movethewheels/pages/InventoryPage.tsx +57 -32
  72. package/src/movethewheels/pages/MultiModalShipmentPage.tsx +52 -3
  73. package/src/movethewheels/pages/OrderDetailsPage.tsx +17 -1
  74. package/src/movethewheels/pages/OrdersListPage.tsx +20 -3
  75. package/src/movethewheels/pages/ProductDetailsPage.tsx +52 -3
  76. package/src/movethewheels/pages/ProductsPage.tsx +11 -1
  77. package/src/movethewheels/pages/RouteDetailsPage.tsx +35 -5
  78. package/src/movethewheels/pages/RoutesPage.tsx +11 -1
  79. package/src/movethewheels/pages/VehiclesPage.tsx +19 -2
  80. package/src/movethewheels/pages/WarehousesPage.tsx +11 -1
  81. package/src/movethewheels/types/index.ts +7 -5
  82. package/src/movethewheels/utils/navigation.ts +32 -0
@@ -23,11 +23,7 @@ export type OrderWriteInput = Omit<Partial<Order>, 'scheduledDelivery'> & {
23
23
  interface UseOrdersResult {
24
24
  orders: Order[];
25
25
  totalOrders: number;
26
- /**
27
- * True when the most recent fetch returned a full page (rawCount === limit),
28
- * i.e. there may be another page. Used for offset pagers where no grand
29
- * total is available.
30
- */
26
+ /** True when the most recent fetch has another server page. */
31
27
  hasMore: boolean;
32
28
  isLoading: boolean;
33
29
  error: Error | null;
@@ -42,7 +38,7 @@ interface UseOrdersResult {
42
38
  // The `order` domain was renamed to `delivery_jobs`/MtwDeliveryJob on 2026-07-07 (commit
43
39
  // 7e99ec5); `orderType` was dropped entirely in that rename with no replacement field, so it
44
40
  // is intentionally absent here — see BOFF-4505.
45
- interface ApiOrder {
41
+ export interface ApiOrder {
46
42
  id: string;
47
43
  deliveryJobNumber: string;
48
44
  customerId: string;
@@ -111,6 +107,7 @@ const LIST_ORDERS_QUERY = `
111
107
  deliveryJobs(filter: $filter, pagination: $pagination) {
112
108
  ${DELIVERY_JOB_FIELDS}
113
109
  }
110
+ deliveryJobsCount(filter: $filter)
114
111
  }
115
112
  `;
116
113
 
@@ -266,7 +263,7 @@ function mapAddress(address: Record<string, unknown> | null | undefined): Order[
266
263
  } as Order['pickupAddress'];
267
264
  }
268
265
 
269
- function mapOrder(order: ApiOrder): Order {
266
+ export function mapOrder(order: ApiOrder): Order {
270
267
  return {
271
268
  id: order.id,
272
269
  orderNumber: order.deliveryJobNumber,
@@ -324,6 +321,7 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
324
321
  rawFilters?.priority,
325
322
  rawFilters?.customerId,
326
323
  rawFilters?.driverId,
324
+ rawFilters?.search,
327
325
  rawFilters?.unassigned,
328
326
  ]
329
327
  );
@@ -331,6 +329,7 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
331
329
  useMoveTheWheels();
332
330
 
333
331
  const [orders, setOrders] = useState<Order[]>([]);
332
+ const [totalOrders, setTotalOrders] = useState(0);
334
333
  const [hasMore, setHasMore] = useState(false);
335
334
  const [isLoading, setIsLoading] = useState(false);
336
335
  const [error, setError] = useState<Error | null>(null);
@@ -353,6 +352,7 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
353
352
  try {
354
353
  const data = await executeGraphQL<{
355
354
  deliveryJobs: ApiOrder[];
355
+ deliveryJobsCount: number;
356
356
  }>(graphQLOptions, LIST_ORDERS_QUERY, {
357
357
  filter: {
358
358
  status: filters?.status ? toApiStatus(filters.status) : undefined,
@@ -361,17 +361,16 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
361
361
  : undefined,
362
362
  customerId: filters?.customerId,
363
363
  driverId: filters?.driverId,
364
+ unassigned: filters?.unassigned,
365
+ search: filters?.search,
364
366
  },
365
367
  pagination: { limit, offset },
366
368
  });
367
369
 
368
370
  const raw = data.deliveryJobs;
369
- setHasMore(raw.length === limit);
370
- let mapped = raw.map(mapOrder);
371
- if (filters?.unassigned) {
372
- mapped = mapped.filter((item) => !item.driverId);
373
- }
374
- setOrders(mapped);
371
+ setTotalOrders(data.deliveryJobsCount);
372
+ setHasMore(offset + raw.length < data.deliveryJobsCount);
373
+ setOrders(raw.map(mapOrder));
375
374
  } catch (fetchError) {
376
375
  setError(fetchError instanceof Error ? fetchError : new Error('Failed to load orders'));
377
376
  } finally {
@@ -481,7 +480,7 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
481
480
 
482
481
  return {
483
482
  orders,
484
- totalOrders: orders.length,
483
+ totalOrders,
485
484
  hasMore,
486
485
  isLoading,
487
486
  error,
@@ -7,6 +7,7 @@ interface UseRoutesOptions {
7
7
  driverId?: string;
8
8
  vehicleId?: string;
9
9
  status?: 'planned' | 'in_progress' | 'completed' | 'cancelled';
10
+ search?: string;
10
11
  limit?: number;
11
12
  offset?: number;
12
13
  }
@@ -14,7 +15,7 @@ interface UseRoutesOptions {
14
15
  interface UseRoutesResult {
15
16
  routes: Route[];
16
17
  totalRoutes: number;
17
- /** True when the last fetch returned a full page (rawCount === limit). */
18
+ /** True when the last fetch has another server page. */
18
19
  hasMore: boolean;
19
20
  isLoading: boolean;
20
21
  error: Error | null;
@@ -63,6 +64,7 @@ const LIST_ROUTES_QUERY = `
63
64
  actualDuration
64
65
  createdAt
65
66
  }
67
+ routesCount(filter: $filter)
66
68
  }
67
69
  `;
68
70
 
@@ -253,9 +255,10 @@ function useGraphqlOptions() {
253
255
  }
254
256
 
255
257
  export function useRoutes(options: UseRoutesOptions = {}): UseRoutesResult {
256
- const { driverId, vehicleId, status, limit = 500, offset = 0 } = options;
258
+ const { driverId, vehicleId, status, search, limit = 20, offset = 0 } = options;
257
259
  const graphQLOptions = useGraphqlOptions();
258
260
  const [routes, setRoutes] = useState<Route[]>([]);
261
+ const [totalRoutes, setTotalRoutes] = useState(0);
259
262
  const [hasMore, setHasMore] = useState(false);
260
263
  const [isLoading, setIsLoading] = useState(false);
261
264
  const [error, setError] = useState<Error | null>(null);
@@ -264,23 +267,28 @@ export function useRoutes(options: UseRoutesOptions = {}): UseRoutesResult {
264
267
  setIsLoading(true);
265
268
  setError(null);
266
269
  try {
267
- const data = await executeGraphQL<{ routes: ApiRoute[] }>(graphQLOptions, LIST_ROUTES_QUERY, {
270
+ const data = await executeGraphQL<{
271
+ routes: ApiRoute[];
272
+ routesCount: number;
273
+ }>(graphQLOptions, LIST_ROUTES_QUERY, {
268
274
  filter: {
269
275
  driverId,
270
276
  vehicleId,
271
277
  status: status ? toApiRouteStatus(status) : undefined,
278
+ search: search || undefined,
272
279
  },
273
280
  pagination: { limit, offset },
274
281
  });
275
282
 
276
- setHasMore(data.routes.length === limit);
283
+ setTotalRoutes(data.routesCount);
284
+ setHasMore(offset + data.routes.length < data.routesCount);
277
285
  setRoutes(data.routes.map(mapRoute));
278
286
  } catch (fetchError) {
279
287
  setError(fetchError instanceof Error ? fetchError : new Error('Failed to load routes'));
280
288
  } finally {
281
289
  setIsLoading(false);
282
290
  }
283
- }, [driverId, graphQLOptions, status, vehicleId, limit, offset]);
291
+ }, [driverId, graphQLOptions, search, status, vehicleId, limit, offset]);
284
292
 
285
293
  useEffect(() => {
286
294
  void load();
@@ -400,7 +408,7 @@ export function useRoutes(options: UseRoutesOptions = {}): UseRoutesResult {
400
408
 
401
409
  return {
402
410
  routes,
403
- totalRoutes: routes.length,
411
+ totalRoutes,
404
412
  hasMore,
405
413
  isLoading,
406
414
  error,
@@ -455,26 +463,75 @@ export function useRoute(routeId: string | undefined) {
455
463
  };
456
464
  }
457
465
 
458
- export function useRouteStats() {
459
- const { routes, isLoading, error } = useRoutes();
466
+ const ROUTE_STATS_QUERY = `
467
+ query RouteStats {
468
+ routeStats {
469
+ total
470
+ totalDistance
471
+ totalDuration
472
+ completed
473
+ inProgress
474
+ planned
475
+ optimized
476
+ }
477
+ }
478
+ `;
460
479
 
461
- const stats = useMemo(() => {
462
- const totalDistance = routes.reduce((sum, route) => sum + route.distance, 0);
463
- const totalDuration = routes.reduce((sum, route) => sum + route.estimatedDuration, 0);
464
- const totalRoutes = routes.length;
480
+ export function useRouteStats() {
481
+ const graphQLOptions = useGraphqlOptions();
482
+ const [stats, setStats] = useState({
483
+ totalRoutes: 0,
484
+ totalDistance: 0,
485
+ totalDuration: 0,
486
+ avgDistance: 0,
487
+ avgDuration: 0,
488
+ completedRoutes: 0,
489
+ inProgressRoutes: 0,
490
+ plannedRoutes: 0,
491
+ optimizedRoutes: 0,
492
+ });
493
+ const [isLoading, setIsLoading] = useState(false);
494
+ const [error, setError] = useState<Error | null>(null);
465
495
 
466
- return {
467
- totalRoutes,
468
- totalDistance,
469
- totalDuration,
470
- avgDistance: totalRoutes > 0 ? totalDistance / totalRoutes : 0,
471
- avgDuration: totalRoutes > 0 ? totalDuration / totalRoutes : 0,
472
- completedRoutes: routes.filter((route) => route.status === 'completed').length,
473
- inProgressRoutes: routes.filter((route) => route.status === 'in_progress').length,
474
- plannedRoutes: routes.filter((route) => route.status === 'planned').length,
475
- optimizedRoutes: routes.filter((route) => route.optimized).length,
496
+ useEffect(() => {
497
+ const load = async () => {
498
+ setIsLoading(true);
499
+ setError(null);
500
+ try {
501
+ const data = await executeGraphQL<{
502
+ routeStats: {
503
+ total: number;
504
+ totalDistance: number;
505
+ totalDuration: number;
506
+ completed: number;
507
+ inProgress: number;
508
+ planned: number;
509
+ optimized: number;
510
+ };
511
+ }>(graphQLOptions, ROUTE_STATS_QUERY);
512
+ const routeStats = data.routeStats;
513
+ setStats({
514
+ totalRoutes: routeStats.total,
515
+ totalDistance: routeStats.totalDistance,
516
+ totalDuration: routeStats.totalDuration,
517
+ avgDistance: routeStats.total > 0 ? routeStats.totalDistance / routeStats.total : 0,
518
+ avgDuration: routeStats.total > 0 ? routeStats.totalDuration / routeStats.total : 0,
519
+ completedRoutes: routeStats.completed,
520
+ inProgressRoutes: routeStats.inProgress,
521
+ plannedRoutes: routeStats.planned,
522
+ optimizedRoutes: routeStats.optimized,
523
+ });
524
+ } catch (fetchError) {
525
+ setError(
526
+ fetchError instanceof Error ? fetchError : new Error('Failed to load route stats')
527
+ );
528
+ } finally {
529
+ setIsLoading(false);
530
+ }
476
531
  };
477
- }, [routes]);
532
+
533
+ void load();
534
+ }, [graphQLOptions]);
478
535
 
479
536
  return { stats, isLoading, error };
480
537
  }
@@ -1,9 +1,18 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from 'react';
2
2
  import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
3
- import type { Location, Tracking, TrackingAlert } from '../types';
3
+ import type { Location, Order, Tracking, TrackingAlert, Driver, Vehicle } from '../types';
4
4
  import { executeGraphQL } from '../utils/graphqlClient';
5
- import { useDriver, useDrivers, useVehicle, useVehicles } from './useFleet';
6
- import { useOrder, useOrders } from './useOrders';
5
+ import {
6
+ mapDriver,
7
+ mapVehicle,
8
+ type ApiDriver,
9
+ type ApiVehicle,
10
+ useDriver,
11
+ useVehicle,
12
+ useVehicles,
13
+ useDrivers,
14
+ } from './useFleet';
15
+ import { mapOrder, type ApiOrder, useOrder } from './useOrders';
7
16
 
8
17
  interface UseTrackingOptions {
9
18
  entityId?: string;
@@ -36,6 +45,23 @@ interface ApiShipment {
36
45
  trackingEvents?: ApiTrackingEvent[];
37
46
  }
38
47
 
48
+ interface ApiActiveDelivery extends ApiShipment {
49
+ deliveryJob?:
50
+ | (ApiOrder & {
51
+ vehicle?: ApiVehicle | null;
52
+ driver?: ApiDriver | null;
53
+ })
54
+ | null;
55
+ }
56
+
57
+ interface LiveDelivery {
58
+ order: Order;
59
+ driver: Driver | null;
60
+ vehicle: Vehicle | null;
61
+ tracking: Tracking | null;
62
+ estimatedArrival: Date | undefined;
63
+ }
64
+
39
65
  const SHIPMENTS_QUERY = `
40
66
  query ListShipments($filter: ShipmentFilterInput, $pagination: PaginationInput) {
41
67
  shipments(filter: $filter, pagination: $pagination) {
@@ -48,29 +74,9 @@ const SHIPMENTS_QUERY = `
48
74
  }
49
75
  `;
50
76
 
51
- const SHIPMENT_QUERY = `
52
- query GetShipment($id: ID!) {
53
- shipment(id: $id) {
54
- id
55
- shipmentNumber
56
- deliveryJobId
57
- status
58
- estimatedDelivery
59
- trackingEvents {
60
- id
61
- shipmentId
62
- eventType
63
- location
64
- details
65
- timestamp
66
- }
67
- }
68
- }
69
- `;
70
-
71
77
  const DELIVERY_JOB_TRACKING_QUERY = `
72
- query GetDeliveryJobTracking($deliveryJobId: ID!) {
73
- deliveryJobTracking(deliveryJobId: $deliveryJobId) {
78
+ query GetDeliveryJobTracking($deliveryJobId: ID!, $pagination: PaginationInput) {
79
+ deliveryJobTracking(deliveryJobId: $deliveryJobId, pagination: $pagination) {
74
80
  id
75
81
  shipmentId
76
82
  eventType
@@ -82,13 +88,81 @@ const DELIVERY_JOB_TRACKING_QUERY = `
82
88
  `;
83
89
 
84
90
  const ACTIVE_DELIVERIES_QUERY = `
85
- query GetActiveDeliveries {
86
- activeDeliveries {
91
+ query GetActiveDeliveries($pagination: PaginationInput) {
92
+ activeDeliveries(pagination: $pagination) {
87
93
  id
88
94
  shipmentNumber
89
95
  deliveryJobId
90
96
  status
91
97
  estimatedDelivery
98
+ trackingEvents(pagination: { limit: 1, offset: 0 }) {
99
+ id
100
+ shipmentId
101
+ eventType
102
+ location
103
+ details
104
+ timestamp
105
+ }
106
+ deliveryJob {
107
+ id
108
+ deliveryJobNumber
109
+ customerId
110
+ status
111
+ priority
112
+ pickupAddress
113
+ deliveryAddress
114
+ cargoItems
115
+ pricing
116
+ trackingNumber
117
+ specialInstructions
118
+ scheduledDelivery
119
+ vehicleId
120
+ driverId
121
+ createdAt
122
+ updatedAt
123
+ customer {
124
+ name
125
+ email
126
+ phone
127
+ company
128
+ }
129
+ vehicle {
130
+ id
131
+ vehicleNumber
132
+ type
133
+ make
134
+ model
135
+ licensePlate
136
+ ownershipType
137
+ year
138
+ capacity
139
+ status
140
+ currentLocation
141
+ fuelLevel
142
+ mileage
143
+ lastServiceDate
144
+ nextServiceDueDate
145
+ nextServiceDueMileage
146
+ createdAt
147
+ }
148
+ driver {
149
+ id
150
+ userId
151
+ firstName
152
+ lastName
153
+ email
154
+ phone
155
+ employeeId
156
+ licenseNumber
157
+ licenseClass
158
+ licenseExpiry
159
+ status
160
+ currentLocation
161
+ rating
162
+ vehicleId
163
+ createdAt
164
+ }
165
+ }
92
166
  }
93
167
  }
94
168
  `;
@@ -157,14 +231,21 @@ export function useTracking(options: UseTrackingOptions = {}): UseTrackingResult
157
231
  graphQLOptions,
158
232
  SHIPMENTS_QUERY,
159
233
  {
160
- pagination: { limit: 200, offset: 0 },
234
+ filter: entityId
235
+ ? {
236
+ ...(entityType === 'order' ? { deliveryJobId: entityId } : {}),
237
+ ...(entityType === 'vehicle' ? { vehicleId: entityId } : {}),
238
+ ...(entityType === 'driver' ? { driverId: entityId } : {}),
239
+ }
240
+ : undefined,
241
+ pagination: { limit: 50, offset: 0 },
161
242
  }
162
243
  );
163
244
 
164
245
  const mapped = data.shipments.map((shipment) => ({
165
246
  id: shipment.id,
166
- entityId: shipment.deliveryJobId ?? shipment.id,
167
- entityType: 'order' as const,
247
+ entityId: entityId ?? shipment.deliveryJobId ?? shipment.id,
248
+ entityType: entityType ?? 'order',
168
249
  currentLocation: {
169
250
  latitude: 0,
170
251
  longitude: 0,
@@ -180,13 +261,7 @@ export function useTracking(options: UseTrackingOptions = {}): UseTrackingResult
180
261
  lastUpdated: new Date(),
181
262
  }));
182
263
 
183
- const filtered = mapped.filter((item) => {
184
- if (entityId && item.entityId !== entityId) return false;
185
- if (entityType && item.entityType !== entityType) return false;
186
- return true;
187
- });
188
-
189
- setTracking(filtered);
264
+ setTracking(mapped);
190
265
  } catch (fetchError) {
191
266
  setError(
192
267
  fetchError instanceof Error ? fetchError : new Error('Failed to load tracking data')
@@ -229,9 +304,10 @@ export function useOrderTracking(orderId: string | undefined) {
229
304
  DELIVERY_JOB_TRACKING_QUERY,
230
305
  {
231
306
  deliveryJobId: orderId,
307
+ pagination: { limit: 1, offset: 0 },
232
308
  }
233
309
  );
234
- const latest = data.deliveryJobTracking[data.deliveryJobTracking.length - 1];
310
+ const latest = data.deliveryJobTracking[0];
235
311
  setTracking(latest ? mapTrackingEventToTracking(latest, orderId) : null);
236
312
  } catch (fetchError) {
237
313
  setError(
@@ -291,18 +367,7 @@ export function useDriverTracking(driverId: string | undefined) {
291
367
 
292
368
  export function useLiveTracking() {
293
369
  const graphQLOptions = useGraphqlOptions();
294
- const { orders } = useOrders();
295
- const { vehicles } = useVehicles();
296
- const { drivers } = useDrivers();
297
- const [activeDeliveries, setActiveDeliveries] = useState<
298
- Array<{
299
- order: (typeof orders)[number];
300
- driver: (typeof drivers)[number] | null;
301
- vehicle: (typeof vehicles)[number] | null;
302
- tracking: Tracking | null;
303
- estimatedArrival: Date | undefined;
304
- }>
305
- >([]);
370
+ const [activeDeliveries, setActiveDeliveries] = useState<LiveDelivery[]>([]);
306
371
  const [isLoading, setIsLoading] = useState(false);
307
372
  const [error, setError] = useState<Error | null>(null);
308
373
 
@@ -310,64 +375,31 @@ export function useLiveTracking() {
310
375
  setIsLoading(true);
311
376
  setError(null);
312
377
  try {
313
- const data = await executeGraphQL<{ activeDeliveries: ApiShipment[] }>(
378
+ const data = await executeGraphQL<{ activeDeliveries: ApiActiveDelivery[] }>(
314
379
  graphQLOptions,
315
- ACTIVE_DELIVERIES_QUERY
380
+ ACTIVE_DELIVERIES_QUERY,
381
+ { pagination: { limit: 100, offset: 0 } }
316
382
  );
317
383
 
318
- const list = await Promise.all(
319
- data.activeDeliveries.map(async (shipment) => {
320
- const shipmentData = await executeGraphQL<{ shipment: ApiShipment | null }>(
321
- graphQLOptions,
322
- SHIPMENT_QUERY,
323
- {
324
- id: shipment.id,
325
- }
326
- );
327
-
328
- const shipmentEvents = shipmentData.shipment?.trackingEvents ?? [];
329
- const latestEvent = shipmentEvents[shipmentEvents.length - 1];
330
- const order = orders.find((item) => item.id === shipment.deliveryJobId);
331
- if (!order) {
332
- return null;
333
- }
334
-
335
- const driver = order.driverId
336
- ? (drivers.find(
337
- (item) => item.id === order.driverId || item.userId === order.driverId
338
- ) ?? null)
339
- : null;
340
- const vehicle = order.vehicleId
341
- ? (vehicles.find((item) => item.id === order.vehicleId) ?? null)
342
- : null;
384
+ const list = (data.activeDeliveries ?? [])
385
+ .map((shipment): LiveDelivery | null => {
386
+ const deliveryJob = shipment.deliveryJob;
387
+ if (!deliveryJob) return null;
343
388
 
389
+ const latestEvent = shipment.trackingEvents?.[0];
344
390
  return {
345
- order,
346
- driver,
347
- vehicle,
348
- tracking: latestEvent
349
- ? mapTrackingEventToTracking(latestEvent, shipment.deliveryJobId ?? shipment.id)
350
- : null,
391
+ order: mapOrder(deliveryJob),
392
+ driver: deliveryJob.driver ? mapDriver(deliveryJob.driver) : null,
393
+ vehicle: deliveryJob.vehicle ? mapVehicle(deliveryJob.vehicle) : null,
394
+ tracking: latestEvent ? mapTrackingEventToTracking(latestEvent, deliveryJob.id) : null,
351
395
  estimatedArrival: shipment.estimatedDelivery
352
396
  ? new Date(shipment.estimatedDelivery)
353
397
  : undefined,
354
398
  };
355
399
  })
356
- );
400
+ .filter((item): item is LiveDelivery => item !== null);
357
401
 
358
- setActiveDeliveries(
359
- list.filter(
360
- (
361
- item
362
- ): item is {
363
- order: (typeof orders)[number];
364
- driver: (typeof drivers)[number] | null;
365
- vehicle: (typeof vehicles)[number] | null;
366
- tracking: Tracking | null;
367
- estimatedArrival: Date | undefined;
368
- } => item !== null
369
- )
370
- );
402
+ setActiveDeliveries(list);
371
403
  } catch (fetchError) {
372
404
  setError(
373
405
  fetchError instanceof Error ? fetchError : new Error('Failed to load live tracking')
@@ -375,7 +407,7 @@ export function useLiveTracking() {
375
407
  } finally {
376
408
  setIsLoading(false);
377
409
  }
378
- }, [drivers, graphQLOptions, orders, vehicles]);
410
+ }, [graphQLOptions]);
379
411
 
380
412
  useEffect(() => {
381
413
  void load();