@burdenoff/microfe-movethewheels 2026.929.3 → 2026.929.4
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/generated/wspace-types.d.ts +1547 -64
- package/dist/hooks/useCustomers.js +3 -9
- package/dist/hooks/useFleet.js +146 -149
- package/dist/hooks/useInventory.js +178 -153
- package/dist/hooks/useRoutes.js +9 -9
- package/dist/hooks/useTracking.js +14 -16
- package/dist/movethewheels/hooks/useFleet.d.ts +9 -2
- package/dist/movethewheels/hooks/useInventory.d.ts +13 -12
- package/dist/movethewheels/hooks/useRoutes.d.ts +4 -1
- package/dist/movethewheels/types/index.d.ts +33 -23
- package/dist/movethewheels/utils/formatters.d.ts +1 -1
- package/dist/pages/CustomerDetailsPage.js +6 -6
- package/dist/pages/CustomersPage.js +2 -2
- package/dist/pages/DashboardPage.js +1 -1
- package/dist/pages/DriverDetailsPage.js +7 -3
- package/dist/pages/DriversPage.js +4 -4
- package/dist/pages/FleetPage.js +1 -1
- package/dist/pages/InventoryPage.js +11 -12
- package/dist/pages/LiveTrackingPage.js +4 -4
- package/dist/pages/ProductDetailsPage.js +408 -249
- package/dist/pages/ProductsPage.js +5 -5
- package/dist/pages/RouteDetailsPage.js +8 -10
- package/dist/pages/RoutesPage.js +2 -2
- package/dist/pages/TrackingPage.js +14 -8
- package/dist/pages/WarehouseDetailsPage.js +166 -163
- package/dist/pages/WarehousesPage.js +29 -21
- package/package.json +1 -1
- package/src/generated/wspace-types.ts +1762 -64
- package/src/movethewheels/hooks/useCustomers.ts +7 -9
- package/src/movethewheels/hooks/useFleet.ts +89 -31
- package/src/movethewheels/hooks/useInventory.ts +143 -49
- package/src/movethewheels/hooks/useRoutes.ts +36 -16
- package/src/movethewheels/hooks/useTracking.ts +23 -15
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +14 -8
- package/src/movethewheels/pages/CustomersPage.tsx +5 -2
- package/src/movethewheels/pages/DashboardPage.tsx +3 -1
- package/src/movethewheels/pages/DriverDetailsPage.tsx +13 -3
- package/src/movethewheels/pages/DriversPage.tsx +1 -1
- package/src/movethewheels/pages/FleetPage.tsx +1 -1
- package/src/movethewheels/pages/InventoryPage.tsx +20 -16
- package/src/movethewheels/pages/LiveTrackingPage.tsx +6 -4
- package/src/movethewheels/pages/ProductDetailsPage.tsx +228 -49
- package/src/movethewheels/pages/ProductsPage.tsx +13 -5
- package/src/movethewheels/pages/RouteDetailsPage.tsx +16 -8
- package/src/movethewheels/pages/RoutesPage.tsx +4 -2
- package/src/movethewheels/pages/TrackingPage.tsx +25 -10
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +42 -29
- package/src/movethewheels/pages/WarehousesPage.tsx +23 -15
- package/src/movethewheels/types/index.ts +36 -24
- package/src/movethewheels/utils/formatters.ts +1 -1
|
@@ -61,12 +61,12 @@ function NewWarehouseForm({ editId, onSaved }: { editId?: string; onSaved?: () =
|
|
|
61
61
|
code: '',
|
|
62
62
|
name: '',
|
|
63
63
|
type: 'DISTRIBUTION',
|
|
64
|
-
status: '
|
|
64
|
+
status: '',
|
|
65
65
|
street: '',
|
|
66
66
|
city: '',
|
|
67
67
|
state: '',
|
|
68
68
|
postalCode: '',
|
|
69
|
-
country: '
|
|
69
|
+
country: '',
|
|
70
70
|
totalCapacity: '',
|
|
71
71
|
usedCapacity: '',
|
|
72
72
|
contactEmail: '',
|
|
@@ -84,14 +84,16 @@ function NewWarehouseForm({ editId, onSaved }: { editId?: string; onSaved?: () =
|
|
|
84
84
|
code: warehouse.code || '',
|
|
85
85
|
name: warehouse.name || '',
|
|
86
86
|
type: warehouse.type ? warehouse.type.toUpperCase() : 'DISTRIBUTION',
|
|
87
|
-
status: warehouse.status
|
|
88
|
-
street: warehouse.address.street
|
|
89
|
-
city: warehouse.address.city
|
|
90
|
-
state: warehouse.address.state
|
|
91
|
-
postalCode: warehouse.address.postalCode
|
|
92
|
-
country: warehouse.address.country
|
|
93
|
-
totalCapacity:
|
|
94
|
-
|
|
87
|
+
status: warehouse.status ?? '',
|
|
88
|
+
street: warehouse.address.street ?? '',
|
|
89
|
+
city: warehouse.address.city ?? '',
|
|
90
|
+
state: warehouse.address.state ?? '',
|
|
91
|
+
postalCode: warehouse.address.postalCode ?? '',
|
|
92
|
+
country: warehouse.address.country ?? '',
|
|
93
|
+
totalCapacity:
|
|
94
|
+
warehouse.capacity.totalSpace !== undefined ? String(warehouse.capacity.totalSpace) : '',
|
|
95
|
+
usedCapacity:
|
|
96
|
+
warehouse.capacity.usedSpace !== undefined ? String(warehouse.capacity.usedSpace) : '',
|
|
95
97
|
contactEmail: warehouse.contactInfo?.email || '',
|
|
96
98
|
contactPhone: warehouse.contactInfo?.phone || '',
|
|
97
99
|
});
|
|
@@ -117,18 +119,21 @@ function NewWarehouseForm({ editId, onSaved }: { editId?: string; onSaved?: () =
|
|
|
117
119
|
try {
|
|
118
120
|
const totalCapacity = form.totalCapacity.trim() ? Number(form.totalCapacity) : undefined;
|
|
119
121
|
const usedCapacity = form.usedCapacity.trim() ? Number(form.usedCapacity) : undefined;
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
name: form.name.trim(),
|
|
123
|
-
type: form.type,
|
|
124
|
-
status: form.status,
|
|
125
|
-
address: {
|
|
122
|
+
const address = Object.fromEntries(
|
|
123
|
+
Object.entries({
|
|
126
124
|
street: form.street.trim(),
|
|
127
125
|
city: form.city.trim(),
|
|
128
126
|
state: form.state.trim(),
|
|
129
127
|
postalCode: form.postalCode.trim(),
|
|
130
128
|
country: form.country.trim(),
|
|
131
|
-
},
|
|
129
|
+
}).filter(([, value]) => value.length > 0)
|
|
130
|
+
);
|
|
131
|
+
const warehouseInput = {
|
|
132
|
+
code: form.code.trim(),
|
|
133
|
+
name: form.name.trim(),
|
|
134
|
+
type: form.type,
|
|
135
|
+
status: form.status.trim() || undefined,
|
|
136
|
+
address,
|
|
132
137
|
contactInfo:
|
|
133
138
|
form.contactEmail.trim() || form.contactPhone.trim()
|
|
134
139
|
? {
|
|
@@ -437,7 +442,7 @@ export default function WarehouseDetailsPage() {
|
|
|
437
442
|
if (isLoading) return <LoadingState message="Loading warehouse details..." />;
|
|
438
443
|
if (error || !warehouse) return <ErrorState title="Warehouse not found" onRetry={refetch} />;
|
|
439
444
|
|
|
440
|
-
const isActive = warehouse.status
|
|
445
|
+
const isActive = warehouse.status?.toUpperCase() === 'ACTIVE';
|
|
441
446
|
|
|
442
447
|
return (
|
|
443
448
|
<PageLayout
|
|
@@ -460,8 +465,8 @@ export default function WarehouseDetailsPage() {
|
|
|
460
465
|
}
|
|
461
466
|
headerContent={
|
|
462
467
|
<div className="flex gap-2">
|
|
463
|
-
<Badge variant={isActive ? 'default' : 'secondary'}>
|
|
464
|
-
{isActive ? 'Active' : 'Inactive'}
|
|
468
|
+
<Badge variant={warehouse.status ? (isActive ? 'default' : 'secondary') : 'outline'}>
|
|
469
|
+
{warehouse.status ? (isActive ? 'Active' : 'Inactive') : 'Not provided'}
|
|
465
470
|
</Badge>
|
|
466
471
|
<Badge variant="outline" className="capitalize">
|
|
467
472
|
{warehouse.type.replace('_', ' ')}
|
|
@@ -503,15 +508,23 @@ export default function WarehouseDetailsPage() {
|
|
|
503
508
|
<Box size={18} /> Capacity
|
|
504
509
|
</h2>
|
|
505
510
|
<div>
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
511
|
+
{warehouse.utilization !== undefined &&
|
|
512
|
+
warehouse.capacity.usedSpace !== undefined &&
|
|
513
|
+
warehouse.capacity.totalSpace !== undefined ? (
|
|
514
|
+
<>
|
|
515
|
+
<div className="flex justify-between text-sm mb-2">
|
|
516
|
+
<span>Space Utilization</span>
|
|
517
|
+
<span>{warehouse.utilization.toFixed(0)}%</span>
|
|
518
|
+
</div>
|
|
519
|
+
<Progress value={warehouse.utilization} />
|
|
520
|
+
<p className="text-sm text-text-secondary mt-1">
|
|
521
|
+
{warehouse.capacity.usedSpace.toLocaleString()} /{' '}
|
|
522
|
+
{warehouse.capacity.totalSpace.toLocaleString()} sq ft
|
|
523
|
+
</p>
|
|
524
|
+
</>
|
|
525
|
+
) : (
|
|
526
|
+
<p className="text-sm text-text-secondary">Not provided</p>
|
|
527
|
+
)}
|
|
515
528
|
</div>
|
|
516
529
|
</EmphasisPanel>
|
|
517
530
|
|
|
@@ -70,9 +70,9 @@ export default function WarehousesPage() {
|
|
|
70
70
|
cell: (row) => (
|
|
71
71
|
<div>
|
|
72
72
|
<p>
|
|
73
|
-
{row.address.city
|
|
73
|
+
{[row.address.city, row.address.state].filter(Boolean).join(', ') || 'Not provided'}
|
|
74
74
|
</p>
|
|
75
|
-
<p className="text-sm text-text-secondary">{row.address.country}</p>
|
|
75
|
+
<p className="text-sm text-text-secondary">{row.address.country || 'Not provided'}</p>
|
|
76
76
|
</div>
|
|
77
77
|
),
|
|
78
78
|
},
|
|
@@ -90,37 +90,45 @@ export default function WarehousesPage() {
|
|
|
90
90
|
id: 'utilization',
|
|
91
91
|
header: 'Utilization',
|
|
92
92
|
cell: (row) => {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return (
|
|
93
|
+
const { totalSpace, usedSpace } = row.capacity;
|
|
94
|
+
const hasCapacity =
|
|
95
|
+
typeof totalSpace === 'number' && totalSpace > 0 && typeof usedSpace === 'number';
|
|
96
|
+
const pct = hasCapacity ? (usedSpace / totalSpace) * 100 : undefined;
|
|
97
|
+
return pct !== undefined ? (
|
|
98
98
|
<div className="flex items-center gap-2">
|
|
99
99
|
<Progress value={pct} className="w-16 h-2" />
|
|
100
100
|
<span className="text-sm">{pct.toFixed(0)}%</span>
|
|
101
101
|
</div>
|
|
102
|
+
) : (
|
|
103
|
+
<span className="text-sm text-text-secondary">Not provided</span>
|
|
102
104
|
);
|
|
103
105
|
},
|
|
104
106
|
},
|
|
105
107
|
{
|
|
106
108
|
id: 'capacity',
|
|
107
109
|
header: 'Capacity',
|
|
108
|
-
cell: (row) =>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
cell: (row) => {
|
|
111
|
+
const { totalSpace, usedSpace } = row.capacity;
|
|
112
|
+
return typeof usedSpace === 'number' && typeof totalSpace === 'number' ? (
|
|
113
|
+
<p className="text-sm">
|
|
114
|
+
{usedSpace.toLocaleString()} / {totalSpace.toLocaleString()} sq ft
|
|
115
|
+
</p>
|
|
116
|
+
) : (
|
|
117
|
+
<span className="text-sm text-text-secondary">Not provided</span>
|
|
118
|
+
);
|
|
119
|
+
},
|
|
114
120
|
},
|
|
115
121
|
{
|
|
116
122
|
id: 'status',
|
|
117
123
|
header: 'Status',
|
|
118
124
|
cell: (row) => {
|
|
119
|
-
const isActive = row.status
|
|
120
|
-
return (
|
|
125
|
+
const isActive = row.status?.toUpperCase() === 'ACTIVE';
|
|
126
|
+
return row.status ? (
|
|
121
127
|
<Badge variant={isActive ? 'default' : 'secondary'}>
|
|
122
128
|
{isActive ? 'Active' : 'Inactive'}
|
|
123
129
|
</Badge>
|
|
130
|
+
) : (
|
|
131
|
+
<span className="text-sm text-text-secondary">Not provided</span>
|
|
124
132
|
);
|
|
125
133
|
},
|
|
126
134
|
},
|
|
@@ -101,7 +101,7 @@ export interface Location {
|
|
|
101
101
|
accuracy?: number;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
// Dimensions type
|
|
104
|
+
// Dimensions type used by orders, where all measurements are required.
|
|
105
105
|
export interface Dimensions {
|
|
106
106
|
length: number;
|
|
107
107
|
width: number;
|
|
@@ -109,6 +109,14 @@ export interface Dimensions {
|
|
|
109
109
|
unit: 'cm' | 'in' | 'm';
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
// Catalog dimensions are JSON on the backend and may be only partially provided.
|
|
113
|
+
export interface ProductDimensions {
|
|
114
|
+
length?: number;
|
|
115
|
+
width?: number;
|
|
116
|
+
height?: number;
|
|
117
|
+
unit?: 'cm' | 'in' | 'm' | string;
|
|
118
|
+
}
|
|
119
|
+
|
|
112
120
|
// Company type
|
|
113
121
|
export interface Company {
|
|
114
122
|
id: string;
|
|
@@ -168,10 +176,10 @@ export interface Customer {
|
|
|
168
176
|
phone: string;
|
|
169
177
|
type: 'b2b' | 'b2c';
|
|
170
178
|
company?: string;
|
|
171
|
-
address: Address
|
|
179
|
+
address: Partial<Address>;
|
|
172
180
|
businessLicense?: string;
|
|
173
181
|
taxId?: string;
|
|
174
|
-
rating
|
|
182
|
+
rating?: number;
|
|
175
183
|
createdAt: Date;
|
|
176
184
|
updatedAt: Date;
|
|
177
185
|
}
|
|
@@ -181,12 +189,12 @@ export interface Product {
|
|
|
181
189
|
id: string;
|
|
182
190
|
sku: string;
|
|
183
191
|
name: string;
|
|
184
|
-
description
|
|
185
|
-
category
|
|
192
|
+
description?: string;
|
|
193
|
+
category?: string;
|
|
186
194
|
brand?: string;
|
|
187
|
-
dimensions
|
|
188
|
-
weight
|
|
189
|
-
value
|
|
195
|
+
dimensions?: ProductDimensions;
|
|
196
|
+
weight?: number;
|
|
197
|
+
value?: number;
|
|
190
198
|
barcode?: string;
|
|
191
199
|
images: string[];
|
|
192
200
|
attributes: Array<{
|
|
@@ -345,7 +353,9 @@ export interface Vehicle {
|
|
|
345
353
|
pallets?: number;
|
|
346
354
|
};
|
|
347
355
|
status: VehicleStatus;
|
|
356
|
+
/** Deprecated single-driver convenience value; use driverIds for all links. */
|
|
348
357
|
driverId?: string;
|
|
358
|
+
driverIds: string[];
|
|
349
359
|
currentLocation?: Location;
|
|
350
360
|
lastMaintenance?: Date;
|
|
351
361
|
nextMaintenance?: Date;
|
|
@@ -382,11 +392,12 @@ export interface Driver {
|
|
|
382
392
|
licenseType: string;
|
|
383
393
|
licenseExpiry?: Date;
|
|
384
394
|
vehicleIds: string[];
|
|
395
|
+
/** Deprecated single-vehicle convenience value; use vehicleIds for all links. */
|
|
385
396
|
assignedVehicle?: string;
|
|
386
397
|
status: DriverStatus;
|
|
387
398
|
currentLocation?: Location;
|
|
388
399
|
/** MtwDriver.rating — a single overall rating; the backend does not track per-category breakdowns. */
|
|
389
|
-
rating
|
|
400
|
+
rating?: number;
|
|
390
401
|
/** Free-text HR/performance notes. */
|
|
391
402
|
notes?: string;
|
|
392
403
|
hireDate?: Date;
|
|
@@ -406,13 +417,13 @@ export interface Warehouse {
|
|
|
406
417
|
id: string;
|
|
407
418
|
name: string;
|
|
408
419
|
code: string;
|
|
409
|
-
address: Address
|
|
420
|
+
address: Partial<Address>;
|
|
410
421
|
type: 'distribution' | 'fulfillment' | 'cold_storage' | 'cross_dock' | 'retail';
|
|
411
|
-
/** Raw backend value (free-text, e.g. "ACTIVE"/"INACTIVE"
|
|
412
|
-
status
|
|
422
|
+
/** Raw backend value (free-text, e.g. "ACTIVE"/"INACTIVE"). */
|
|
423
|
+
status?: string;
|
|
413
424
|
capacity: {
|
|
414
|
-
totalSpace
|
|
415
|
-
usedSpace
|
|
425
|
+
totalSpace?: number;
|
|
426
|
+
usedSpace?: number;
|
|
416
427
|
};
|
|
417
428
|
operatingHours: Record<
|
|
418
429
|
string,
|
|
@@ -425,8 +436,8 @@ export interface Warehouse {
|
|
|
425
436
|
email?: string;
|
|
426
437
|
phone?: string;
|
|
427
438
|
};
|
|
428
|
-
/** Real computed value: usedSpace / totalSpace
|
|
429
|
-
utilization
|
|
439
|
+
/** Real computed value: usedSpace / totalSpace, when both values are available. */
|
|
440
|
+
utilization?: number;
|
|
430
441
|
createdAt: Date;
|
|
431
442
|
}
|
|
432
443
|
|
|
@@ -468,9 +479,9 @@ export interface RouteStop {
|
|
|
468
479
|
sequence: number;
|
|
469
480
|
address: Address;
|
|
470
481
|
type: 'pickup' | 'delivery';
|
|
471
|
-
scheduledTime
|
|
482
|
+
scheduledTime?: Date;
|
|
472
483
|
actualTime?: Date;
|
|
473
|
-
duration
|
|
484
|
+
duration?: number;
|
|
474
485
|
status: 'pending' | 'completed' | 'skipped' | 'failed';
|
|
475
486
|
notes?: string;
|
|
476
487
|
}
|
|
@@ -487,8 +498,8 @@ export interface TrafficCondition {
|
|
|
487
498
|
export interface Route {
|
|
488
499
|
id: string;
|
|
489
500
|
name: string;
|
|
490
|
-
driverId
|
|
491
|
-
vehicleId
|
|
501
|
+
driverId?: string;
|
|
502
|
+
vehicleId?: string;
|
|
492
503
|
status: 'planned' | 'in_progress' | 'completed' | 'cancelled';
|
|
493
504
|
stops: RouteStop[];
|
|
494
505
|
distance: number;
|
|
@@ -496,7 +507,8 @@ export interface Route {
|
|
|
496
507
|
/** Estimated fuel cost for the route (svc MtwRoute.fuelCost). */
|
|
497
508
|
fuelCost?: number;
|
|
498
509
|
actualDuration?: number;
|
|
499
|
-
|
|
510
|
+
/** Backend currently does not expose whether optimization has run. */
|
|
511
|
+
optimized?: boolean;
|
|
500
512
|
createdAt: Date;
|
|
501
513
|
}
|
|
502
514
|
|
|
@@ -506,7 +518,7 @@ export interface TrackingAlert {
|
|
|
506
518
|
type: 'delay' | 'deviation' | 'temperature' | 'damage' | 'security';
|
|
507
519
|
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
508
520
|
message: string;
|
|
509
|
-
timestamp
|
|
521
|
+
timestamp?: Date;
|
|
510
522
|
acknowledged: boolean;
|
|
511
523
|
resolvedAt?: Date;
|
|
512
524
|
}
|
|
@@ -516,13 +528,13 @@ export interface Tracking {
|
|
|
516
528
|
id: string;
|
|
517
529
|
entityId: string;
|
|
518
530
|
entityType: 'order' | 'vehicle' | 'driver';
|
|
519
|
-
currentLocation
|
|
531
|
+
currentLocation?: Location;
|
|
520
532
|
locationHistory: Location[];
|
|
521
533
|
status: string;
|
|
522
534
|
estimatedArrival?: Date;
|
|
523
535
|
nextCheckpoint?: string;
|
|
524
536
|
alerts: TrackingAlert[];
|
|
525
|
-
lastUpdated
|
|
537
|
+
lastUpdated?: Date;
|
|
526
538
|
}
|
|
527
539
|
|
|
528
540
|
// Notification type
|
|
@@ -7,7 +7,7 @@ import type { Address } from '../types';
|
|
|
7
7
|
/**
|
|
8
8
|
* Format an address object into a readable string.
|
|
9
9
|
*/
|
|
10
|
-
export function formatAddress(address: Address): string {
|
|
10
|
+
export function formatAddress(address: Partial<Address>): string {
|
|
11
11
|
const parts = [
|
|
12
12
|
address.street,
|
|
13
13
|
address.city,
|