@burdenoff/microfe-movethewheels 2026.920.3 → 2026.923.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 (79) hide show
  1. package/dist/components/CarbonFootprintPanel.js +1 -1
  2. package/dist/components/CustomerPicker.js +76 -65
  3. package/dist/components/DataTable.js +140 -139
  4. package/dist/components/DocumentsCard.js +14 -8
  5. package/dist/generated/wspace-types.d.ts +2719 -8
  6. package/dist/hooks/index.js +1 -1
  7. package/dist/hooks/useAnalytics.js +21 -1
  8. package/dist/hooks/useCustomers.js +94 -78
  9. package/dist/hooks/useDebouncedValue.js +11 -0
  10. package/dist/hooks/useEntityDocuments.js +28 -18
  11. package/dist/hooks/useEpod.js +24 -17
  12. package/dist/hooks/useFleet.js +148 -125
  13. package/dist/hooks/useInventory.js +255 -186
  14. package/dist/hooks/useMultiModalShipments.js +35 -27
  15. package/dist/hooks/useOrders.js +139 -134
  16. package/dist/hooks/useRoutes.js +97 -75
  17. package/dist/hooks/useTracking.js +100 -88
  18. package/dist/index.js +6 -6
  19. package/dist/movethewheels/components/CustomerPicker.d.ts +3 -1
  20. package/dist/movethewheels/components/DataTable.d.ts +5 -1
  21. package/dist/movethewheels/hooks/useCustomers.d.ts +0 -1
  22. package/dist/movethewheels/hooks/useDebouncedValue.d.ts +1 -0
  23. package/dist/movethewheels/hooks/useEntityDocuments.d.ts +2 -0
  24. package/dist/movethewheels/hooks/useEpod.d.ts +6 -1
  25. package/dist/movethewheels/hooks/useFleet.d.ts +49 -5
  26. package/dist/movethewheels/hooks/useInventory.d.ts +24 -17
  27. package/dist/movethewheels/hooks/useMultiModalShipments.d.ts +6 -1
  28. package/dist/movethewheels/hooks/useOrders.d.ts +48 -5
  29. package/dist/movethewheels/hooks/useRoutes.d.ts +18 -1
  30. package/dist/movethewheels/hooks/useTracking.d.ts +12 -11
  31. package/dist/movethewheels/types/index.d.ts +12 -5
  32. package/dist/pages/CreateOrderPage.js +191 -129
  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 +190 -106
  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 +324 -293
  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/package.json +1 -1
  47. package/src/generated/wspace-types.ts +3136 -8
  48. package/src/movethewheels/components/CarbonFootprintPanel.tsx +0 -1
  49. package/src/movethewheels/components/CustomerPicker.tsx +14 -0
  50. package/src/movethewheels/components/DataTable.tsx +34 -18
  51. package/src/movethewheels/components/DocumentsCard.tsx +6 -1
  52. package/src/movethewheels/components/maps/leaflet-setup.ts +1 -8
  53. package/src/movethewheels/hooks/useAnalytics.ts +27 -0
  54. package/src/movethewheels/hooks/useCustomers.ts +64 -24
  55. package/src/movethewheels/hooks/useDebouncedValue.ts +12 -0
  56. package/src/movethewheels/hooks/useEntityDocuments.ts +43 -25
  57. package/src/movethewheels/hooks/useEpod.ts +14 -7
  58. package/src/movethewheels/hooks/useFleet.ts +66 -38
  59. package/src/movethewheels/hooks/useFleetIntelligence.ts +1 -4
  60. package/src/movethewheels/hooks/useInventory.ts +211 -90
  61. package/src/movethewheels/hooks/useMultiModalShipments.ts +15 -11
  62. package/src/movethewheels/hooks/useOrders.ts +48 -14
  63. package/src/movethewheels/hooks/useRoutes.ts +81 -24
  64. package/src/movethewheels/hooks/useTracking.ts +130 -98
  65. package/src/movethewheels/pages/CreateOrderPage.tsx +93 -2
  66. package/src/movethewheels/pages/CustomersPage.tsx +11 -1
  67. package/src/movethewheels/pages/DeliveryCapturePage.tsx +31 -1
  68. package/src/movethewheels/pages/DriversPage.tsx +24 -12
  69. package/src/movethewheels/pages/InventoryPage.tsx +57 -32
  70. package/src/movethewheels/pages/MultiModalShipmentPage.tsx +52 -3
  71. package/src/movethewheels/pages/OrderDetailsPage.tsx +91 -6
  72. package/src/movethewheels/pages/OrdersListPage.tsx +20 -3
  73. package/src/movethewheels/pages/ProductDetailsPage.tsx +52 -3
  74. package/src/movethewheels/pages/ProductsPage.tsx +11 -1
  75. package/src/movethewheels/pages/RouteDetailsPage.tsx +45 -5
  76. package/src/movethewheels/pages/RoutesPage.tsx +11 -1
  77. package/src/movethewheels/pages/VehiclesPage.tsx +19 -2
  78. package/src/movethewheels/pages/WarehousesPage.tsx +11 -1
  79. package/src/movethewheels/types/index.ts +12 -5
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from 'react';
2
2
  import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
