@burdenoff/microfe-movethewheels 2026.925.3 → 2026.929.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/components/StatusBadge.js +4 -1
- package/dist/hooks/useFleet.js +4 -4
- package/dist/hooks/useInventory.js +2 -1
- package/dist/movethewheels/components/StatusBadge.d.ts +3 -1
- package/dist/movethewheels/hooks/useFleet.d.ts +4 -3
- package/dist/movethewheels/hooks/useInventory.d.ts +1 -0
- package/dist/movethewheels/types/index.d.ts +6 -5
- package/dist/pages/DriverDetailsPage.js +2 -2
- package/dist/pages/DriversPage.js +2 -2
- package/dist/pages/InventoryPage.js +1 -1
- package/dist/pages/VehicleDetailsPage.js +49 -26
- package/dist/pages/VehiclesPage.js +2 -2
- package/dist/pages/WarehouseDetailsPage.js +73 -70
- package/dist/pages/WarehousesPage.js +1 -1
- package/package.json +1 -1
- package/src/movethewheels/components/StatusBadge.tsx +7 -4
- package/src/movethewheels/hooks/useFleet.ts +16 -7
- package/src/movethewheels/hooks/useInventory.ts +1 -1
- package/src/movethewheels/pages/DriverDetailsPage.tsx +3 -1
- package/src/movethewheels/pages/DriversPage.tsx +5 -1
- package/src/movethewheels/pages/InventoryPage.tsx +4 -1
- package/src/movethewheels/pages/VehicleDetailsPage.tsx +35 -13
- package/src/movethewheels/pages/VehiclesPage.tsx +4 -2
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +4 -3
- package/src/movethewheels/pages/WarehousesPage.tsx +4 -1
- package/src/movethewheels/types/index.ts +14 -5
package/dist/hooks/useFleet.js
CHANGED
|
@@ -63,9 +63,9 @@ function T(e) {
|
|
|
63
63
|
ownershipType: e.ownershipType ?? void 0,
|
|
64
64
|
year: e.year ?? void 0,
|
|
65
65
|
capacity: {
|
|
66
|
-
weight: 0,
|
|
67
|
-
volume: 0,
|
|
68
|
-
|
|
66
|
+
weight: typeof e.capacity?.weight == "number" ? e.capacity.weight : void 0,
|
|
67
|
+
volume: typeof e.capacity?.volume == "number" ? e.capacity.volume : void 0,
|
|
68
|
+
pallets: typeof e.capacity?.pallets == "number" ? e.capacity.pallets : void 0
|
|
69
69
|
},
|
|
70
70
|
status: b(e.status),
|
|
71
71
|
driverId: void 0,
|
|
@@ -91,7 +91,7 @@ function E(e) {
|
|
|
91
91
|
employeeId: e.employeeId,
|
|
92
92
|
licenseNumber: e.licenseNumber ?? "",
|
|
93
93
|
licenseType: e.licenseClass ?? "standard",
|
|
94
|
-
licenseExpiry: e.licenseExpiry ? new Date(e.licenseExpiry) :
|
|
94
|
+
licenseExpiry: e.licenseExpiry ? new Date(e.licenseExpiry) : void 0,
|
|
95
95
|
vehicleIds: e.vehicleId ? [e.vehicleId] : [],
|
|
96
96
|
assignedVehicle: e.vehicleId ?? void 0,
|
|
97
97
|
status: S(e.status),
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { VehicleType } from '../types';
|
|
2
|
+
|
|
1
3
|
type StatusType = 'order' | 'vehicle' | 'driver' | 'custom';
|
|
2
4
|
interface StatusBadgeProps {
|
|
3
5
|
status: string;
|
|
@@ -44,7 +46,7 @@ export declare function PaymentStatusBadge({ status, size, className, }: {
|
|
|
44
46
|
* VehicleTypeBadge - Displays vehicle type
|
|
45
47
|
*/
|
|
46
48
|
export declare function VehicleTypeBadge({ type, size, className, }: {
|
|
47
|
-
type:
|
|
49
|
+
type: VehicleType;
|
|
48
50
|
size?: 'sm' | 'md' | 'lg';
|
|
49
51
|
className?: string;
|
|
50
52
|
}): import("react").JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Driver, DriverStatus, FleetFilter, Vehicle, VehicleStatus } from '../types';
|
|
1
|
+
import { Driver, DriverStatus, FleetFilter, Vehicle, VehicleFuelType, VehicleStatus } from '../types';
|
|
2
2
|
|
|
3
3
|
interface UseFleetOptions {
|
|
4
4
|
filters?: FleetFilter;
|
|
@@ -16,7 +16,8 @@ export interface CreateVehicleInput {
|
|
|
16
16
|
year?: number;
|
|
17
17
|
/** VehicleStatus enum value, e.g. `ACTIVE`, `MAINTENANCE`. */
|
|
18
18
|
status?: string;
|
|
19
|
-
|
|
19
|
+
/** FuelType enum value, e.g. `DIESEL`, `ELECTRIC`. */
|
|
20
|
+
fuelType?: VehicleFuelType;
|
|
20
21
|
fuelEfficiencyLPer100Km?: number;
|
|
21
22
|
capacity?: {
|
|
22
23
|
weight?: number;
|
|
@@ -75,7 +76,7 @@ export interface ApiVehicle {
|
|
|
75
76
|
capacity?: Record<string, unknown> | null;
|
|
76
77
|
status: string;
|
|
77
78
|
currentLocation?: Record<string, unknown> | null;
|
|
78
|
-
fuelType?:
|
|
79
|
+
fuelType?: VehicleFuelType | null;
|
|
79
80
|
fuelEfficiencyLPer100Km?: number | null;
|
|
80
81
|
fuelLevel?: number | null;
|
|
81
82
|
mileage?: number | null;
|
|
@@ -111,6 +111,7 @@ export declare function useWarehouse(warehouseId: string | undefined): {
|
|
|
111
111
|
isLoading: boolean;
|
|
112
112
|
error: Error | null;
|
|
113
113
|
refetch: () => undefined;
|
|
114
|
+
reload: () => Promise<void>;
|
|
114
115
|
};
|
|
115
116
|
export interface InventoryStats {
|
|
116
117
|
/** Units physically in the warehouses. */
|
|
@@ -245,7 +245,8 @@ export interface ProofOfDelivery {
|
|
|
245
245
|
capturedByDriverId: string;
|
|
246
246
|
notes?: string;
|
|
247
247
|
}
|
|
248
|
-
export type VehicleType = 'van' | 'truck' | 'motorcycle' | 'bicycle' | '
|
|
248
|
+
export type VehicleType = 'van' | 'truck' | 'motorcycle' | 'bicycle' | 'flatbed' | 'refrigerated' | 'semi_trailer' | 'drone';
|
|
249
|
+
export type VehicleFuelType = 'DIESEL' | 'PETROL' | 'CNG' | 'ELECTRIC' | 'HYBRID';
|
|
249
250
|
export type VehicleStatus = 'available' | 'assigned' | 'maintenance' | 'out_of_service' | 'decommissioned';
|
|
250
251
|
export interface VehicleDocument {
|
|
251
252
|
id: string;
|
|
@@ -265,8 +266,8 @@ export interface Vehicle {
|
|
|
265
266
|
ownershipType?: string;
|
|
266
267
|
year?: number;
|
|
267
268
|
capacity: {
|
|
268
|
-
weight
|
|
269
|
-
volume
|
|
269
|
+
weight?: number;
|
|
270
|
+
volume?: number;
|
|
270
271
|
pallets?: number;
|
|
271
272
|
};
|
|
272
273
|
status: VehicleStatus;
|
|
@@ -276,7 +277,7 @@ export interface Vehicle {
|
|
|
276
277
|
nextMaintenance?: Date;
|
|
277
278
|
/** Odometer reading (km) at which the next service is due. */
|
|
278
279
|
nextMaintenanceMileage?: number;
|
|
279
|
-
fuelType?:
|
|
280
|
+
fuelType?: VehicleFuelType;
|
|
280
281
|
/** Fuel consumption, litres per 100km. */
|
|
281
282
|
fuelEfficiencyLPer100Km?: number;
|
|
282
283
|
fuelLevel?: number;
|
|
@@ -301,7 +302,7 @@ export interface Driver {
|
|
|
301
302
|
employeeId: string;
|
|
302
303
|
licenseNumber: string;
|
|
303
304
|
licenseType: string;
|
|
304
|
-
licenseExpiry
|
|
305
|
+
licenseExpiry?: Date;
|
|
305
306
|
vehicleIds: string[];
|
|
306
307
|
assignedVehicle?: string;
|
|
307
308
|
status: DriverStatus;
|
|
@@ -709,9 +709,9 @@ function Y() {
|
|
|
709
709
|
className: "text-sm text-text-secondary",
|
|
710
710
|
children: [_.licenseNumber || "—", _.licenseType ? ` - ${_.licenseType}` : ""]
|
|
711
711
|
}),
|
|
712
|
-
/* @__PURE__ */
|
|
712
|
+
/* @__PURE__ */ U("p", {
|
|
713
713
|
className: "text-sm text-text-secondary",
|
|
714
|
-
children:
|
|
714
|
+
children: _.licenseExpiry ? `Expires: ${L(_.licenseExpiry)}` : "Expiry not recorded"
|
|
715
715
|
})
|
|
716
716
|
] })
|
|
717
717
|
}) })] }),
|
|
@@ -183,9 +183,9 @@ function P() {
|
|
|
183
183
|
header: "License",
|
|
184
184
|
cell: (e) => /* @__PURE__ */ A("div", {
|
|
185
185
|
className: "text-sm",
|
|
186
|
-
children: [/* @__PURE__ */ k("p", { children: e.licenseType }), /* @__PURE__ */
|
|
186
|
+
children: [/* @__PURE__ */ k("p", { children: e.licenseType }), /* @__PURE__ */ k("p", {
|
|
187
187
|
className: "text-text-secondary",
|
|
188
|
-
children:
|
|
188
|
+
children: e.licenseExpiry ? `Exp: ${E(e.licenseExpiry, "short")}` : "Expiry not recorded"
|
|
189
189
|
})]
|
|
190
190
|
})
|
|
191
191
|
},
|
|
@@ -427,7 +427,7 @@ function M() {
|
|
|
427
427
|
className: "text-xs text-text-secondary",
|
|
428
428
|
children: N("movethewheels.inventory.warehouses.utilization", "Utilization")
|
|
429
429
|
}), /* @__PURE__ */ C(g, {
|
|
430
|
-
value: e.capacity.usedSpace / e.capacity.totalSpace * 100,
|
|
430
|
+
value: e.capacity.totalSpace > 0 ? e.capacity.usedSpace / e.capacity.totalSpace * 100 : 0,
|
|
431
431
|
className: "w-24 h-2"
|
|
432
432
|
})]
|
|
433
433
|
})]
|
|
@@ -64,6 +64,27 @@ var se = [
|
|
|
64
64
|
value: e,
|
|
65
65
|
label: t
|
|
66
66
|
})), le = [
|
|
67
|
+
{
|
|
68
|
+
value: "DIESEL",
|
|
69
|
+
label: "Diesel"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
value: "PETROL",
|
|
73
|
+
label: "Petrol"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
value: "CNG",
|
|
77
|
+
label: "CNG"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
value: "ELECTRIC",
|
|
81
|
+
label: "Electric"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
value: "HYBRID",
|
|
85
|
+
label: "Hybrid"
|
|
86
|
+
}
|
|
87
|
+
], ue = [
|
|
67
88
|
{
|
|
68
89
|
value: "owned",
|
|
69
90
|
label: "Owned"
|
|
@@ -77,7 +98,7 @@ var se = [
|
|
|
77
98
|
label: "Rented"
|
|
78
99
|
}
|
|
79
100
|
];
|
|
80
|
-
function
|
|
101
|
+
function de({ editId: t }) {
|
|
81
102
|
let { basePath: n, navigate: r } = d(), { createVehicle: i, updateVehicle: o, deleteVehicle: l } = p(), { vehicle: h } = m(t), g = !!t, [_, T] = K({
|
|
82
103
|
vehicleNumber: "",
|
|
83
104
|
type: "VAN",
|
|
@@ -293,7 +314,7 @@ function ue({ editId: t }) {
|
|
|
293
314
|
children: [/* @__PURE__ */ q(k, {
|
|
294
315
|
id: "ownershipType",
|
|
295
316
|
children: /* @__PURE__ */ q(A, { placeholder: "Select ownership" })
|
|
296
|
-
}), /* @__PURE__ */ q(D, { children:
|
|
317
|
+
}), /* @__PURE__ */ q(D, { children: ue.map((e) => /* @__PURE__ */ q(O, {
|
|
297
318
|
value: e.value,
|
|
298
319
|
children: e.label
|
|
299
320
|
}, e.value)) })]
|
|
@@ -346,14 +367,22 @@ function ue({ editId: t }) {
|
|
|
346
367
|
children: [/* @__PURE__ */ q(w, {
|
|
347
368
|
htmlFor: "fuelType",
|
|
348
369
|
children: "Fuel Type"
|
|
349
|
-
}), /* @__PURE__ */
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
370
|
+
}), /* @__PURE__ */ J(E, {
|
|
371
|
+
value: _.fuelType || void 0,
|
|
372
|
+
onValueChange: (e) => {
|
|
373
|
+
let t = le.find((t) => t.value === e)?.value;
|
|
374
|
+
T((e) => ({
|
|
375
|
+
...e,
|
|
376
|
+
fuelType: t ?? ""
|
|
377
|
+
}));
|
|
378
|
+
},
|
|
379
|
+
children: [/* @__PURE__ */ q(k, {
|
|
380
|
+
id: "fuelType",
|
|
381
|
+
children: /* @__PURE__ */ q(A, { placeholder: "Select fuel type" })
|
|
382
|
+
}), /* @__PURE__ */ q(D, { children: le.map((e) => /* @__PURE__ */ q(O, {
|
|
383
|
+
value: e.value,
|
|
384
|
+
children: e.label
|
|
385
|
+
}, e.value)) })]
|
|
357
386
|
})]
|
|
358
387
|
}), /* @__PURE__ */ J("div", {
|
|
359
388
|
className: "space-y-2",
|
|
@@ -491,12 +520,12 @@ function ue({ editId: t }) {
|
|
|
491
520
|
]
|
|
492
521
|
});
|
|
493
522
|
}
|
|
494
|
-
function
|
|
523
|
+
function fe() {
|
|
495
524
|
let { vehicleId: a } = te(), { basePath: s, navigate: f } = d(), { vehicle: p, isLoading: C, error: w, refetch: E } = m(a), D = U(), { message: O, notify: k } = z(), { t: A } = ne(), P = (e, t) => {
|
|
496
525
|
let n = A(e);
|
|
497
526
|
return n === e ? t : n;
|
|
498
527
|
}, L = ee().pathname.endsWith("/edit"), { drivers: G, isLoading: K, error: Z } = h(L ? void 0 : a), Q = () => f?.(u(s, "fleet/vehicles")), $ = (e) => f?.(u(s, e));
|
|
499
|
-
return a ? L ? /* @__PURE__ */ q(
|
|
528
|
+
return a ? L ? /* @__PURE__ */ q(de, { editId: a }) : C ? /* @__PURE__ */ q(M, { message: "Loading vehicle details..." }) : w || !p ? /* @__PURE__ */ q(j, {
|
|
500
529
|
title: "Vehicle not found",
|
|
501
530
|
onRetry: E
|
|
502
531
|
}) : /* @__PURE__ */ J(N, {
|
|
@@ -608,22 +637,16 @@ function de() {
|
|
|
608
637
|
/* @__PURE__ */ J("div", { children: [/* @__PURE__ */ q("p", {
|
|
609
638
|
className: "text-sm text-text-secondary",
|
|
610
639
|
children: "Weight"
|
|
611
|
-
}), /* @__PURE__ */
|
|
640
|
+
}), /* @__PURE__ */ q("p", {
|
|
612
641
|
className: "text-2xl font-bold",
|
|
613
|
-
children:
|
|
614
|
-
className: "text-sm font-normal",
|
|
615
|
-
children: " kg"
|
|
616
|
-
})]
|
|
642
|
+
children: p.capacity.weight === void 0 ? "—" : `${p.capacity.weight} kg`
|
|
617
643
|
})] }),
|
|
618
644
|
/* @__PURE__ */ J("div", { children: [/* @__PURE__ */ q("p", {
|
|
619
645
|
className: "text-sm text-text-secondary",
|
|
620
646
|
children: "Volume"
|
|
621
|
-
}), /* @__PURE__ */
|
|
647
|
+
}), /* @__PURE__ */ q("p", {
|
|
622
648
|
className: "text-2xl font-bold",
|
|
623
|
-
children:
|
|
624
|
-
className: "text-sm font-normal",
|
|
625
|
-
children: " m³"
|
|
626
|
-
})]
|
|
649
|
+
children: p.capacity.volume === void 0 ? "—" : `${p.capacity.volume} m³`
|
|
627
650
|
})] }),
|
|
628
651
|
p.capacity.pallets && /* @__PURE__ */ J("div", { children: [/* @__PURE__ */ q("p", {
|
|
629
652
|
className: "text-sm text-text-secondary",
|
|
@@ -763,15 +786,15 @@ function de() {
|
|
|
763
786
|
}) }), /* @__PURE__ */ J(b, { children: [/* @__PURE__ */ q("p", {
|
|
764
787
|
className: "text-sm",
|
|
765
788
|
children: p.currentLocation.address || "Location updating..."
|
|
766
|
-
}), /* @__PURE__ */
|
|
789
|
+
}), /* @__PURE__ */ q("p", {
|
|
767
790
|
className: "text-xs text-text-secondary mt-1",
|
|
768
|
-
children:
|
|
791
|
+
children: p.currentLocation.timestamp ? `Updated ${B(p.currentLocation.timestamp, "relative")}` : "Update time not recorded"
|
|
769
792
|
})] })] })
|
|
770
793
|
]
|
|
771
794
|
})]
|
|
772
795
|
})
|
|
773
796
|
]
|
|
774
|
-
}) : /* @__PURE__ */ q(
|
|
797
|
+
}) : /* @__PURE__ */ q(de, {});
|
|
775
798
|
}
|
|
776
799
|
//#endregion
|
|
777
|
-
export {
|
|
800
|
+
export { fe as default };
|
|
@@ -181,9 +181,9 @@ function P() {
|
|
|
181
181
|
header: "Capacity",
|
|
182
182
|
cell: (e) => /* @__PURE__ */ A("div", {
|
|
183
183
|
className: "text-sm",
|
|
184
|
-
children: [/* @__PURE__ */
|
|
184
|
+
children: [/* @__PURE__ */ k("p", { children: e.capacity.weight === void 0 ? "—" : `${e.capacity.weight} kg` }), /* @__PURE__ */ k("p", {
|
|
185
185
|
className: "text-text-secondary",
|
|
186
|
-
children:
|
|
186
|
+
children: e.capacity.volume === void 0 ? "—" : `${e.capacity.volume} m³`
|
|
187
187
|
})]
|
|
188
188
|
})
|
|
189
189
|
},
|
|
@@ -46,8 +46,8 @@ var ne = [
|
|
|
46
46
|
value: "INACTIVE",
|
|
47
47
|
label: "Inactive"
|
|
48
48
|
}];
|
|
49
|
-
function W({ editId: t }) {
|
|
50
|
-
let { basePath:
|
|
49
|
+
function W({ editId: t, onSaved: n }) {
|
|
50
|
+
let { basePath: a, navigate: f } = l(), { createWarehouse: b, updateWarehouse: E, deleteWarehouse: D } = u(), { warehouse: A } = d(t), N = !!t, [P, R] = M({
|
|
51
51
|
code: "",
|
|
52
52
|
name: "",
|
|
53
53
|
type: "DISTRIBUTION",
|
|
@@ -61,68 +61,68 @@ function W({ editId: t }) {
|
|
|
61
61
|
usedCapacity: "",
|
|
62
62
|
contactEmail: "",
|
|
63
63
|
contactPhone: ""
|
|
64
|
-
}), [
|
|
64
|
+
}), [W, G] = M(!1), [ie, K] = M(null), [ae, q] = M(!1), [J, Y] = M(!1), [oe, X] = M(null);
|
|
65
65
|
j(() => {
|
|
66
|
-
!t || !
|
|
67
|
-
code:
|
|
68
|
-
name:
|
|
69
|
-
type:
|
|
70
|
-
status:
|
|
71
|
-
street:
|
|
72
|
-
city:
|
|
73
|
-
state:
|
|
74
|
-
postalCode:
|
|
75
|
-
country:
|
|
76
|
-
totalCapacity:
|
|
77
|
-
usedCapacity:
|
|
78
|
-
contactEmail:
|
|
79
|
-
contactPhone:
|
|
66
|
+
!t || !A || R({
|
|
67
|
+
code: A.code || "",
|
|
68
|
+
name: A.name || "",
|
|
69
|
+
type: A.type ? A.type.toUpperCase() : "DISTRIBUTION",
|
|
70
|
+
status: A.status || "ACTIVE",
|
|
71
|
+
street: A.address.street || "",
|
|
72
|
+
city: A.address.city || "",
|
|
73
|
+
state: A.address.state || "",
|
|
74
|
+
postalCode: A.address.postalCode || "",
|
|
75
|
+
country: A.address.country || "USA",
|
|
76
|
+
totalCapacity: A.capacity.totalSpace ? String(A.capacity.totalSpace) : "",
|
|
77
|
+
usedCapacity: A.capacity.usedSpace ? String(A.capacity.usedSpace) : "",
|
|
78
|
+
contactEmail: A.contactInfo?.email || "",
|
|
79
|
+
contactPhone: A.contactInfo?.phone || ""
|
|
80
80
|
});
|
|
81
|
-
}, [t,
|
|
82
|
-
let Z = () =>
|
|
81
|
+
}, [t, A]);
|
|
82
|
+
let Z = () => f?.(c(a, "inventory/warehouses")), Q = (e, t) => R((n) => ({
|
|
83
83
|
...n,
|
|
84
84
|
[e]: t
|
|
85
85
|
})), $ = async (e) => {
|
|
86
|
-
if (e.preventDefault(), !
|
|
86
|
+
if (e.preventDefault(), !P.code.trim() || !P.name.trim()) {
|
|
87
87
|
K("Warehouse code and name are required.");
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
|
-
if (!
|
|
90
|
+
if (!P.street.trim() || !P.city.trim()) {
|
|
91
91
|
K("A street and city are required for the warehouse address.");
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
|
-
|
|
94
|
+
G(!0), K(null);
|
|
95
95
|
try {
|
|
96
|
-
let e =
|
|
97
|
-
code:
|
|
98
|
-
name:
|
|
99
|
-
type:
|
|
100
|
-
status:
|
|
96
|
+
let e = P.totalCapacity.trim() ? Number(P.totalCapacity) : void 0, r = P.usedCapacity.trim() ? Number(P.usedCapacity) : void 0, i = {
|
|
97
|
+
code: P.code.trim(),
|
|
98
|
+
name: P.name.trim(),
|
|
99
|
+
type: P.type,
|
|
100
|
+
status: P.status,
|
|
101
101
|
address: {
|
|
102
|
-
street:
|
|
103
|
-
city:
|
|
104
|
-
state:
|
|
105
|
-
postalCode:
|
|
106
|
-
country:
|
|
102
|
+
street: P.street.trim(),
|
|
103
|
+
city: P.city.trim(),
|
|
104
|
+
state: P.state.trim(),
|
|
105
|
+
postalCode: P.postalCode.trim(),
|
|
106
|
+
country: P.country.trim()
|
|
107
107
|
},
|
|
108
|
-
contactInfo:
|
|
109
|
-
email:
|
|
110
|
-
phone:
|
|
108
|
+
contactInfo: P.contactEmail.trim() || P.contactPhone.trim() ? {
|
|
109
|
+
email: P.contactEmail.trim() || void 0,
|
|
110
|
+
phone: P.contactPhone.trim() || void 0
|
|
111
111
|
} : void 0,
|
|
112
112
|
totalCapacity: e !== void 0 && !Number.isNaN(e) ? e : void 0,
|
|
113
113
|
usedCapacity: r !== void 0 && !Number.isNaN(r) ? r : void 0
|
|
114
114
|
};
|
|
115
|
-
if (t) await
|
|
115
|
+
if (t) await E(t, i), await n?.(), f?.(c(a, `inventory/warehouses/${t}`));
|
|
116
116
|
else {
|
|
117
|
-
let e = await
|
|
118
|
-
|
|
117
|
+
let e = await b(i);
|
|
118
|
+
f?.(c(a, `inventory/warehouses/${e.id}`));
|
|
119
119
|
}
|
|
120
120
|
} catch (e) {
|
|
121
|
-
console.error("Failed to create warehouse:", e), K(e instanceof Error ? e.message : "Failed to create warehouse. Please try again."),
|
|
121
|
+
console.error("Failed to create warehouse:", e), K(e instanceof Error ? e.message : "Failed to create warehouse. Please try again."), G(!1);
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
return /* @__PURE__ */ I(O, {
|
|
125
|
-
title:
|
|
125
|
+
title: N ? "Edit Warehouse" : "Add Warehouse",
|
|
126
126
|
subtitle: "Register a storage and fulfilment site",
|
|
127
127
|
icon: /* @__PURE__ */ F(s, { size: 24 }),
|
|
128
128
|
headerActions: /* @__PURE__ */ I("div", {
|
|
@@ -130,15 +130,15 @@ function W({ editId: t }) {
|
|
|
130
130
|
children: [/* @__PURE__ */ I(p, {
|
|
131
131
|
variant: "outline",
|
|
132
132
|
onClick: Z,
|
|
133
|
-
disabled:
|
|
133
|
+
disabled: W,
|
|
134
134
|
children: [/* @__PURE__ */ F(e, { size: 16 }), " Cancel"]
|
|
135
135
|
}), /* @__PURE__ */ I(p, {
|
|
136
136
|
onClick: $,
|
|
137
|
-
disabled:
|
|
137
|
+
disabled: W,
|
|
138
138
|
children: [
|
|
139
139
|
/* @__PURE__ */ F(i, { size: 16 }),
|
|
140
140
|
" ",
|
|
141
|
-
|
|
141
|
+
N ? W ? "Saving..." : "Save Changes" : W ? "Saving..." : "Add Warehouse"
|
|
142
142
|
]
|
|
143
143
|
})]
|
|
144
144
|
}),
|
|
@@ -151,7 +151,7 @@ function W({ editId: t }) {
|
|
|
151
151
|
onSubmit: $,
|
|
152
152
|
className: "max-w-2xl space-y-6",
|
|
153
153
|
children: [
|
|
154
|
-
/* @__PURE__ */ F(k, { message:
|
|
154
|
+
/* @__PURE__ */ F(k, { message: ie }),
|
|
155
155
|
/* @__PURE__ */ I(L, { children: [/* @__PURE__ */ F("h2", {
|
|
156
156
|
className: "text-base font-semibold text-text-primary mb-4",
|
|
157
157
|
children: "Warehouse Details"
|
|
@@ -171,7 +171,7 @@ function W({ editId: t }) {
|
|
|
171
171
|
})]
|
|
172
172
|
}), /* @__PURE__ */ F(v, {
|
|
173
173
|
id: "code",
|
|
174
|
-
value:
|
|
174
|
+
value: P.code,
|
|
175
175
|
onChange: (e) => Q("code", e.target.value),
|
|
176
176
|
placeholder: "WH-SF-01",
|
|
177
177
|
required: !0
|
|
@@ -187,7 +187,7 @@ function W({ editId: t }) {
|
|
|
187
187
|
})]
|
|
188
188
|
}), /* @__PURE__ */ F(v, {
|
|
189
189
|
id: "name",
|
|
190
|
-
value:
|
|
190
|
+
value: P.name,
|
|
191
191
|
onChange: (e) => Q("name", e.target.value),
|
|
192
192
|
placeholder: "San Francisco DC",
|
|
193
193
|
required: !0
|
|
@@ -202,7 +202,7 @@ function W({ editId: t }) {
|
|
|
202
202
|
children: "*"
|
|
203
203
|
})]
|
|
204
204
|
}), /* @__PURE__ */ I(x, {
|
|
205
|
-
value:
|
|
205
|
+
value: P.type,
|
|
206
206
|
onValueChange: (e) => Q("type", e),
|
|
207
207
|
children: [/* @__PURE__ */ F(w, {
|
|
208
208
|
id: "type",
|
|
@@ -224,7 +224,7 @@ function W({ editId: t }) {
|
|
|
224
224
|
htmlFor: "status",
|
|
225
225
|
children: "Status"
|
|
226
226
|
}), /* @__PURE__ */ I(x, {
|
|
227
|
-
value:
|
|
227
|
+
value: P.status,
|
|
228
228
|
onValueChange: (e) => Q("status", e),
|
|
229
229
|
children: [/* @__PURE__ */ F(w, {
|
|
230
230
|
id: "status",
|
|
@@ -244,7 +244,7 @@ function W({ editId: t }) {
|
|
|
244
244
|
id: "totalCapacity",
|
|
245
245
|
type: "number",
|
|
246
246
|
min: "0",
|
|
247
|
-
value:
|
|
247
|
+
value: P.totalCapacity,
|
|
248
248
|
onChange: (e) => Q("totalCapacity", e.target.value),
|
|
249
249
|
placeholder: "50000"
|
|
250
250
|
})]
|
|
@@ -258,7 +258,7 @@ function W({ editId: t }) {
|
|
|
258
258
|
id: "usedCapacity",
|
|
259
259
|
type: "number",
|
|
260
260
|
min: "0",
|
|
261
|
-
value:
|
|
261
|
+
value: P.usedCapacity,
|
|
262
262
|
onChange: (e) => Q("usedCapacity", e.target.value),
|
|
263
263
|
placeholder: "0"
|
|
264
264
|
})]
|
|
@@ -275,7 +275,7 @@ function W({ editId: t }) {
|
|
|
275
275
|
}), /* @__PURE__ */ F(v, {
|
|
276
276
|
id: "contactEmail",
|
|
277
277
|
type: "email",
|
|
278
|
-
value:
|
|
278
|
+
value: P.contactEmail,
|
|
279
279
|
onChange: (e) => Q("contactEmail", e.target.value),
|
|
280
280
|
placeholder: "dc-sf@example.com"
|
|
281
281
|
})]
|
|
@@ -286,7 +286,7 @@ function W({ editId: t }) {
|
|
|
286
286
|
children: "Contact Phone"
|
|
287
287
|
}), /* @__PURE__ */ F(v, {
|
|
288
288
|
id: "contactPhone",
|
|
289
|
-
value:
|
|
289
|
+
value: P.contactPhone,
|
|
290
290
|
onChange: (e) => Q("contactPhone", e.target.value),
|
|
291
291
|
placeholder: "+1-555-0100"
|
|
292
292
|
})]
|
|
@@ -314,7 +314,7 @@ function W({ editId: t }) {
|
|
|
314
314
|
children: "Street"
|
|
315
315
|
}), /* @__PURE__ */ F(v, {
|
|
316
316
|
id: "street",
|
|
317
|
-
value:
|
|
317
|
+
value: P.street,
|
|
318
318
|
onChange: (e) => Q("street", e.target.value),
|
|
319
319
|
placeholder: "500 Industrial Way"
|
|
320
320
|
})]
|
|
@@ -328,7 +328,7 @@ function W({ editId: t }) {
|
|
|
328
328
|
children: "City"
|
|
329
329
|
}), /* @__PURE__ */ F(v, {
|
|
330
330
|
id: "city",
|
|
331
|
-
value:
|
|
331
|
+
value: P.city,
|
|
332
332
|
onChange: (e) => Q("city", e.target.value),
|
|
333
333
|
placeholder: "San Francisco"
|
|
334
334
|
})]
|
|
@@ -339,7 +339,7 @@ function W({ editId: t }) {
|
|
|
339
339
|
children: "State"
|
|
340
340
|
}), /* @__PURE__ */ F(v, {
|
|
341
341
|
id: "state",
|
|
342
|
-
value:
|
|
342
|
+
value: P.state,
|
|
343
343
|
onChange: (e) => Q("state", e.target.value),
|
|
344
344
|
placeholder: "CA"
|
|
345
345
|
})]
|
|
@@ -354,7 +354,7 @@ function W({ editId: t }) {
|
|
|
354
354
|
children: "ZIP / Postal Code"
|
|
355
355
|
}), /* @__PURE__ */ F(v, {
|
|
356
356
|
id: "postalCode",
|
|
357
|
-
value:
|
|
357
|
+
value: P.postalCode,
|
|
358
358
|
onChange: (e) => Q("postalCode", e.target.value),
|
|
359
359
|
placeholder: "94103"
|
|
360
360
|
})]
|
|
@@ -365,7 +365,7 @@ function W({ editId: t }) {
|
|
|
365
365
|
children: "Country"
|
|
366
366
|
}), /* @__PURE__ */ F(v, {
|
|
367
367
|
id: "country",
|
|
368
|
-
value:
|
|
368
|
+
value: P.country,
|
|
369
369
|
onChange: (e) => Q("country", e.target.value),
|
|
370
370
|
placeholder: "USA"
|
|
371
371
|
})]
|
|
@@ -373,7 +373,7 @@ function W({ editId: t }) {
|
|
|
373
373
|
})
|
|
374
374
|
]
|
|
375
375
|
})] }),
|
|
376
|
-
|
|
376
|
+
N && /* @__PURE__ */ I(m, {
|
|
377
377
|
className: "border-status-error-border",
|
|
378
378
|
children: [/* @__PURE__ */ F(g, { children: /* @__PURE__ */ I(_, {
|
|
379
379
|
className: "flex items-center gap-2 text-status-error-text",
|
|
@@ -392,17 +392,17 @@ function W({ editId: t }) {
|
|
|
392
392
|
]
|
|
393
393
|
}),
|
|
394
394
|
/* @__PURE__ */ F(B, {
|
|
395
|
-
open:
|
|
395
|
+
open: ae,
|
|
396
396
|
onOpenChange: q,
|
|
397
397
|
children: /* @__PURE__ */ I(V, { children: [
|
|
398
398
|
/* @__PURE__ */ I(ee, { children: [/* @__PURE__ */ F(te, { children: "Delete this warehouse?" }), /* @__PURE__ */ I(H, { children: [
|
|
399
399
|
"This removes ",
|
|
400
|
-
|
|
400
|
+
P.name || "this warehouse",
|
|
401
401
|
" and cannot be undone from here."
|
|
402
402
|
] })] }),
|
|
403
403
|
/* @__PURE__ */ F("div", {
|
|
404
404
|
className: "px-1",
|
|
405
|
-
children: /* @__PURE__ */ F(k, { message:
|
|
405
|
+
children: /* @__PURE__ */ F(k, { message: oe })
|
|
406
406
|
}),
|
|
407
407
|
/* @__PURE__ */ I(U, { children: [/* @__PURE__ */ F(p, {
|
|
408
408
|
variant: "outline",
|
|
@@ -415,7 +415,7 @@ function W({ editId: t }) {
|
|
|
415
415
|
if (t) {
|
|
416
416
|
Y(!0), X(null);
|
|
417
417
|
try {
|
|
418
|
-
await
|
|
418
|
+
await D(t), q(!1), Z();
|
|
419
419
|
} catch (e) {
|
|
420
420
|
X(e instanceof Error ? e.message : "Failed to delete warehouse."), Y(!1);
|
|
421
421
|
}
|
|
@@ -430,15 +430,18 @@ function W({ editId: t }) {
|
|
|
430
430
|
});
|
|
431
431
|
}
|
|
432
432
|
function G() {
|
|
433
|
-
let { warehouseId: i } = P(), { basePath: o, navigate: u } = l(), v = N().pathname.endsWith("/edit"), { warehouse: y, isLoading: x, error: S, refetch: C } = d(i),
|
|
433
|
+
let { warehouseId: i } = P(), { basePath: o, navigate: u } = l(), v = N().pathname.endsWith("/edit"), { warehouse: y, isLoading: x, error: S, refetch: C, reload: w } = d(i), T = () => u?.(c(o, "inventory/warehouses")), k = (e) => u?.(c(o, e));
|
|
434
434
|
if (!i) return /* @__PURE__ */ F(W, {});
|
|
435
|
-
if (v) return /* @__PURE__ */ F(W, {
|
|
435
|
+
if (v) return /* @__PURE__ */ F(W, {
|
|
436
|
+
editId: i,
|
|
437
|
+
onSaved: w
|
|
438
|
+
});
|
|
436
439
|
if (x) return /* @__PURE__ */ F(D, { message: "Loading warehouse details..." });
|
|
437
440
|
if (S || !y) return /* @__PURE__ */ F(E, {
|
|
438
441
|
title: "Warehouse not found",
|
|
439
442
|
onRetry: C
|
|
440
443
|
});
|
|
441
|
-
let
|
|
444
|
+
let j = y.status.toUpperCase() === "ACTIVE";
|
|
442
445
|
return /* @__PURE__ */ I(O, {
|
|
443
446
|
title: y.name,
|
|
444
447
|
subtitle: `Code: ${y.code}`,
|
|
@@ -447,7 +450,7 @@ function G() {
|
|
|
447
450
|
className: "flex gap-2",
|
|
448
451
|
children: [/* @__PURE__ */ I(p, {
|
|
449
452
|
variant: "outline",
|
|
450
|
-
onClick:
|
|
453
|
+
onClick: T,
|
|
451
454
|
children: [/* @__PURE__ */ F(e, { size: 16 }), " Back"]
|
|
452
455
|
}), /* @__PURE__ */ I(p, {
|
|
453
456
|
onClick: () => u?.(c(o, `inventory/warehouses/${i}/edit`)),
|
|
@@ -457,8 +460,8 @@ function G() {
|
|
|
457
460
|
headerContent: /* @__PURE__ */ I("div", {
|
|
458
461
|
className: "flex gap-2",
|
|
459
462
|
children: [/* @__PURE__ */ F(f, {
|
|
460
|
-
variant:
|
|
461
|
-
children:
|
|
463
|
+
variant: j ? "default" : "secondary",
|
|
464
|
+
children: j ? "Active" : "Inactive"
|
|
462
465
|
}), /* @__PURE__ */ F(f, {
|
|
463
466
|
variant: "outline",
|
|
464
467
|
className: "capitalize",
|
|
@@ -477,13 +480,13 @@ function G() {
|
|
|
477
480
|
label: "Review stock here",
|
|
478
481
|
description: "See which products are stored at this site.",
|
|
479
482
|
icon: /* @__PURE__ */ F(t, { size: 16 }),
|
|
480
|
-
onClick: () =>
|
|
483
|
+
onClick: () => k("inventory/products")
|
|
481
484
|
}, {
|
|
482
485
|
id: "fulfil",
|
|
483
486
|
label: "Fulfil an order from this site",
|
|
484
487
|
description: "Pick a pending order to ship out of this warehouse.",
|
|
485
488
|
icon: /* @__PURE__ */ F(r, { size: 16 }),
|
|
486
|
-
onClick: () =>
|
|
489
|
+
onClick: () => k("orders")
|
|
487
490
|
}]
|
|
488
491
|
}),
|
|
489
492
|
/* @__PURE__ */ I("div", {
|
|
@@ -165,7 +165,7 @@ function j() {
|
|
|
165
165
|
id: "utilization",
|
|
166
166
|
header: "Utilization",
|
|
167
167
|
cell: (e) => {
|
|
168
|
-
let t = e.capacity.usedSpace / e.capacity.totalSpace * 100;
|
|
168
|
+
let t = e.capacity.totalSpace > 0 ? e.capacity.usedSpace / e.capacity.totalSpace * 100 : 0;
|
|
169
169
|
return /* @__PURE__ */ D("div", {
|
|
170
170
|
className: "flex items-center gap-2",
|
|
171
171
|
children: [/* @__PURE__ */ E(m, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from '../utils/cn';
|
|
2
2
|
import { Badge } from './ui';
|
|
3
|
-
import type { OrderStatus, VehicleStatus, DriverStatus } from '../types';
|
|
3
|
+
import type { OrderStatus, VehicleStatus, VehicleType, DriverStatus } from '../types';
|
|
4
4
|
import {
|
|
5
5
|
ORDER_STATUS_COLORS,
|
|
6
6
|
VEHICLE_STATUS_COLORS,
|
|
@@ -212,16 +212,19 @@ export function VehicleTypeBadge({
|
|
|
212
212
|
size = 'md',
|
|
213
213
|
className,
|
|
214
214
|
}: {
|
|
215
|
-
type:
|
|
215
|
+
type: VehicleType;
|
|
216
216
|
size?: 'sm' | 'md' | 'lg';
|
|
217
217
|
className?: string;
|
|
218
218
|
}) {
|
|
219
|
-
const typeIcons = {
|
|
219
|
+
const typeIcons: Record<VehicleType, string> = {
|
|
220
220
|
van: '🚐',
|
|
221
221
|
truck: '🚚',
|
|
222
222
|
motorcycle: '🏍️',
|
|
223
223
|
bicycle: '🚲',
|
|
224
|
-
|
|
224
|
+
flatbed: '🚛',
|
|
225
|
+
refrigerated: '🧊',
|
|
226
|
+
semi_trailer: '🚛',
|
|
227
|
+
drone: '🚁',
|
|
225
228
|
};
|
|
226
229
|
|
|
227
230
|
return (
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
Driver,
|
|
5
|
+
DriverStatus,
|
|
6
|
+
FleetFilter,
|
|
7
|
+
Location,
|
|
8
|
+
Vehicle,
|
|
9
|
+
VehicleFuelType,
|
|
10
|
+
VehicleStatus,
|
|
11
|
+
} from '../types';
|
|
4
12
|
import { executeGraphQL } from '../utils/graphqlClient';
|
|
5
13
|
|
|
6
14
|
interface UseFleetOptions {
|
|
@@ -20,7 +28,8 @@ export interface CreateVehicleInput {
|
|
|
20
28
|
year?: number;
|
|
21
29
|
/** VehicleStatus enum value, e.g. `ACTIVE`, `MAINTENANCE`. */
|
|
22
30
|
status?: string;
|
|
23
|
-
|
|
31
|
+
/** FuelType enum value, e.g. `DIESEL`, `ELECTRIC`. */
|
|
32
|
+
fuelType?: VehicleFuelType;
|
|
24
33
|
fuelEfficiencyLPer100Km?: number;
|
|
25
34
|
capacity?: { weight?: number; volume?: number; pallets?: number };
|
|
26
35
|
}
|
|
@@ -79,7 +88,7 @@ export interface ApiVehicle {
|
|
|
79
88
|
capacity?: Record<string, unknown> | null;
|
|
80
89
|
status: string;
|
|
81
90
|
currentLocation?: Record<string, unknown> | null;
|
|
82
|
-
fuelType?:
|
|
91
|
+
fuelType?: VehicleFuelType | null;
|
|
83
92
|
fuelEfficiencyLPer100Km?: number | null;
|
|
84
93
|
fuelLevel?: number | null;
|
|
85
94
|
mileage?: number | null;
|
|
@@ -565,9 +574,9 @@ export function mapVehicle(vehicle: ApiVehicle): Vehicle {
|
|
|
565
574
|
ownershipType: vehicle.ownershipType ?? undefined,
|
|
566
575
|
year: vehicle.year ?? undefined,
|
|
567
576
|
capacity: {
|
|
568
|
-
weight:
|
|
569
|
-
volume:
|
|
570
|
-
|
|
577
|
+
weight: typeof vehicle.capacity?.weight === 'number' ? vehicle.capacity.weight : undefined,
|
|
578
|
+
volume: typeof vehicle.capacity?.volume === 'number' ? vehicle.capacity.volume : undefined,
|
|
579
|
+
pallets: typeof vehicle.capacity?.pallets === 'number' ? vehicle.capacity.pallets : undefined,
|
|
571
580
|
},
|
|
572
581
|
status: mapVehicleStatus(vehicle.status),
|
|
573
582
|
driverId: undefined,
|
|
@@ -594,7 +603,7 @@ export function mapDriver(driver: ApiDriver): Driver {
|
|
|
594
603
|
employeeId: driver.employeeId,
|
|
595
604
|
licenseNumber: driver.licenseNumber ?? '',
|
|
596
605
|
licenseType: driver.licenseClass ?? 'standard',
|
|
597
|
-
licenseExpiry: driver.licenseExpiry ? new Date(driver.licenseExpiry) :
|
|
606
|
+
licenseExpiry: driver.licenseExpiry ? new Date(driver.licenseExpiry) : undefined,
|
|
598
607
|
vehicleIds: driver.vehicleId ? [driver.vehicleId] : [],
|
|
599
608
|
assignedVehicle: driver.vehicleId ?? undefined,
|
|
600
609
|
status: mapDriverStatus(driver.status),
|
|
@@ -985,7 +985,7 @@ export function useWarehouse(warehouseId: string | undefined) {
|
|
|
985
985
|
void load();
|
|
986
986
|
}, [load]);
|
|
987
987
|
|
|
988
|
-
return { warehouse, isLoading, error, refetch: () => void load() };
|
|
988
|
+
return { warehouse, isLoading, error, refetch: () => void load(), reload: load };
|
|
989
989
|
}
|
|
990
990
|
|
|
991
991
|
export interface InventoryStats {
|
|
@@ -709,7 +709,9 @@ export default function DriverDetailsPage() {
|
|
|
709
709
|
{driver.licenseType ? ` - ${driver.licenseType}` : ''}
|
|
710
710
|
</p>
|
|
711
711
|
<p className="text-sm text-text-secondary">
|
|
712
|
-
|
|
712
|
+
{driver.licenseExpiry
|
|
713
|
+
? `Expires: ${formatDate(driver.licenseExpiry)}`
|
|
714
|
+
: 'Expiry not recorded'}
|
|
713
715
|
</p>
|
|
714
716
|
</div>
|
|
715
717
|
</div>
|
|
@@ -141,7 +141,11 @@ export default function DriversPage() {
|
|
|
141
141
|
cell: (row) => (
|
|
142
142
|
<div className="text-sm">
|
|
143
143
|
<p>{row.licenseType}</p>
|
|
144
|
-
<p className="text-text-secondary">
|
|
144
|
+
<p className="text-text-secondary">
|
|
145
|
+
{row.licenseExpiry
|
|
146
|
+
? `Exp: ${formatDate(row.licenseExpiry, 'short')}`
|
|
147
|
+
: 'Expiry not recorded'}
|
|
148
|
+
</p>
|
|
145
149
|
</div>
|
|
146
150
|
),
|
|
147
151
|
},
|
|
@@ -521,7 +521,10 @@ export default function InventoryPage() {
|
|
|
521
521
|
</p>
|
|
522
522
|
<Progress
|
|
523
523
|
value={
|
|
524
|
-
|
|
524
|
+
warehouse.capacity.totalSpace > 0
|
|
525
|
+
? (warehouse.capacity.usedSpace / warehouse.capacity.totalSpace) *
|
|
526
|
+
100
|
|
527
|
+
: 0
|
|
525
528
|
}
|
|
526
529
|
className="w-24 h-2"
|
|
527
530
|
/>
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
ResponsiveDialogDescription,
|
|
48
48
|
} from '@burdenoff/fe-libs/ui';
|
|
49
49
|
import { useVehicle, useVehicleDrivers, useFleet, toApiVehicleStatus } from '../hooks/useFleet';
|
|
50
|
-
import type { VehicleStatus } from '../types';
|
|
50
|
+
import type { VehicleFuelType, VehicleStatus } from '../types';
|
|
51
51
|
import { VEHICLE_STATUS_LABELS } from '../constants';
|
|
52
52
|
import { useRecordVehicleService } from '../hooks/useFleetIntelligence';
|
|
53
53
|
import { VehicleServiceCard } from '../components/VehicleServiceCard';
|
|
@@ -74,6 +74,14 @@ const VEHICLE_STATUS_OPTIONS: Array<{ value: VehicleStatus; label: string }> = (
|
|
|
74
74
|
Object.entries(VEHICLE_STATUS_LABELS) as Array<[VehicleStatus, string]>
|
|
75
75
|
).map(([value, label]) => ({ value, label }));
|
|
76
76
|
|
|
77
|
+
const FUEL_TYPE_OPTIONS: Array<{ value: VehicleFuelType; label: string }> = [
|
|
78
|
+
{ value: 'DIESEL', label: 'Diesel' },
|
|
79
|
+
{ value: 'PETROL', label: 'Petrol' },
|
|
80
|
+
{ value: 'CNG', label: 'CNG' },
|
|
81
|
+
{ value: 'ELECTRIC', label: 'Electric' },
|
|
82
|
+
{ value: 'HYBRID', label: 'Hybrid' },
|
|
83
|
+
];
|
|
84
|
+
|
|
77
85
|
const OWNERSHIP_TYPE_OPTIONS = [
|
|
78
86
|
{ value: 'owned', label: 'Owned' },
|
|
79
87
|
{ value: 'leased', label: 'Leased' },
|
|
@@ -94,7 +102,7 @@ function NewVehicleForm({ editId }: { editId?: string }) {
|
|
|
94
102
|
ownershipType: '',
|
|
95
103
|
year: '',
|
|
96
104
|
status: 'available' as VehicleStatus,
|
|
97
|
-
fuelType: '',
|
|
105
|
+
fuelType: '' as VehicleFuelType | '',
|
|
98
106
|
fuelEfficiencyLPer100Km: '',
|
|
99
107
|
capacityWeight: '',
|
|
100
108
|
capacityVolume: '',
|
|
@@ -342,12 +350,26 @@ function NewVehicleForm({ editId }: { editId?: string }) {
|
|
|
342
350
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
343
351
|
<div className="space-y-2">
|
|
344
352
|
<Label htmlFor="fuelType">Fuel Type</Label>
|
|
345
|
-
<
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
353
|
+
<Select
|
|
354
|
+
value={form.fuelType || undefined}
|
|
355
|
+
onValueChange={(value) => {
|
|
356
|
+
const fuelType = FUEL_TYPE_OPTIONS.find(
|
|
357
|
+
(option) => option.value === value
|
|
358
|
+
)?.value;
|
|
359
|
+
setForm((prev) => ({ ...prev, fuelType: fuelType ?? '' }));
|
|
360
|
+
}}
|
|
361
|
+
>
|
|
362
|
+
<SelectTrigger id="fuelType">
|
|
363
|
+
<SelectValue placeholder="Select fuel type" />
|
|
364
|
+
</SelectTrigger>
|
|
365
|
+
<SelectContent>
|
|
366
|
+
{FUEL_TYPE_OPTIONS.map((option) => (
|
|
367
|
+
<SelectItem key={option.value} value={option.value}>
|
|
368
|
+
{option.label}
|
|
369
|
+
</SelectItem>
|
|
370
|
+
))}
|
|
371
|
+
</SelectContent>
|
|
372
|
+
</Select>
|
|
351
373
|
</div>
|
|
352
374
|
<div className="space-y-2">
|
|
353
375
|
<Label htmlFor="fuelEfficiency">Fuel Efficiency (L/100km)</Label>
|
|
@@ -590,15 +612,13 @@ export default function VehicleDetailsPage() {
|
|
|
590
612
|
<div>
|
|
591
613
|
<p className="text-sm text-text-secondary">Weight</p>
|
|
592
614
|
<p className="text-2xl font-bold">
|
|
593
|
-
{vehicle.capacity.weight}
|
|
594
|
-
<span className="text-sm font-normal"> kg</span>
|
|
615
|
+
{vehicle.capacity.weight !== undefined ? `${vehicle.capacity.weight} kg` : '—'}
|
|
595
616
|
</p>
|
|
596
617
|
</div>
|
|
597
618
|
<div>
|
|
598
619
|
<p className="text-sm text-text-secondary">Volume</p>
|
|
599
620
|
<p className="text-2xl font-bold">
|
|
600
|
-
{vehicle.capacity.volume}
|
|
601
|
-
<span className="text-sm font-normal"> m³</span>
|
|
621
|
+
{vehicle.capacity.volume !== undefined ? `${vehicle.capacity.volume} m³` : '—'}
|
|
602
622
|
</p>
|
|
603
623
|
</div>
|
|
604
624
|
{vehicle.capacity.pallets && (
|
|
@@ -741,7 +761,9 @@ export default function VehicleDetailsPage() {
|
|
|
741
761
|
{vehicle.currentLocation.address || 'Location updating...'}
|
|
742
762
|
</p>
|
|
743
763
|
<p className="text-xs text-text-secondary mt-1">
|
|
744
|
-
|
|
764
|
+
{vehicle.currentLocation.timestamp
|
|
765
|
+
? `Updated ${formatDate(vehicle.currentLocation.timestamp, 'relative')}`
|
|
766
|
+
: 'Update time not recorded'}
|
|
745
767
|
</p>
|
|
746
768
|
</CardContent>
|
|
747
769
|
</Card>
|
|
@@ -112,8 +112,10 @@ export default function VehiclesPage() {
|
|
|
112
112
|
header: 'Capacity',
|
|
113
113
|
cell: (row) => (
|
|
114
114
|
<div className="text-sm">
|
|
115
|
-
<p>{row.capacity.weight} kg</p>
|
|
116
|
-
<p className="text-text-secondary">
|
|
115
|
+
<p>{row.capacity.weight !== undefined ? `${row.capacity.weight} kg` : '—'}</p>
|
|
116
|
+
<p className="text-text-secondary">
|
|
117
|
+
{row.capacity.volume !== undefined ? `${row.capacity.volume} m³` : '—'}
|
|
118
|
+
</p>
|
|
117
119
|
</div>
|
|
118
120
|
),
|
|
119
121
|
},
|
|
@@ -52,7 +52,7 @@ const WAREHOUSE_STATUS_OPTIONS = [
|
|
|
52
52
|
{ value: 'INACTIVE', label: 'Inactive' },
|
|
53
53
|
];
|
|
54
54
|
|
|
55
|
-
function NewWarehouseForm({ editId }: { editId?: string }) {
|
|
55
|
+
function NewWarehouseForm({ editId, onSaved }: { editId?: string; onSaved?: () => Promise<void> }) {
|
|
56
56
|
const { basePath, navigate } = useMoveTheWheels();
|
|
57
57
|
const { createWarehouse, updateWarehouse, deleteWarehouse } = useInventory();
|
|
58
58
|
const { warehouse } = useWarehouse(editId);
|
|
@@ -143,6 +143,7 @@ function NewWarehouseForm({ editId }: { editId?: string }) {
|
|
|
143
143
|
};
|
|
144
144
|
if (editId) {
|
|
145
145
|
await updateWarehouse(editId, warehouseInput);
|
|
146
|
+
await onSaved?.();
|
|
146
147
|
navigate?.(joinPath(basePath, `inventory/warehouses/${editId}`));
|
|
147
148
|
} else {
|
|
148
149
|
const created = await createWarehouse(warehouseInput);
|
|
@@ -426,13 +427,13 @@ export default function WarehouseDetailsPage() {
|
|
|
426
427
|
const { warehouseId } = useParams<{ warehouseId: string }>();
|
|
427
428
|
const { basePath, navigate } = useMoveTheWheels();
|
|
428
429
|
const isEditRoute = useLocation().pathname.endsWith('/edit');
|
|
429
|
-
const { warehouse, isLoading, error, refetch } = useWarehouse(warehouseId);
|
|
430
|
+
const { warehouse, isLoading, error, refetch, reload } = useWarehouse(warehouseId);
|
|
430
431
|
|
|
431
432
|
const goBack = () => navigate?.(joinPath(basePath, 'inventory/warehouses'));
|
|
432
433
|
const goTo = (path: string) => navigate?.(joinPath(basePath, path));
|
|
433
434
|
|
|
434
435
|
if (!warehouseId) return <NewWarehouseForm />;
|
|
435
|
-
if (isEditRoute) return <NewWarehouseForm editId={warehouseId} />;
|
|
436
|
+
if (isEditRoute) return <NewWarehouseForm editId={warehouseId} onSaved={reload} />;
|
|
436
437
|
if (isLoading) return <LoadingState message="Loading warehouse details..." />;
|
|
437
438
|
if (error || !warehouse) return <ErrorState title="Warehouse not found" onRetry={refetch} />;
|
|
438
439
|
|
|
@@ -90,7 +90,10 @@ export default function WarehousesPage() {
|
|
|
90
90
|
id: 'utilization',
|
|
91
91
|
header: 'Utilization',
|
|
92
92
|
cell: (row) => {
|
|
93
|
-
const pct =
|
|
93
|
+
const pct =
|
|
94
|
+
row.capacity.totalSpace > 0
|
|
95
|
+
? (row.capacity.usedSpace / row.capacity.totalSpace) * 100
|
|
96
|
+
: 0;
|
|
94
97
|
return (
|
|
95
98
|
<div className="flex items-center gap-2">
|
|
96
99
|
<Progress value={pct} className="w-16 h-2" />
|
|
@@ -308,7 +308,16 @@ export interface ProofOfDelivery {
|
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
// Vehicle type
|
|
311
|
-
export type VehicleType =
|
|
311
|
+
export type VehicleType =
|
|
312
|
+
| 'van'
|
|
313
|
+
| 'truck'
|
|
314
|
+
| 'motorcycle'
|
|
315
|
+
| 'bicycle'
|
|
316
|
+
| 'flatbed'
|
|
317
|
+
| 'refrigerated'
|
|
318
|
+
| 'semi_trailer'
|
|
319
|
+
| 'drone';
|
|
320
|
+
export type VehicleFuelType = 'DIESEL' | 'PETROL' | 'CNG' | 'ELECTRIC' | 'HYBRID';
|
|
312
321
|
export type VehicleStatus =
|
|
313
322
|
'available' | 'assigned' | 'maintenance' | 'out_of_service' | 'decommissioned';
|
|
314
323
|
|
|
@@ -331,8 +340,8 @@ export interface Vehicle {
|
|
|
331
340
|
ownershipType?: string;
|
|
332
341
|
year?: number;
|
|
333
342
|
capacity: {
|
|
334
|
-
weight
|
|
335
|
-
volume
|
|
343
|
+
weight?: number;
|
|
344
|
+
volume?: number;
|
|
336
345
|
pallets?: number;
|
|
337
346
|
};
|
|
338
347
|
status: VehicleStatus;
|
|
@@ -342,7 +351,7 @@ export interface Vehicle {
|
|
|
342
351
|
nextMaintenance?: Date;
|
|
343
352
|
/** Odometer reading (km) at which the next service is due. */
|
|
344
353
|
nextMaintenanceMileage?: number;
|
|
345
|
-
fuelType?:
|
|
354
|
+
fuelType?: VehicleFuelType;
|
|
346
355
|
/** Fuel consumption, litres per 100km. */
|
|
347
356
|
fuelEfficiencyLPer100Km?: number;
|
|
348
357
|
fuelLevel?: number;
|
|
@@ -371,7 +380,7 @@ export interface Driver {
|
|
|
371
380
|
employeeId: string;
|
|
372
381
|
licenseNumber: string;
|
|
373
382
|
licenseType: string;
|
|
374
|
-
licenseExpiry
|
|
383
|
+
licenseExpiry?: Date;
|
|
375
384
|
vehicleIds: string[];
|
|
376
385
|
assignedVehicle?: string;
|
|
377
386
|
status: DriverStatus;
|