@burdenoff/microfe-movethewheels 2026.703.7 → 2026.704.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 (117) hide show
  1. package/dist/{AIAssistantPage-B7FGmmKT.js → AIAssistantPage-MWJLAMN_.js} +5 -5
  2. package/dist/{AnalyticsPage-DXduj5KB.js → AnalyticsPage-900jpV2T.js} +9 -9
  3. package/dist/{CreateOrderPage-DuJJQ16b.js → CreateOrderPage-CXbhJLLz.js} +164 -139
  4. package/dist/{CustomerDetailsPage-C98SKw7Z.js → CustomerDetailsPage-CHzg75Q5.js} +16 -16
  5. package/dist/{CustomersPage-s8hK55JL.js → CustomersPage-CwtVspuq.js} +11 -11
  6. package/dist/{DashboardPage-BsEgcKEN.js → DashboardPage-BoswH3ec.js} +17 -17
  7. package/dist/{DataTable-Da7yHjM1.js → DataTable-CmdZd6iV.js} +7 -7
  8. package/dist/{DocumentsCard-CziaTJhP.js → DocumentsCard-y2ybnAlu.js} +4 -4
  9. package/dist/DriverDetailsPage-CPO0G8Ki.js +518 -0
  10. package/dist/{DriversPage-Duy_ITG6.js → DriversPage-6Z61dpDI.js} +11 -11
  11. package/dist/{FinancePage-BZtPTpmT.js → FinancePage-DYadOUzC.js} +9 -9
  12. package/dist/{FleetPage-DJfzoHbs.js → FleetPage-qWRj7WFO.js} +10 -10
  13. package/dist/{FormError-CWjGwOW8.js → FormError-D4T5FWZA.js} +1 -1
  14. package/dist/{ImportExportPage-DJ77GK_7.js → ImportExportPage-BhWGfbH3.js} +6 -6
  15. package/dist/{InventoryPage-Dpnwk_N3.js → InventoryPage-DbR4Z--d.js} +10 -10
  16. package/dist/{LiveTrackingPage-CLlvsP1B.js → LiveTrackingPage-5U68u3oA.js} +10 -10
  17. package/dist/{MarketplacePage-d99r-v6p.js → MarketplacePage-DMH0HNDH.js} +9 -9
  18. package/dist/{MetricCard-DA5ZqAVl.js → MetricCard-CqU5WsyW.js} +2 -2
  19. package/dist/{Notice-kkjV2rLR.js → Notice-BrXJ85dA.js} +2 -2
  20. package/dist/{OrderDetailsPage-BePtkyN0.js → OrderDetailsPage-DRYifXm9.js} +28 -24
  21. package/dist/{OrdersListPage-Csy_KScx.js → OrdersListPage-DvICveRv.js} +14 -14
  22. package/dist/{PageLayout-h_q9l8af.js → PageLayout-j9Zw3wBN.js} +579 -561
  23. package/dist/ProductDetailsPage-CWgcRqGR.js +375 -0
  24. package/dist/{ProductsPage-B4jWJ5bv.js → ProductsPage-CokwDOR2.js} +9 -9
  25. package/dist/{ReportsPage-ALRYfXtz.js → ReportsPage-MU8F9DQC.js} +9 -9
  26. package/dist/RouteDetailsPage-BHQbFgI9.js +642 -0
  27. package/dist/{RoutesPage-CQHCDInT.js → RoutesPage-Dqd5Don6.js} +7 -7
  28. package/dist/{SettingsPage-BYBRrlGe.js → SettingsPage-_6SIlcBX.js} +4 -4
  29. package/dist/{Sparkline-Q-6bJ24J.js → Sparkline-DTGdlxUH.js} +1 -1
  30. package/dist/{StatusBadge-1W2p8fEi.js → StatusBadge-BfiF-nNI.js} +1 -1
  31. package/dist/{TrackingPage-CFndTIyk.js → TrackingPage-Cg0tYGJe.js} +11 -11
  32. package/dist/VehicleDetailsPage-BbZlCFPo.js +417 -0
  33. package/dist/{VehiclesPage-ClkfFZxL.js → VehiclesPage-BfBEyXGs.js} +12 -12
  34. package/dist/WarehouseDetailsPage-DxNhiaoI.js +515 -0
  35. package/dist/{WarehousesPage-kNba6cr3.js → WarehousesPage-M3NaBT0z.js} +8 -8
  36. package/dist/{arrow-left-XOzFvBFD.js → arrow-left-1dVWv2dg.js} +1 -1
  37. package/dist/{arrow-right-CRrwlvFe.js → arrow-right-PUCNATnk.js} +1 -1
  38. package/dist/{arrow-up-ClFaLPly.js → arrow-up-VQuGMABN.js} +1 -1
  39. package/dist/{box-BX4WyCLv.js → box-DW1KNP8j.js} +1 -1
  40. package/dist/{boxes-R3nq5f0_.js → boxes-D6I00XqA.js} +1 -1
  41. package/dist/{chart-column-BgF8J9bx.js → chart-column-BwQ5H9ol.js} +1 -1
  42. package/dist/{charts-BS_OnWc6.js → charts-Dv_zxP7L.js} +2 -2
  43. package/dist/{chevron-left-D3hcLQ1g.js → chevron-left-DzNxoaBG.js} +1 -1
  44. package/dist/{chevron-right-BpfSrzQF.js → chevron-right-CKm3ZC1T.js} +1 -1
  45. package/dist/{circle-alert-CLqT8iCQ.js → circle-alert-CtBm6oIb.js} +1 -1
  46. package/dist/{circle-check-CWLAr_8f.js → circle-check-B_Y7FDyR.js} +1 -1
  47. package/dist/{circle-check-big-Dsuz8dJ2.js → circle-check-big-H5pLiMBW.js} +1 -1
  48. package/dist/{clock-DrZOQD8h.js → clock-B4-qjGuB.js} +1 -1
  49. package/dist/{dollar-sign-CJfKfYul.js → dollar-sign-CYGHy2oS.js} +1 -1
  50. package/dist/{download-C-yNvUiL.js → download-BJonq2ty.js} +1 -1
  51. package/dist/{file-text-m2SyQgBo.js → file-text-C-cZx58r.js} +1 -1
  52. package/dist/{filter-uA6govE9.js → filter-B_S6WOqr.js} +1 -1
  53. package/dist/{fuel-BroSIBo1.js → fuel-Cjt6IxKr.js} +1 -1
  54. package/dist/index.js +53 -33
  55. package/dist/{map-BzwQ__kD.js → map-CO0aWUae.js} +1 -1
  56. package/dist/{map-pin-BGbmEGn3.js → map-pin-BISLzLwb.js} +1 -1
  57. package/dist/{maps-DpIuSSmg.js → maps-CtfuCAmh.js} +1 -1
  58. package/dist/{navigation-CjKGLL9T.js → navigation-BFki5UJz.js} +1 -1
  59. package/dist/{package-D36cKFvu.js → package-UyaWiALC.js} +1 -1
  60. package/dist/{phone-mMZFHd1Z.js → phone-Bav_V7rR.js} +1 -1
  61. package/dist/{plus-DY5R-c1j.js → plus-Bk_G-lFC.js} +1 -1
  62. package/dist/{refresh-cw-DyOLYuDU.js → refresh-cw-C4-jecjy.js} +1 -1
  63. package/dist/{route-CuaZ1wU3.js → route-WpCP6F1x.js} +1 -1
  64. package/dist/{save-h9dzHwJE.js → save-CcfoIn2J.js} +1 -1
  65. package/dist/{search-stTGSec-.js → search-C3YRxW9e.js} +1 -1
  66. package/dist/{square-pen-DT5srCPD.js → square-pen-Dr1X-P2s.js} +1 -1
  67. package/dist/{star-gp4yO3PF.js → star-1Ixfuzai.js} +1 -1
  68. package/dist/{store-DJZDAvCY.js → store-BwU935v5.js} +1 -1
  69. package/dist/{trash-2-DwcjTxYE.js → trash-2-CC4KugEJ.js} +1 -1
  70. package/dist/{trending-up-2q2sPVsb.js → trending-up-1brglj1E.js} +1 -1
  71. package/dist/{triangle-alert-CU60xaOi.js → triangle-alert-Dh56Yu3-.js} +1 -1
  72. package/dist/{truck-ByZxqZhI.js → truck-DxhtLfkG.js} +1 -1
  73. package/dist/{upload-CWor1uj3.js → upload-CLf0JLXW.js} +1 -1
  74. package/dist/{useAssistant-uoc8wjr0.js → useAssistant-B9qBtRKm.js} +1 -1
  75. package/dist/{useCustomers-BPPhQXSH.js → useCustomers-BQ4AIEAT.js} +1 -1
  76. package/dist/{useFleet-CT_iW2V9.js → useFleet-BfA_-VUW.js} +101 -72
  77. package/dist/{useImportExport-BG0Cg_D9.js → useImportExport-7-O8noRJ.js} +1 -1
  78. package/dist/{useInventory-BtBGnOC2.js → useInventory-BO7TSgq_.js} +99 -73
  79. package/dist/{useOrders-BC-1mCc0.js → useOrders-B5FV0kcz.js} +1 -1
  80. package/dist/{useReports-Cx8blThR.js → useReports-hUXb8ryJ.js} +1 -1
  81. package/dist/{useRoutes-8fw8oO6y.js → useRoutes-C_ZCYJ_h.js} +1 -1
  82. package/dist/{useSettings-Do5WSUaI.js → useSettings-BNNzQs_B.js} +2 -2
  83. package/dist/{useTracking-Dogilo5C.js → useTracking-7nd_XOSL.js} +3 -3
  84. package/dist/{user-Dr_JS2FF.js → user-B1IjWJtn.js} +1 -1
  85. package/dist/{user-check-CBSiMH6I.js → user-check-Cx6fHHll.js} +1 -1
  86. package/dist/{users-BRV9-2nV.js → users-BKmsb79k.js} +1 -1
  87. package/dist/{warehouse-CmIAl0_w.js → warehouse-DE1irFyJ.js} +1 -1
  88. package/dist/{wrench-6U-BTSbg.js → wrench-DTJGFs17.js} +1 -1
  89. package/dist/{x-DX8sZrkd.js → x-CvsUGJy1.js} +1 -1
  90. package/package.json +1 -1
  91. package/src/movethewheels/MoveTheWheelsRoutes.tsx +5 -0
  92. package/src/movethewheels/components/GlassPanel.tsx +1 -3
  93. package/src/movethewheels/components/MetricCard.tsx +16 -3
  94. package/src/movethewheels/components/charts/StatusDonut.tsx +2 -6
  95. package/src/movethewheels/components/charts/TrendChart.tsx +2 -6
  96. package/src/movethewheels/components/maps/LogisticsMap.tsx +1 -4
  97. package/src/movethewheels/components/maps/RouteMap.tsx +3 -5
  98. package/src/movethewheels/components/maps/TrackingMap.tsx +11 -2
  99. package/src/movethewheels/hooks/useFleet.ts +113 -0
  100. package/src/movethewheels/hooks/useInventory.ts +86 -0
  101. package/src/movethewheels/hooks/useSettings.ts +8 -1
  102. package/src/movethewheels/pages/CreateOrderPage.tsx +48 -6
  103. package/src/movethewheels/pages/CustomerDetailsPage.tsx +3 -2
  104. package/src/movethewheels/pages/DriverDetailsPage.tsx +58 -20
  105. package/src/movethewheels/pages/OrderDetailsPage.tsx +2 -1
  106. package/src/movethewheels/pages/ProductDetailsPage.tsx +51 -15
  107. package/src/movethewheels/pages/RouteDetailsPage.tsx +51 -7
  108. package/src/movethewheels/pages/VehicleDetailsPage.tsx +53 -17
  109. package/src/movethewheels/pages/VehiclesPage.tsx +9 -1
  110. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +49 -11
  111. package/src/movethewheels/types/index.ts +3 -0
  112. package/src/movethewheels/utils/graphqlClient.ts +51 -1
  113. package/dist/DriverDetailsPage-CBzAmM6s.js +0 -490
  114. package/dist/ProductDetailsPage-BMlRoR9E.js +0 -350
  115. package/dist/RouteDetailsPage-Btb1MSIS.js +0 -610
  116. package/dist/VehicleDetailsPage-DNLrMljV.js +0 -393
  117. package/dist/WarehouseDetailsPage-CAQFz7l_.js +0 -493
