@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.
- package/dist/{AIAssistantPage-B7FGmmKT.js → AIAssistantPage-MWJLAMN_.js} +5 -5
- package/dist/{AnalyticsPage-DXduj5KB.js → AnalyticsPage-900jpV2T.js} +9 -9
- package/dist/{CreateOrderPage-DuJJQ16b.js → CreateOrderPage-CXbhJLLz.js} +164 -139
- package/dist/{CustomerDetailsPage-C98SKw7Z.js → CustomerDetailsPage-CHzg75Q5.js} +16 -16
- package/dist/{CustomersPage-s8hK55JL.js → CustomersPage-CwtVspuq.js} +11 -11
- package/dist/{DashboardPage-BsEgcKEN.js → DashboardPage-BoswH3ec.js} +17 -17
- package/dist/{DataTable-Da7yHjM1.js → DataTable-CmdZd6iV.js} +7 -7
- package/dist/{DocumentsCard-CziaTJhP.js → DocumentsCard-y2ybnAlu.js} +4 -4
- package/dist/DriverDetailsPage-CPO0G8Ki.js +518 -0
- package/dist/{DriversPage-Duy_ITG6.js → DriversPage-6Z61dpDI.js} +11 -11
- package/dist/{FinancePage-BZtPTpmT.js → FinancePage-DYadOUzC.js} +9 -9
- package/dist/{FleetPage-DJfzoHbs.js → FleetPage-qWRj7WFO.js} +10 -10
- package/dist/{FormError-CWjGwOW8.js → FormError-D4T5FWZA.js} +1 -1
- package/dist/{ImportExportPage-DJ77GK_7.js → ImportExportPage-BhWGfbH3.js} +6 -6
- package/dist/{InventoryPage-Dpnwk_N3.js → InventoryPage-DbR4Z--d.js} +10 -10
- package/dist/{LiveTrackingPage-CLlvsP1B.js → LiveTrackingPage-5U68u3oA.js} +10 -10
- package/dist/{MarketplacePage-d99r-v6p.js → MarketplacePage-DMH0HNDH.js} +9 -9
- package/dist/{MetricCard-DA5ZqAVl.js → MetricCard-CqU5WsyW.js} +2 -2
- package/dist/{Notice-kkjV2rLR.js → Notice-BrXJ85dA.js} +2 -2
- package/dist/{OrderDetailsPage-BePtkyN0.js → OrderDetailsPage-DRYifXm9.js} +28 -24
- package/dist/{OrdersListPage-Csy_KScx.js → OrdersListPage-DvICveRv.js} +14 -14
- package/dist/{PageLayout-h_q9l8af.js → PageLayout-j9Zw3wBN.js} +579 -561
- package/dist/ProductDetailsPage-CWgcRqGR.js +375 -0
- package/dist/{ProductsPage-B4jWJ5bv.js → ProductsPage-CokwDOR2.js} +9 -9
- package/dist/{ReportsPage-ALRYfXtz.js → ReportsPage-MU8F9DQC.js} +9 -9
- package/dist/RouteDetailsPage-BHQbFgI9.js +642 -0
- package/dist/{RoutesPage-CQHCDInT.js → RoutesPage-Dqd5Don6.js} +7 -7
- package/dist/{SettingsPage-BYBRrlGe.js → SettingsPage-_6SIlcBX.js} +4 -4
- package/dist/{Sparkline-Q-6bJ24J.js → Sparkline-DTGdlxUH.js} +1 -1
- package/dist/{StatusBadge-1W2p8fEi.js → StatusBadge-BfiF-nNI.js} +1 -1
- package/dist/{TrackingPage-CFndTIyk.js → TrackingPage-Cg0tYGJe.js} +11 -11
- package/dist/VehicleDetailsPage-BbZlCFPo.js +417 -0
- package/dist/{VehiclesPage-ClkfFZxL.js → VehiclesPage-BfBEyXGs.js} +12 -12
- package/dist/WarehouseDetailsPage-DxNhiaoI.js +515 -0
- package/dist/{WarehousesPage-kNba6cr3.js → WarehousesPage-M3NaBT0z.js} +8 -8
- package/dist/{arrow-left-XOzFvBFD.js → arrow-left-1dVWv2dg.js} +1 -1
- package/dist/{arrow-right-CRrwlvFe.js → arrow-right-PUCNATnk.js} +1 -1
- package/dist/{arrow-up-ClFaLPly.js → arrow-up-VQuGMABN.js} +1 -1
- package/dist/{box-BX4WyCLv.js → box-DW1KNP8j.js} +1 -1
- package/dist/{boxes-R3nq5f0_.js → boxes-D6I00XqA.js} +1 -1
- package/dist/{chart-column-BgF8J9bx.js → chart-column-BwQ5H9ol.js} +1 -1
- package/dist/{charts-BS_OnWc6.js → charts-Dv_zxP7L.js} +2 -2
- package/dist/{chevron-left-D3hcLQ1g.js → chevron-left-DzNxoaBG.js} +1 -1
- package/dist/{chevron-right-BpfSrzQF.js → chevron-right-CKm3ZC1T.js} +1 -1
- package/dist/{circle-alert-CLqT8iCQ.js → circle-alert-CtBm6oIb.js} +1 -1
- package/dist/{circle-check-CWLAr_8f.js → circle-check-B_Y7FDyR.js} +1 -1
- package/dist/{circle-check-big-Dsuz8dJ2.js → circle-check-big-H5pLiMBW.js} +1 -1
- package/dist/{clock-DrZOQD8h.js → clock-B4-qjGuB.js} +1 -1
- package/dist/{dollar-sign-CJfKfYul.js → dollar-sign-CYGHy2oS.js} +1 -1
- package/dist/{download-C-yNvUiL.js → download-BJonq2ty.js} +1 -1
- package/dist/{file-text-m2SyQgBo.js → file-text-C-cZx58r.js} +1 -1
- package/dist/{filter-uA6govE9.js → filter-B_S6WOqr.js} +1 -1
- package/dist/{fuel-BroSIBo1.js → fuel-Cjt6IxKr.js} +1 -1
- package/dist/index.js +53 -33
- package/dist/{map-BzwQ__kD.js → map-CO0aWUae.js} +1 -1
- package/dist/{map-pin-BGbmEGn3.js → map-pin-BISLzLwb.js} +1 -1
- package/dist/{maps-DpIuSSmg.js → maps-CtfuCAmh.js} +1 -1
- package/dist/{navigation-CjKGLL9T.js → navigation-BFki5UJz.js} +1 -1
- package/dist/{package-D36cKFvu.js → package-UyaWiALC.js} +1 -1
- package/dist/{phone-mMZFHd1Z.js → phone-Bav_V7rR.js} +1 -1
- package/dist/{plus-DY5R-c1j.js → plus-Bk_G-lFC.js} +1 -1
- package/dist/{refresh-cw-DyOLYuDU.js → refresh-cw-C4-jecjy.js} +1 -1
- package/dist/{route-CuaZ1wU3.js → route-WpCP6F1x.js} +1 -1
- package/dist/{save-h9dzHwJE.js → save-CcfoIn2J.js} +1 -1
- package/dist/{search-stTGSec-.js → search-C3YRxW9e.js} +1 -1
- package/dist/{square-pen-DT5srCPD.js → square-pen-Dr1X-P2s.js} +1 -1
- package/dist/{star-gp4yO3PF.js → star-1Ixfuzai.js} +1 -1
- package/dist/{store-DJZDAvCY.js → store-BwU935v5.js} +1 -1
- package/dist/{trash-2-DwcjTxYE.js → trash-2-CC4KugEJ.js} +1 -1
- package/dist/{trending-up-2q2sPVsb.js → trending-up-1brglj1E.js} +1 -1
- package/dist/{triangle-alert-CU60xaOi.js → triangle-alert-Dh56Yu3-.js} +1 -1
- package/dist/{truck-ByZxqZhI.js → truck-DxhtLfkG.js} +1 -1
- package/dist/{upload-CWor1uj3.js → upload-CLf0JLXW.js} +1 -1
- package/dist/{useAssistant-uoc8wjr0.js → useAssistant-B9qBtRKm.js} +1 -1
- package/dist/{useCustomers-BPPhQXSH.js → useCustomers-BQ4AIEAT.js} +1 -1
- package/dist/{useFleet-CT_iW2V9.js → useFleet-BfA_-VUW.js} +101 -72
- package/dist/{useImportExport-BG0Cg_D9.js → useImportExport-7-O8noRJ.js} +1 -1
- package/dist/{useInventory-BtBGnOC2.js → useInventory-BO7TSgq_.js} +99 -73
- package/dist/{useOrders-BC-1mCc0.js → useOrders-B5FV0kcz.js} +1 -1
- package/dist/{useReports-Cx8blThR.js → useReports-hUXb8ryJ.js} +1 -1
- package/dist/{useRoutes-8fw8oO6y.js → useRoutes-C_ZCYJ_h.js} +1 -1
- package/dist/{useSettings-Do5WSUaI.js → useSettings-BNNzQs_B.js} +2 -2
- package/dist/{useTracking-Dogilo5C.js → useTracking-7nd_XOSL.js} +3 -3
- package/dist/{user-Dr_JS2FF.js → user-B1IjWJtn.js} +1 -1
- package/dist/{user-check-CBSiMH6I.js → user-check-Cx6fHHll.js} +1 -1
- package/dist/{users-BRV9-2nV.js → users-BKmsb79k.js} +1 -1
- package/dist/{warehouse-CmIAl0_w.js → warehouse-DE1irFyJ.js} +1 -1
- package/dist/{wrench-6U-BTSbg.js → wrench-DTJGFs17.js} +1 -1
- package/dist/{x-DX8sZrkd.js → x-CvsUGJy1.js} +1 -1
- package/package.json +1 -1
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +5 -0
- package/src/movethewheels/components/GlassPanel.tsx +1 -3
- package/src/movethewheels/components/MetricCard.tsx +16 -3
- package/src/movethewheels/components/charts/StatusDonut.tsx +2 -6
- package/src/movethewheels/components/charts/TrendChart.tsx +2 -6
- package/src/movethewheels/components/maps/LogisticsMap.tsx +1 -4
- package/src/movethewheels/components/maps/RouteMap.tsx +3 -5
- package/src/movethewheels/components/maps/TrackingMap.tsx +11 -2
- package/src/movethewheels/hooks/useFleet.ts +113 -0
- package/src/movethewheels/hooks/useInventory.ts +86 -0
- package/src/movethewheels/hooks/useSettings.ts +8 -1
- package/src/movethewheels/pages/CreateOrderPage.tsx +48 -6
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +3 -2
- package/src/movethewheels/pages/DriverDetailsPage.tsx +58 -20
- package/src/movethewheels/pages/OrderDetailsPage.tsx +2 -1
- package/src/movethewheels/pages/ProductDetailsPage.tsx +51 -15
- package/src/movethewheels/pages/RouteDetailsPage.tsx +51 -7
- package/src/movethewheels/pages/VehicleDetailsPage.tsx +53 -17
- package/src/movethewheels/pages/VehiclesPage.tsx +9 -1
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +49 -11
- package/src/movethewheels/types/index.ts +3 -0
- package/src/movethewheels/utils/graphqlClient.ts +51 -1
- package/dist/DriverDetailsPage-CBzAmM6s.js +0 -490
- package/dist/ProductDetailsPage-BMlRoR9E.js +0 -350
- package/dist/RouteDetailsPage-Btb1MSIS.js +0 -610
- package/dist/VehicleDetailsPage-DNLrMljV.js +0 -393
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
120
|
-
Only a name is required — add email, phone and company to make deliveries easier to
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
75
|
-
setError(err instanceof Error ? err.message : 'Failed to
|
|
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=
|
|
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} />
|
|
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
|
|
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:
|
|
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);
|