@burdenoff/microfe-movethewheels 2026.925.2 → 2026.925.3

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 (38) hide show
  1. package/dist/components/StatusBadge.js +30 -30
  2. package/dist/constants/index.js +12 -5
  3. package/dist/hooks/useCustomers.js +0 -3
  4. package/dist/hooks/useFleet.js +144 -131
  5. package/dist/hooks/useInventory.js +144 -145
  6. package/dist/hooks/useRoutes.js +5 -14
  7. package/dist/movethewheels/constants/index.d.ts +1 -0
  8. package/dist/movethewheels/hooks/useFleet.d.ts +13 -1
  9. package/dist/movethewheels/hooks/useInventory.d.ts +10 -0
  10. package/dist/movethewheels/types/index.d.ts +13 -70
  11. package/dist/pages/CustomerDetailsPage.js +666 -344
  12. package/dist/pages/CustomersPage.js +71 -80
  13. package/dist/pages/FleetPage.js +1 -3
  14. package/dist/pages/InventoryPage.js +4 -4
  15. package/dist/pages/RouteDetailsPage.js +273 -248
  16. package/dist/pages/RoutesPage.js +130 -67
  17. package/dist/pages/VehicleDetailsPage.js +506 -391
  18. package/dist/pages/VehiclesPage.js +119 -70
  19. package/dist/pages/WarehouseDetailsPage.js +410 -370
  20. package/dist/pages/WarehousesPage.js +131 -82
  21. package/package.json +1 -1
  22. package/src/movethewheels/components/StatusBadge.tsx +8 -3
  23. package/src/movethewheels/constants/index.ts +9 -0
  24. package/src/movethewheels/hooks/useCustomers.ts +0 -3
  25. package/src/movethewheels/hooks/useFleet.ts +74 -19
  26. package/src/movethewheels/hooks/useInventory.ts +76 -32
  27. package/src/movethewheels/hooks/useRoutes.ts +17 -12
  28. package/src/movethewheels/pages/CustomerDetailsPage.tsx +376 -109
  29. package/src/movethewheels/pages/CustomersPage.tsx +6 -15
  30. package/src/movethewheels/pages/FleetPage.tsx +2 -1
  31. package/src/movethewheels/pages/InventoryPage.tsx +14 -5
  32. package/src/movethewheels/pages/RouteDetailsPage.tsx +74 -34
  33. package/src/movethewheels/pages/RoutesPage.tsx +66 -2
  34. package/src/movethewheels/pages/VehicleDetailsPage.tsx +151 -11
  35. package/src/movethewheels/pages/VehiclesPage.tsx +68 -3
  36. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +213 -135
  37. package/src/movethewheels/pages/WarehousesPage.tsx +77 -16
  38. package/src/movethewheels/types/index.ts +14 -70
@@ -30,7 +30,11 @@ export interface CreateWarehouseInput {
30
30
  postalCode: string;
31
31
  country: string;
32
32
  };
33
+ /** Free-text on the backend, e.g. "ACTIVE", "INACTIVE". Defaults to ACTIVE. */
34
+ status?: string;
35
+ contactInfo?: { email?: string; phone?: string };
33
36
  totalCapacity?: number;
37
+ usedCapacity?: number;
34
38
  }
35
39
 