@@ -35,6 +35,8 @@ interface UseFleetResult {
35
35
  refetch: () => void;
36
36
  createVehicle: (input: CreateVehicleInput) => Promise<Vehicle>;
37
37
  createDriver: (input: CreateDriverInput) => Promise<Driver>;
38
+ updateVehicle: (id: string, input: CreateVehicleInput) => Promise<Vehicle>;
39
+ updateDriver: (id: string, input: CreateDriverInput) => Promise<Driver>;
38
40
  }
39
41
 
40
42
  interface ApiVehicle {
@@ -43,6 +45,7 @@ interface ApiVehicle {
43
45
  type: string;
44
46
  make?: string | null;
45
47
  model?: string | null;
48
+ licensePlate?: string | null;
46
49
  year?: number | null;
47
50
  capacity?: Record<string, unknown> | null;
48
51
  status: string;
@@ -58,6 +61,8 @@ interface ApiDriver {
58
61
  userId?: string | null;
59
62
  firstName: string;
60
63
  lastName: string;
64
+ email?: string | null;
65
+ phone?: string | null;
61
66
  employeeId: string;
62
67
  licenseNumber?: string | null;
63
68
  licenseClass?: string | null;
@@ -77,6 +82,7 @@ const LIST_VEHICLES_QUERY = `
77
82
  type
78
83
  make
79
84
  model
85
+ licensePlate
80
86
  year
81
87
  capacity
82
88
  status
@@ -96,6 +102,8 @@ const LIST_DRIVERS_QUERY = `
96
102
  userId
97
103
  firstName
98
104
  lastName
105
+ email
106
+ phone
99
107
  employeeId
100
108
  licenseNumber
101
109
  licenseClass
@@ -117,6 +125,7 @@ const GET_VEHICLE_QUERY = `
117
125
  type
118
126
  make
119
127
  model
128
+ licensePlate
120
129
  year
121
130
  capacity
122
131
  status
@@ -136,6 +145,8 @@ const GET_DRIVER_QUERY = `
136
145
  userId
137
146
  firstName
138
147
  lastName
148
+ email
149
+ phone
139
150
  employeeId
140
151
  licenseNumber
141
152
  licenseClass
@@ -157,6 +168,7 @@ const UPDATE_VEHICLE_STATUS_MUTATION = `
157
168
  type
158
169
  make
159
170
  model
171
+ licensePlate
160
172
  year
161
173
  capacity
162
174
  status
@@ -176,6 +188,8 @@ const ASSIGN_DRIVER_VEHICLE_MUTATION = `
176
188
  userId
177
189
  firstName
178
190
  lastName
191
+ email
192
+ phone
179
193
  employeeId
180
194
  licenseNumber
181
195
  licenseClass
@@ -196,6 +210,8 @@ const UPDATE_DRIVER_STATUS_MUTATION = `
196
210
  userId
197
211
  firstName
198
212
  lastName
213
+ email
214
+ phone
199
215
  employeeId
200
216
  licenseNumber
201
217
  licenseClass
@@ -217,6 +233,7 @@ const CREATE_VEHICLE_MUTATION = `
217
233
  type
218
234
  make
219
235
  model
236
+ licensePlate
220
237
  year
221
238
  capacity
222
239
  status
@@ -236,6 +253,51 @@ const CREATE_DRIVER_MUTATION = `
236
253
  userId
237
254
  firstName
238
255
  lastName
256
+ email
257
+ phone
258
+ employeeId
259
+ licenseNumber
260
+ licenseClass
261
+ licenseExpiry
262
+ status
263
+ currentLocation
264
+ rating
265
+ vehicleId
266
+ createdAt
267
+ }
268
+ }
269
+ `;
270
+
271
+ const UPDATE_VEHICLE_MUTATION = `
272
+ mutation UpdateVehicle($id: ID!, $input: UpdateVehicleInput!) {
273
+ updateVehicle(id: $id, input: $input) {
274
+ id
275
+ vehicleNumber
276
+ type
277
+ make
278
+ model
279
+ licensePlate
280
+ year
281
+ capacity
282
+ status
283
+ currentLocation
284
+ fuelLevel
285
+ mileage
286
+ createdAt
287
+ updatedAt
288
+ }
289
+ }
290
+ `;
291
+
292
+ const UPDATE_DRIVER_MUTATION = `
293
+ mutation UpdateDriver($id: ID!, $input: UpdateDriverInput!) {
294
+ updateDriver(id: $id, input: $input) {
295
+ id
296
+ userId
297
+ firstName
298
+ lastName
299
+ email
300
+ phone
239
301
  employeeId
240
302
  licenseNumber
241
303
  licenseClass
@@ -323,6 +385,7 @@ function mapVehicle(vehicle: ApiVehicle): Vehicle {
323
385
  type: (vehicle.type?.toLowerCase() ?? 'van') as Vehicle['type'],
324
386
  make: vehicle.make ?? '',
325
387
  model: vehicle.model ?? '',
388
+ licensePlate: vehicle.licensePlate ?? '',
326
389
  year: vehicle.year ?? new Date().getFullYear(),
327
390
  capacity: {
328
391
  weight: 0,
@@ -353,6 +416,8 @@ function mapDriver(driver: ApiDriver): Driver {
353
416
  id: driver.id,
354
417
  userId: driver.userId ?? driver.id,
355
418
  fullName: `${driver.firstName} ${driver.lastName}`.trim(),
419
+ email: driver.email ?? '',
420
+ phone: driver.phone ?? '',
356
421
  employeeId: driver.employeeId,
357
422
  licenseNumber: driver.licenseNumber ?? '',
358
423
  licenseType: driver.licenseClass ?? 'standard',
@@ -523,6 +588,52 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
523
588
  [graphQLOptions]
524
589
  );
525
590
 
591
+ const updateVehicle = useCallback(
592
+ async (id: string, input: CreateVehicleInput): Promise<Vehicle> => {
593
+ const data = await executeGraphQL<{ updateVehicle: ApiVehicle }>(
594
+ graphQLOptions,
595
+ UPDATE_VEHICLE_MUTATION,
596
+ {
597
+ id,
598
+ input: {
599
+ vehicleNumber: input.vehicleNumber,
600
+ type: input.type,
601
+ make: input.make,
602
+ model: input.model,
603
+ licensePlate: input.licensePlate,
604
+ },
605
+ }
606
+ );
607
+ const updated = mapVehicle(data.updateVehicle);
608
+ setVehicles((previous) => previous.map((item) => (item.id === id ? updated : item)));
609
+ return updated;
610
+ },
611
+ [graphQLOptions]
612
+ );
613
+
614
+ const updateDriver = useCallback(
615
+ async (id: string, input: CreateDriverInput): Promise<Driver> => {
616
+ const data = await executeGraphQL<{ updateDriver: ApiDriver }>(
617
+ graphQLOptions,
618
+ UPDATE_DRIVER_MUTATION,
619
+ {
620
+ id,
621
+ input: {
622
+ firstName: input.firstName,
623
+ lastName: input.lastName,
624
+ email: input.email,
625
+ phone: input.phone,
626
+ licenseNumber: input.licenseNumber,
627
+ },
628
+ }
629
+ );
630
+ const updated = mapDriver(data.updateDriver);
631
+ setDrivers((previous) => previous.map((item) => (item.id === id ? updated : item)));
632
+ return updated;
633
+ },
634
+ [graphQLOptions]
635
+ );
636
+
526
637
  return {
527
638
  vehicles,
528
639
  drivers,
@@ -533,6 +644,8 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
533
644
  refetch,
534
645
  createVehicle,
535
646
  createDriver,
647
+ updateVehicle,
648
+ updateDriver,
536
649
  };
537
650
  }
538
651
 
@@ -47,6 +47,8 @@ interface UseInventoryResult {
47
47
  ) => Promise<void>;
48
48
  createProduct: (input: CreateProductInput) => Promise<Product>;
49
49
  createWarehouse: (input: CreateWarehouseInput) => Promise<Warehouse>;
50
+ updateProduct: (id: string, input: CreateProductInput) => Promise<Product>;
51
+ updateWarehouse: (id: string, input: CreateWarehouseInput) => Promise<Warehouse>;
50
52
  }
51
53
 
52
54
  interface ApiInventoryItem {
@@ -212,6 +214,27 @@ const CREATE_PRODUCT_MUTATION = `
212
214
  }
213
215
  `;
214
216
 
217
+ const UPDATE_PRODUCT_MUTATION = `
218
+ mutation UpdateProduct($id: ID!, $input: UpdateProductInput!) {
219
+ updateProduct(id: $id, input: $input) {
220
+ id
221
+ sku
222
+ name
223
+ description
224
+ category
225
+ brand
226
+ dimensions
227
+ weight
228
+ value
229
+ hazardous
230
+ fragile
231
+ temperatureControlled
232
+ createdAt
233
+ updatedAt
234
+ }
235
+ }
236
+ `;
237
+
215
238
  const CREATE_WAREHOUSE_MUTATION = `
216
239
  mutation CreateWarehouse($input: CreateWarehouseInput!) {
217
240
  createWarehouse(input: $input) {
@@ -227,6 +250,21 @@ const CREATE_WAREHOUSE_MUTATION = `
227
250
  }
228
251
  `;
229
252
 
253
+ const UPDATE_WAREHOUSE_MUTATION = `
254
+ mutation UpdateWarehouse($id: ID!, $input: UpdateWarehouseInput!) {
255
+ updateWarehouse(id: $id, input: $input) {
256
+ id
257
+ name
258
+ code
259
+ type
260
+ address
261
+ totalCapacity
262
+ usedCapacity
263
+ createdAt
264
+ }
265
+ }
266
+ `;
267
+
230
268
  const ADJUST_INVENTORY_MUTATION = `
231
269
  mutation AdjustInventoryStock($input: AdjustInventoryStockInput!) {
232
270
  adjustInventoryStock(input: $input) {
@@ -519,6 +557,52 @@ export function useInventory(options: UseInventoryOptions = {}): UseInventoryRes
519
557
  [graphQLOptions]
520
558
  );
521
559
 
560
+ const updateProduct = useCallback(
561
+ async (id: string, input: CreateProductInput): Promise<Product> => {
562
+ const data = await executeGraphQL<{ updateProduct: ApiProduct }>(
563
+ graphQLOptions,
564
+ UPDATE_PRODUCT_MUTATION,
565
+ {
566
+ id,
567
+ input: {
568
+ name: input.name,
569
+ sku: input.sku,
570
+ category: input.category,
571
+ brand: input.brand,
572
+ weight: input.weight,
573
+ },
574
+ }
575
+ );
576
+ const updated = mapProduct(data.updateProduct);
577
+ setProducts((previous) => previous.map((item) => (item.id === id ? updated : item)));
578
+ return updated;
579
+ },
580
+ [graphQLOptions]
581
+ );
582
+
583
+ const updateWarehouse = useCallback(
584
+ async (id: string, input: CreateWarehouseInput): Promise<Warehouse> => {
585
+ const data = await executeGraphQL<{ updateWarehouse: ApiWarehouse }>(
586
+ graphQLOptions,
587
+ UPDATE_WAREHOUSE_MUTATION,
588
+ {
589
+ id,
590
+ input: {
591
+ code: input.code,
592
+ name: input.name,
593
+ type: input.type,
594
+ address: input.address,
595
+ totalCapacity: input.totalCapacity,
596
+ },
597
+ }
598
+ );
599
+ const updated = mapWarehouse(data.updateWarehouse);
600
+ setWarehouses((previous) => previous.map((item) => (item.id === id ? updated : item)));
601
+ return updated;
602
+ },
603
+ [graphQLOptions]
604
+ );
605
+
522
606
  return {
523
607
  inventory,
524
608
  products,
@@ -531,6 +615,8 @@ export function useInventory(options: UseInventoryOptions = {}): UseInventoryRes
531
615
  transferStock,
532
616
  createProduct,
533
617
  createWarehouse,
618
+ updateProduct,
619
+ updateWarehouse,
534
620
  };
535
621
  }
536
622
 
@@ -108,9 +108,16 @@ export function useModuleSettings(): UseModuleSettingsResult {
108
108
  [graphQLOptions]
109
109
  );
110
110
 
111
+ // Stabilize the preferences reference. Returning `settings?.preferences ?? {}`
112
+ // inline minted a fresh `{}` on every render while settings was null, which
113
+ // made SettingsPage's `useMemo(toForm, [preferences])` recompute every render
114
+ // and its `useEffect(setForm, [loadedForm])` fire in a loop → "Maximum update
115
+ // depth exceeded" → the page rendered blank behind the shell's error boundary.
116
+ const preferences = useMemo<ModulePreferences>(() => settings?.preferences ?? {}, [settings]);
117
+
111
118
  return {
112
119
  settings,
113
- preferences: settings?.preferences ?? {},
120
+ preferences,
114
121
  isLoading,
115
122
  isSaving,
116
123
  error,
@@ -4,7 +4,7 @@
4
4
  * Form for creating or editing orders.
5
5
  */
6
6
 
7
- import { useState } from 'react';
7
+ import { useEffect, useState } from 'react';
8
8
  import { useParams } from 'react-router-dom';
9
9
  import { Package, ArrowLeft, Save, Plus, Trash2, Users, MapPin } from 'lucide-react';
10
10
  import { PageLayout, EmptyState } from '../components/PageLayout';
@@ -35,7 +35,7 @@ export default function CreateOrderPage() {
35
35
  const { orderId } = useParams<{ orderId: string }>();
36
36
  const { basePath, navigate } = useMoveTheWheels();
37
37
  const { order } = useOrder(orderId);
38
- const { createOrder } = useOrders();
38
+ const { createOrder, updateOrder } = useOrders();
39
39
  const { customers } = useCustomers({ limit: 100 });
40
40
 
41
41
  const isEditing = !!orderId && !!order;
@@ -65,6 +65,36 @@ export default function CreateOrderPage() {
65
65
  const [isSubmitting, setIsSubmitting] = useState(false);
66
66
  const [formError, setFormError] = useState<string | null>(null);
67
67
 
68
+ // Prefill the form from the loaded order when editing. `order` arrives
69
+ // asynchronously after the initial render, so the useState initializers alone
70
+ // leave the fields blank until this syncs them.
71
+ useEffect(() => {
72
+ if (!order) {
73
+ return;
74
+ }
75
+ setFormData({
76
+ customerId: order.customerId || '',
77
+ orderType: order.orderType || 'b2c',
78
+ priority: order.priority || 'normal',
79
+ pickupAddress: {
80
+ street: order.pickupAddress.street || '',
81
+ city: order.pickupAddress.city || '',
82
+ state: order.pickupAddress.state || '',
83
+ postalCode: order.pickupAddress.postalCode || '',
84
+ country: order.pickupAddress.country || 'USA',
85
+ },
86
+ deliveryAddress: {
87
+ street: order.deliveryAddress.street || '',
88
+ city: order.deliveryAddress.city || '',
89
+ state: order.deliveryAddress.state || '',
90
+ postalCode: order.deliveryAddress.postalCode || '',
91
+ country: order.deliveryAddress.country || 'USA',
92
+ },
93
+ items: order.items || [],
94
+ specialInstructions: order.specialInstructions || '',
95
+ });
96
+ }, [order]);
97
+
68
98
  const goBack = () => {
69
99
  if (navigate) {
70
100
  navigate(joinPath(basePath, 'orders'));
@@ -106,7 +136,7 @@ export default function CreateOrderPage() {
106
136
 
107
137
  setIsSubmitting(true);
108
138
  try {
109
- await createOrder({
139
+ const input = {
110
140
  customerId: formData.customerId,
111
141
  customerInfo: {
112
142
  name: selectedCustomer.name,
@@ -120,12 +150,24 @@ export default function CreateOrderPage() {
120
150
  deliveryAddress: formData.deliveryAddress,
121
151
  items: formData.items,
122
152
  specialInstructions: formData.specialInstructions,
123
- });
153
+ };
124
154
 
125
- goBack();
155
+ if (isEditing && orderId) {
156
+ await updateOrder(orderId, input);
157
+ if (navigate) {
158
+ navigate(joinPath(basePath, `orders/${orderId}`));
159
+ }
160
+ } else {
161
+ await createOrder(input);
162
+ goBack();
163
+ }
126
164
  } catch (error) {
127
165
  setFormError(
128
- error instanceof Error ? error.message : 'Could not create the order. Please try again.'
166
+ error instanceof Error
167
+ ? error.message
168
+ : isEditing
169
+ ? 'Could not update the order. Please try again.'
170
+ : 'Could not create the order. Please try again.'
129
171
  );
130
172
  } finally {
131
173
  setIsSubmitting(false);
@@ -116,8 +116,9 @@ function NewCustomerForm() {
116
116
  }
117
117
  >
118
118
  <PagePurpose icon={<Users size={18} />}>
119
- Add a customer once and their contact details auto-fill every future order you book for them.
120
- Only a name is required — add email, phone and company to make deliveries easier to arrange.
119
+ Add a customer once and their contact details auto-fill every future order you book for
120
+ them. Only a name is required — add email, phone and company to make deliveries easier to
121
+ arrange.
121
122
  </PagePurpose>
122
123
 
123
124
  <form onSubmit={handleSubmit} className="max-w-2xl space-y-6">
@@ -2,8 +2,8 @@
2
2
  * Driver Details Page
3
3
  */
4
4
 
5
- import { useState } from 'react';
6
- import { useParams } from 'react-router-dom';
5
+ import { useEffect, useState } from 'react';
6
+ import { useLocation, useParams } from 'react-router-dom';
7
7
  import {
8
8
  Users,
9
9
  ArrowLeft,
@@ -36,9 +36,11 @@ import { joinPath } from '../utils/navigation';
36
36
  import { FormError } from '../components/FormError';
37
37
  import { DocumentsCard } from '../components/DocumentsCard';
38
38
 
39
- function NewDriverForm() {
39
+ function NewDriverForm({ editId }: { editId?: string }) {
40
40
  const { basePath, navigate } = useMoveTheWheels();
41
- const { createDriver } = useFleet();
41
+ const { createDriver, updateDriver } = useFleet();
42
+ const { driver } = useDriver(editId);
43
+ const isEditing = !!editId;
42
44
  const [form, setForm] = useState({
43
45
  employeeId: '',
44
46
  firstName: '',
@@ -50,6 +52,19 @@ function NewDriverForm() {
50
52
  const [submitting, setSubmitting] = useState(false);
51
53
  const [error, setError] = useState<string | null>(null);
52
54
 
55
+ useEffect(() => {
56
+ if (!editId || !driver) return;
57
+ const [firstName, ...rest] = driver.fullName.trim().split(/\s+/);
58
+ setForm({
59
+ employeeId: driver.employeeId || '',
60
+ firstName: firstName || '',
61
+ lastName: rest.join(' '),
62
+ email: driver.email || '',
63
+ phone: driver.phone || '',
64
+ licenseNumber: driver.licenseNumber || '',
65
+ });
66
+ }, [editId, driver]);
67
+
53
68
  const goBack = () => navigate?.(joinPath(basePath, 'fleet/drivers'));
54
69
 
55
70
  const handleSubmit = async (e: React.FormEvent) => {
@@ -61,25 +76,37 @@ function NewDriverForm() {
61
76
  setSubmitting(true);
62
77
  setError(null);
63
78
  try {
64
- const created = await createDriver({
65
- employeeId: form.employeeId.trim(),
66
- firstName: form.firstName.trim(),
67
- lastName: form.lastName.trim(),
68
- email: form.email.trim() || undefined,
69
- phone: form.phone.trim() || undefined,
70
- licenseNumber: form.licenseNumber.trim() || undefined,
71
- });
72
- navigate?.(joinPath(basePath, `fleet/drivers/${created.id}`));
79
+ if (editId) {
80
+ const updated = await updateDriver(editId, {
81
+ employeeId: form.employeeId.trim(),
82
+ firstName: form.firstName.trim(),
83
+ lastName: form.lastName.trim(),
84
+ email: form.email.trim() || undefined,
85
+ phone: form.phone.trim() || undefined,
86
+ licenseNumber: form.licenseNumber.trim() || undefined,
87
+ });
88
+ navigate?.(joinPath(basePath, `fleet/drivers/${updated.id}`));
89
+ } else {
90
+ const created = await createDriver({
91
+ employeeId: form.employeeId.trim(),
92
+ firstName: form.firstName.trim(),
93
+ lastName: form.lastName.trim(),
94
+ email: form.email.trim() || undefined,
95
+ phone: form.phone.trim() || undefined,
96
+ licenseNumber: form.licenseNumber.trim() || undefined,
97
+ });
98
+ navigate?.(joinPath(basePath, `fleet/drivers/${created.id}`));
99
+ }
73
100
  } catch (err) {
74
- console.error('Failed to create driver:', err);
75
- setError(err instanceof Error ? err.message : 'Failed to create driver. Please try again.');
101
+ console.error('Failed to save driver:', err);
102
+ setError(err instanceof Error ? err.message : 'Failed to save driver. Please try again.');
76
103
  setSubmitting(false);
77
104
  }
78
105
  };
79
106
 
80
107
  return (
81
108
  <PageLayout
82
- title="Add Driver"
109
+ title={isEditing ? 'Edit Driver' : 'Add Driver'}
83
110
  subtitle="Add a driver to your workforce"
84
111
  icon={<Users size={24} />}
85
112
  headerActions={
@@ -88,14 +115,21 @@ function NewDriverForm() {
88
115
  <ArrowLeft size={16} /> Cancel
89
116
  </Button>
90
117
  <Button onClick={handleSubmit} disabled={submitting}>
91
- <Save size={16} /> {submitting ? 'Saving...' : 'Add Driver'}
118
+ <Save size={16} />{' '}
119
+ {isEditing
120
+ ? submitting
121
+ ? 'Saving...'
122
+ : 'Save Changes'
123
+ : submitting
124
+ ? 'Saving...'
125
+ : 'Add Driver'}
92
126
  </Button>
93
127
  </div>
94
128
  }
95
129
  >
96
130
  <PagePurpose icon={<Users size={18} />}>
97
- Add a driver so they can be assigned to orders and routes. An employee ID and name get them on
98
- the roster — add contact details and license number to keep the record complete.
131
+ Add a driver so they can be assigned to orders and routes. An employee ID and name get them
132
+ on the roster — add contact details and license number to keep the record complete.
99
133
  </PagePurpose>
100
134
 
101
135
  <form onSubmit={handleSubmit} className="max-w-2xl space-y-6">
@@ -180,6 +214,7 @@ function NewDriverForm() {
180
214
 
181
215
  export default function DriverDetailsPage() {
182
216
  const { driverId } = useParams<{ driverId: string }>();
217
+ const isEditRoute = useLocation().pathname.endsWith('/edit');
183
218
  const { basePath, navigate } = useMoveTheWheels();
184
219
  const { driver, isLoading, error, refetch } = useDriver(driverId);
185
220
 
@@ -187,6 +222,7 @@ export default function DriverDetailsPage() {
187
222
  const goTo = (path: string) => navigate?.(joinPath(basePath, path));
188
223
 
189
224
  if (!driverId) return <NewDriverForm />;
225
+ if (isEditRoute) return <NewDriverForm editId={driverId} />;
190
226
  if (isLoading) return <LoadingState message="Loading driver details..." />;
191
227
  if (error || !driver) return <ErrorState title="Driver not found" onRetry={refetch} />;
192
228
 
@@ -200,7 +236,9 @@ export default function DriverDetailsPage() {
200
236
  <Button variant="outline" onClick={goBack}>
201
237
  <ArrowLeft size={16} /> Back
202
238
  </Button>
203
- <Button>
239
+ <Button
240
+ onClick={() => navigate?.(joinPath(basePath, 'fleet/drivers/' + driverId + '/edit'))}
241
+ >
204
242
  <Edit size={16} /> Edit
205
243
  </Button>
206
244
  </div>
@@ -548,7 +548,8 @@ export default function OrderDetailsPage() {
548
548
 
549
549
  <div className="space-y-2">
550
550
  <p className="text-sm text-text-secondary">
551
- Current status: <span className="font-medium">{STATUS_LABELS[order.status]}</span>
551
+ Current status:{' '}
552
+ <span className="font-medium">{STATUS_LABELS[order.status]}</span>
552
553
  </p>
553
554
  {(() => {
554
555
  const upcoming = nextStatus(order.status);