3
3
  import type { Order, OrderFilter, OrderStatus } from '../types';
4
+ import { mapRoute } from './useRoutes';
4
5
  import { executeGraphQL } from '../utils/graphqlClient';
5
6
 
6
7
  interface UseOrdersOptions {
@@ -23,11 +24,7 @@ export type OrderWriteInput = Omit<Partial<Order>, 'scheduledDelivery'> & {
23
24
  interface UseOrdersResult {
24
25
  orders: Order[];
25
26
  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
- */
27
+ /** True when the most recent fetch has another server page. */
31
28
  hasMore: boolean;
32
29
  isLoading: boolean;
33
30
  error: Error | null;
@@ -42,7 +39,7 @@ interface UseOrdersResult {
42
39
  // The `order` domain was renamed to `delivery_jobs`/MtwDeliveryJob on 2026-07-07 (commit
43
40
  // 7e99ec5); `orderType` was dropped entirely in that rename with no replacement field, so it
44
41
  // is intentionally absent here — see BOFF-4505.
45
- interface ApiOrder {
42
+ export interface ApiOrder {
46
43
  id: string;
47
44
  deliveryJobNumber: string;
48
45
  customerId: string;
@@ -57,6 +54,21 @@ interface ApiOrder {
57
54
  scheduledDelivery?: string | null;
58
55
  vehicleId?: string | null;
59
56
  driverId?: string | null;
57
+ routeId?: string | null;
58
+ route?: {
59
+ id: string;
60
+ name: string;
61
+ status: string;
62
+ vehicleId?: string | null;
63
+ driverId?: string | null;
64
+ waypoints?: unknown;
65
+ estimatedDistance?: number | null;
66
+ estimatedDuration?: number | null;
67
+ fuelCost?: number | null;
68
+ actualDistance?: number | null;
69
+ actualDuration?: number | null;
70
+ createdAt: string;
71
+ } | null;
60
72
  createdAt: string;
61
73
  updatedAt: string;
62
74
  customer?: {
@@ -89,6 +101,21 @@ const DELIVERY_JOB_FIELDS = `
89
101
  scheduledDelivery
90
102
  vehicleId
91
103
  driverId
104
+ routeId
105
+ route {
106
+ id
107
+ name
108
+ status
109
+ vehicleId
110
+ driverId
111
+ waypoints
112
+ estimatedDistance
113
+ estimatedDuration
114
+ fuelCost
115
+ actualDistance
116
+ actualDuration
117
+ createdAt
118
+ }
92
119
  createdAt
93
120
  updatedAt
94
121
  customer {
@@ -111,6 +138,7 @@ const LIST_ORDERS_QUERY = `
111
138
  deliveryJobs(filter: $filter, pagination: $pagination) {
112
139
  ${DELIVERY_JOB_FIELDS}
113
140
  }
141
+ deliveryJobsCount(filter: $filter)
114
142
  }
115
143
  `;
116
144
 
@@ -266,7 +294,7 @@ function mapAddress(address: Record<string, unknown> | null | undefined): Order[
266
294
  } as Order['pickupAddress'];
267
295
  }
268
296
 
269
- function mapOrder(order: ApiOrder): Order {
297
+ export function mapOrder(order: ApiOrder): Order {
270
298
  return {
271
299
  id: order.id,
272
300
  orderNumber: order.deliveryJobNumber,
@@ -305,6 +333,8 @@ function mapOrder(order: ApiOrder): Order {
305
333
  scheduledDelivery: order.scheduledDelivery ? new Date(order.scheduledDelivery) : undefined,
306
334
  vehicleId: order.vehicleId ?? undefined,
307
335
  driverId: order.driverId ?? undefined,
336
+ routeId: order.routeId ?? undefined,
337
+ route: order.route ? mapRoute(order.route) : undefined,
308
338
  createdAt: new Date(order.createdAt),
309
339
  updatedAt: new Date(order.updatedAt),
310
340
  };
@@ -324,6 +354,7 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
324
354
  rawFilters?.priority,
325
355
  rawFilters?.customerId,
326
356
  rawFilters?.driverId,
357
+ rawFilters?.search,
327
358
  rawFilters?.unassigned,
328
359
  ]
329
360
  );
@@ -331,6 +362,7 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
331
362
  useMoveTheWheels();
332
363
 
333
364
  const [orders, setOrders] = useState<Order[]>([]);
365
+ const [totalOrders, setTotalOrders] = useState(0);
334
366
  const [hasMore, setHasMore] = useState(false);
335
367
  const [isLoading, setIsLoading] = useState(false);
336
368
  const [error, setError] = useState<Error | null>(null);
@@ -353,6 +385,7 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
353
385
  try {
354
386
  const data = await executeGraphQL<{
355
387
  deliveryJobs: ApiOrder[];
388
+ deliveryJobsCount: number;
356
389
  }>(graphQLOptions, LIST_ORDERS_QUERY, {
357
390
  filter: {
358
391
  status: filters?.status ? toApiStatus(filters.status) : undefined,
@@ -361,17 +394,16 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
361
394
  : undefined,
362
395
  customerId: filters?.customerId,
363
396
  driverId: filters?.driverId,
397
+ unassigned: filters?.unassigned,
398
+ search: filters?.search,
364
399
  },
365
400
  pagination: { limit, offset },
366
401
  });
367
402
 
368
403
  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);
404
+ setTotalOrders(data.deliveryJobsCount);
405
+ setHasMore(offset + raw.length < data.deliveryJobsCount);
406
+ setOrders(raw.map(mapOrder));
375
407
  } catch (fetchError) {
376
408
  setError(fetchError instanceof Error ? fetchError : new Error('Failed to load orders'));
377
409
  } finally {
@@ -410,6 +442,7 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
410
442
  scheduledDelivery: orderData.scheduledDelivery?.toISOString(),
411
443
  vehicleId: orderData.vehicleId,
412
444
  driverId: orderData.driverId,
445
+ routeId: orderData.routeId,
413
446
  },
414
447
  });
415
448
 
@@ -441,6 +474,7 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
441
474
  updates.scheduledDelivery === null ? null : updates.scheduledDelivery?.toISOString(),
442
475
  vehicleId: updates.vehicleId,
443
476
  driverId: updates.driverId,
477
+ routeId: updates.routeId,
444
478
  },
445
479
  });
446
480
 
@@ -481,7 +515,7 @@ export function useOrders(options: UseOrdersOptions = {}): UseOrdersResult {
481
515
 
482
516
  return {
483
517
  orders,
484
- totalOrders: orders.length,
518
+ totalOrders,
485
519
  hasMore,
486
520
  isLoading,
487
521
  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
 
@@ -216,7 +218,7 @@ function mapWaypoints(waypoints: unknown): RouteStop[] {
216
218
  });
217
219
  }
218
220
 
219
- function mapRoute(route: ApiRoute): Route {
221
+ export function mapRoute(route: ApiRoute): Route {
220
222
  return {
221
223
  id: route.id,
222
224
  name: route.name,
@@ -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();