36
40
  interface UseInventoryResult {
@@ -54,6 +58,7 @@ interface UseInventoryResult {
54
58
  createWarehouse: (input: CreateWarehouseInput) => Promise<Warehouse>;
55
59
  updateProduct: (id: string, input: CreateProductInput) => Promise<Product>;
56
60
  updateWarehouse: (id: string, input: CreateWarehouseInput) => Promise<Warehouse>;
61
+ deleteWarehouse: (id: string) => Promise<void>;
57
62
  }
58
63
 
59
64
  // Wire shape of MtwInventoryItem (wspace-movethewheels-svc:
@@ -100,6 +105,9 @@ interface ApiWarehouse {
100
105
  code: string;
101
106
  type: string;
102
107
  address: Record<string, unknown>;
108
+ status: string;
109
+ contactInfo?: Record<string, unknown> | null;
110
+ operatingHours?: Record<string, unknown> | null;
103
111
  totalCapacity?: number | null;
104
112
  usedCapacity?: number | null;
105
113
  createdAt: string;
@@ -163,6 +171,9 @@ const LIST_WAREHOUSES_QUERY = `
163
171
  code
164
172
  type
165
173
  address
174
+ status
175
+ contactInfo
176
+ operatingHours
166
177
  totalCapacity
167
178
  usedCapacity
168
179
  createdAt
@@ -200,6 +211,9 @@ const GET_WAREHOUSE_QUERY = `
200
211
  code
201
212
  type
202
213
  address
214
+ status
215
+ contactInfo
216
+ operatingHours
203
217
  totalCapacity
204
218
  usedCapacity
205
219
  createdAt
@@ -293,6 +307,9 @@ const CREATE_WAREHOUSE_MUTATION = `
293
307
  code
294
308
  type
295
309
  address
310
+ status
311
+ contactInfo
312
+ operatingHours
296
313
  totalCapacity
297
314
  usedCapacity
298
315
  createdAt
@@ -300,6 +317,12 @@ const CREATE_WAREHOUSE_MUTATION = `
300
317
  }
301
318
  `;
302
319
 
320
+ const DELETE_WAREHOUSE_MUTATION = `
321
+ mutation DeleteWarehouse($id: ID!) {
322
+ deleteWarehouse(id: $id)
323
+ }
324
+ `;
325
+
303
326
  const UPDATE_WAREHOUSE_MUTATION = `
304
327
  mutation UpdateWarehouse($id: ID!, $input: UpdateWarehouseInput!) {
305
328
  updateWarehouse(id: $id, input: $input) {
@@ -308,6 +331,9 @@ const UPDATE_WAREHOUSE_MUTATION = `
308
331
  code
309
332
  type
310
333
  address
334
+ status
335
+ contactInfo
336
+ operatingHours
311
337
  totalCapacity
312
338
  usedCapacity
313
339
  createdAt
@@ -363,6 +389,16 @@ function mapProduct(product: ApiProduct): Product {
363
389
  function mapWarehouse(warehouse: ApiWarehouse): Warehouse {
364
390
  const totalSpace = warehouse.totalCapacity ?? 0;
365
391
  const usedSpace = warehouse.usedCapacity ?? 0;
392
+ const operatingHoursRaw = warehouse.operatingHours ?? {};
393
+ const operatingHours: Warehouse['operatingHours'] = {};
394
+ for (const [day, hours] of Object.entries(operatingHoursRaw)) {
395
+ if (hours && typeof hours === 'object' && 'open' in hours && 'close' in hours) {
396
+ operatingHours[day] = { open: String(hours.open), close: String(hours.close) };
397
+ } else {
398
+ operatingHours[day] = null;
399
+ }
400
+ }
401
+ const contactInfoRaw = warehouse.contactInfo ?? undefined;
366
402
  return {
367
403
  id: warehouse.id,
368
404
  name: warehouse.name,
@@ -376,28 +412,19 @@ function mapWarehouse(warehouse: ApiWarehouse): Warehouse {
376
412
  ...(warehouse.address ?? {}),
377
413
  },
378
414
  type: (warehouse.type?.toLowerCase() ?? 'distribution') as Warehouse['type'],
415
+ status: warehouse.status ?? 'ACTIVE',
379
416
  capacity: {
380
417
  totalSpace,
381
418
  usedSpace,
382
- totalPallets: 0,
383
- usedPallets: 0,
384
- specialAreas: [],
385
- },
386
- operatingHours: {},
387
- contacts: [],
388
- zones: [],
389
- equipment: [],
390
- staff: [],
391
- metrics: {
392
- throughput: 0,
393
- accuracy: 0,
394
- productivity: 0,
395
- utilization: totalSpace > 0 ? (usedSpace / totalSpace) * 100 : 0,
396
- ordersFulfilled: 0,
397
- averageProcessingTime: 0,
398
- lastUpdated: new Date(),
399
419
  },
400
- isActive: true,
420
+ operatingHours,
421
+ contactInfo: contactInfoRaw
422
+ ? {
423
+ email: (contactInfoRaw.email as string | undefined) ?? undefined,
424
+ phone: (contactInfoRaw.phone as string | undefined) ?? undefined,
425
+ }
426
+ : undefined,
427
+ utilization: totalSpace > 0 ? (usedSpace / totalSpace) * 100 : 0,
401
428
  createdAt: new Date(warehouse.createdAt),
402
429
  };
403
430
  }
@@ -410,20 +437,12 @@ function mapInventory(item: ApiInventoryItem): Inventory {
410
437
  productSku: item.cargoItem?.sku,
411
438
  warehouseId: item.warehouseId,
412
439
  warehouseName: item.warehouse?.name,
413
- zoneId: '',
414
440
  quantity: item.onHand,
415
441
  reservedQuantity: item.allocated,
416
442
  availableQuantity: item.available,
417
- reorderLevel: item.reorderPoint ?? 0,
418
- maxLevel: Math.max(item.onHand * 2, item.available),
443
+ damagedQuantity: item.damaged,
444
+ reorderLevel: item.reorderPoint ?? undefined,
419
445
  lastRestocked: new Date(item.updatedAt),
420
- location: {
421
- aisle: '',
422
- shelf: '',
423
- position: '',
424
- level: 0,
425
- },
426
- movements: [],
427
446
  createdAt: new Date(item.createdAt),
428
447
  updatedAt: new Date(item.updatedAt),
429
448
  };
@@ -541,7 +560,11 @@ export function useInventory(options: UseInventoryOptions = {}): UseInventoryRes
541
560
  onHand: current.quantity + quantity,
542
561
  available: current.availableQuantity + quantity,
543
562
  allocated: current.reservedQuantity,
544
- damaged: 0,
563
+ // Pass the current value through — the resolver spreads this input
564
+ // straight into a Prisma update, so hardcoding 0 here would
565
+ // silently zero out any real damaged-unit count on every
566
+ // adjustment (BOFF-7909 follow-up).
567
+ damaged: current.damagedQuantity,
545
568
  },
546
569
  }
547
570
  );
@@ -614,7 +637,10 @@ export function useInventory(options: UseInventoryOptions = {}): UseInventoryRes
614
637
  name: input.name,
615
638
  type: input.type,
616
639
  address: input.address,
640
+ status: input.status,
641
+ contactInfo: input.contactInfo,
617
642
  totalCapacity: input.totalCapacity,
643
+ usedCapacity: input.usedCapacity,
618
644
  },
619
645
  }
620
646
  );
@@ -660,7 +686,10 @@ export function useInventory(options: UseInventoryOptions = {}): UseInventoryRes
660
686
  name: input.name,
661
687
  type: input.type,
662
688
  address: input.address,
689
+ status: input.status,
690
+ contactInfo: input.contactInfo,
663
691
  totalCapacity: input.totalCapacity,
692
+ usedCapacity: input.usedCapacity,
664
693
  },
665
694
  }
666
695
  );
@@ -671,6 +700,18 @@ export function useInventory(options: UseInventoryOptions = {}): UseInventoryRes
671
700
  [graphQLOptions]
672
701
  );
673
702
 
703
+ const deleteWarehouse = useCallback(
704
+ async (id: string): Promise<void> => {
705
+ await executeGraphQL<{ deleteWarehouse: boolean }>(
706
+ graphQLOptions,
707
+ DELETE_WAREHOUSE_MUTATION,
708
+ { id }
709
+ );
710
+ setWarehouses((previous) => previous.filter((item) => item.id !== id));
711
+ },
712
+ [graphQLOptions]
713
+ );
714
+
674
715
  return {
675
716
  inventory,
676
717
  lowStockItems,
@@ -687,6 +728,7 @@ export function useInventory(options: UseInventoryOptions = {}): UseInventoryRes
687
728
  createWarehouse,
688
729
  updateProduct,
689
730
  updateWarehouse,
731
+ deleteWarehouse,
690
732
  };
691
733
  }
692
734
 
@@ -702,6 +744,8 @@ interface UseProductsOptions extends UseListOptions {
702
744
 
703
745
  interface UseWarehousesOptions extends UseListOptions {
704
746
  search?: string;
747
+ /** Raw backend value, e.g. "ACTIVE"/"INACTIVE". */
748
+ status?: string;
705
749
  }
706
750
 
707
751
  export function useProducts(options: UseProductsOptions = {}) {
@@ -826,7 +870,7 @@ export function useProduct(productId: string | undefined) {
826
870
  }
827
871
 
828
872
  export function useWarehouses(options: UseWarehousesOptions = {}) {
829
- const { limit = 20, offset = 0, search } = options;
873
+ const { limit = 20, offset = 0, search, status } = options;
830
874
  const graphQLOptions = useGraphqlOptions();
831
875
  const [warehouses, setWarehouses] = useState<Warehouse[]>([]);
832
876
  const [totalWarehouses, setTotalWarehouses] = useState(0);
@@ -842,7 +886,7 @@ export function useWarehouses(options: UseWarehousesOptions = {}) {
842
886
  warehouses: ApiWarehouse[];
843
887
  warehousesCount: number;
844
888
  }>(graphQLOptions, LIST_WAREHOUSES_QUERY, {
845
- filter: { search: search || undefined },
889
+ filter: { search: search || undefined, status: status || undefined },
846
890
  pagination: { limit, offset },
847
891
  });
848
892
  setTotalWarehouses(data.warehousesCount);
@@ -853,7 +897,7 @@ export function useWarehouses(options: UseWarehousesOptions = {}) {
853
897
  } finally {
854
898
  setIsLoading(false);
855
899
  }
856
- }, [graphQLOptions, limit, offset, search]);
900
+ }, [graphQLOptions, limit, offset, search, status]);
857
901
 
858
902
  useEffect(() => {
859
903
  void load();
@@ -226,14 +226,18 @@ export function mapRoute(route: ApiRoute): Route {
226
226
  vehicleId: route.vehicleId ?? '',
227
227
  status: mapRouteStatus(route.status),
228
228
  stops: mapWaypoints(route.waypoints),
229
- distance: route.estimatedDistance ?? 0,
229
+ // Prefer the real post-drive distance once it exists; the backend fetches
230
+ // it (`actualDistance`) but it used to be silently discarded here.
231
+ distance: route.actualDistance ?? route.estimatedDistance ?? 0,
230
232
  estimatedDuration: route.estimatedDuration ?? 0,
231
233
  fuelCost: route.fuelCost ?? undefined,
232
234
  actualDuration: route.actualDuration ?? undefined,
233
- startTime: undefined,
234
- endTime: undefined,
235
- optimized: false,
236
- trafficConditions: [],
235
+ // No `optimized` flag exists on the backend — `optimizeRoute` is the only
236
+ // path that computes a real `estimatedDistance` (create/update never
237
+ // collect one), so a positive value is a reasonably honest proxy. This
238
+ // used to be hardcoded `false` and reset on every reload even for a
239
+ // route that really had been optimized (BOFF-7909 follow-up).
240
+ optimized: (route.estimatedDistance ?? 0) > 0,
237
241
  createdAt: new Date(route.createdAt),
238
242
  };
239
243
  }
@@ -311,11 +315,13 @@ export function useRoutes(options: UseRoutesOptions = {}): UseRoutesResult {
311
315
  vehicleId: route.vehicleId,
312
316
  driverId: route.driverId,
313
317
  waypoints: route.stops ?? [],
314
- estimatedDistance: route.distance ?? 0,
315
- estimatedDuration: route.estimatedDuration ?? 0,
318
+ // The create form never collects a distance — leave estimated/actual
319
+ // unset (not a forced 0) so a fresh route honestly shows as
320
+ // "not yet optimized" until `optimizeRoute` computes a real one.
321
+ // `distance` is never sent as `actualDistance`: that field means a
322
+ // real post-drive distance, which this form has no way to know.
323
+ estimatedDuration: route.estimatedDuration,
316
324
  fuelCost: route.fuelCost,
317
- actualDistance: route.distance,
318
- actualDuration: route.actualDuration,
319
325
  },
320
326
  }
321
327
  );
@@ -341,11 +347,10 @@ export function useRoutes(options: UseRoutesOptions = {}): UseRoutesResult {
341
347
  vehicleId: updates.vehicleId,
342
348
  driverId: updates.driverId,
343
349
  waypoints: updates.stops,
344
- estimatedDistance: updates.distance,
350
+ // See createRoute — the edit form doesn't collect a distance
351
+ // either, and `distance` must never masquerade as `actualDistance`.
345
352
  estimatedDuration: updates.estimatedDuration,
346
353
  fuelCost: updates.fuelCost,
347
- actualDistance: updates.distance,
348
- actualDuration: updates.actualDuration,
349
354
  },
350
355
  }
351
356
  );