@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.
- package/dist/components/StatusBadge.js +30 -30
- package/dist/constants/index.js +12 -5
- package/dist/hooks/useCustomers.js +0 -3
- package/dist/hooks/useFleet.js +144 -131
- package/dist/hooks/useInventory.js +144 -145
- package/dist/hooks/useRoutes.js +5 -14
- package/dist/movethewheels/constants/index.d.ts +1 -0
- package/dist/movethewheels/hooks/useFleet.d.ts +13 -1
- package/dist/movethewheels/hooks/useInventory.d.ts +10 -0
- package/dist/movethewheels/types/index.d.ts +13 -70
- package/dist/pages/CustomerDetailsPage.js +666 -344
- package/dist/pages/CustomersPage.js +71 -80
- package/dist/pages/FleetPage.js +1 -3
- package/dist/pages/InventoryPage.js +4 -4
- package/dist/pages/RouteDetailsPage.js +273 -248
- package/dist/pages/RoutesPage.js +130 -67
- package/dist/pages/VehicleDetailsPage.js +506 -391
- package/dist/pages/VehiclesPage.js +119 -70
- package/dist/pages/WarehouseDetailsPage.js +410 -370
- package/dist/pages/WarehousesPage.js +131 -82
- package/package.json +1 -1
- package/src/movethewheels/components/StatusBadge.tsx +8 -3
- package/src/movethewheels/constants/index.ts +9 -0
- package/src/movethewheels/hooks/useCustomers.ts +0 -3
- package/src/movethewheels/hooks/useFleet.ts +74 -19
- package/src/movethewheels/hooks/useInventory.ts +76 -32
- package/src/movethewheels/hooks/useRoutes.ts +17 -12
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +376 -109
- package/src/movethewheels/pages/CustomersPage.tsx +6 -15
- package/src/movethewheels/pages/FleetPage.tsx +2 -1
- package/src/movethewheels/pages/InventoryPage.tsx +14 -5
- package/src/movethewheels/pages/RouteDetailsPage.tsx +74 -34
- package/src/movethewheels/pages/RoutesPage.tsx +66 -2
- package/src/movethewheels/pages/VehicleDetailsPage.tsx +151 -11
- package/src/movethewheels/pages/VehiclesPage.tsx +68 -3
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +213 -135
- package/src/movethewheels/pages/WarehousesPage.tsx +77 -16
- 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
|
-
|
|
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
|
-
|
|
418
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
315
|
-
|
|
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
|
-
|
|
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
|
);
|