@burdenoff/microfe-movethewheels 2026.925.2 → 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 +34 -31
- package/dist/constants/index.js +12 -5
- package/dist/hooks/useCustomers.js +0 -3
- package/dist/hooks/useFleet.js +148 -135
- package/dist/hooks/useInventory.js +146 -146
- package/dist/hooks/useRoutes.js +5 -14
- package/dist/movethewheels/components/StatusBadge.d.ts +3 -1
- package/dist/movethewheels/constants/index.d.ts +1 -0
- package/dist/movethewheels/hooks/useFleet.d.ts +17 -4
- package/dist/movethewheels/hooks/useInventory.d.ts +11 -0
- package/dist/movethewheels/types/index.d.ts +19 -75
- package/dist/pages/CustomerDetailsPage.js +666 -344
- package/dist/pages/CustomersPage.js +71 -80
- package/dist/pages/DriverDetailsPage.js +2 -2
- package/dist/pages/DriversPage.js +2 -2
- package/dist/pages/FleetPage.js +1 -3
- package/dist/pages/InventoryPage.js +5 -5
- package/dist/pages/RouteDetailsPage.js +273 -248
- package/dist/pages/RoutesPage.js +130 -67
- package/dist/pages/VehicleDetailsPage.js +527 -389
- package/dist/pages/VehiclesPage.js +120 -71
- package/dist/pages/WarehouseDetailsPage.js +411 -368
- package/dist/pages/WarehousesPage.js +132 -83
- package/package.json +1 -1
- package/src/movethewheels/components/StatusBadge.tsx +15 -7
- package/src/movethewheels/constants/index.ts +9 -0
- package/src/movethewheels/hooks/useCustomers.ts +0 -3
- package/src/movethewheels/hooks/useFleet.ts +89 -25
- package/src/movethewheels/hooks/useInventory.ts +77 -33
- 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/DriverDetailsPage.tsx +3 -1
- package/src/movethewheels/pages/DriversPage.tsx +5 -1
- package/src/movethewheels/pages/FleetPage.tsx +2 -1
- package/src/movethewheels/pages/InventoryPage.tsx +18 -6
- package/src/movethewheels/pages/RouteDetailsPage.tsx +74 -34
- package/src/movethewheels/pages/RoutesPage.tsx +66 -2
- package/src/movethewheels/pages/VehicleDetailsPage.tsx +185 -23
- package/src/movethewheels/pages/VehiclesPage.tsx +72 -5
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +217 -138
- package/src/movethewheels/pages/WarehousesPage.tsx +81 -17
- package/src/movethewheels/types/index.ts +28 -75
|
@@ -3,11 +3,22 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { useState } from 'react';
|
|
6
|
-
import { Warehouse, Plus, RefreshCw, CircleCheck, Gauge } from 'lucide-react';
|
|
6
|
+
import { Warehouse, Plus, RefreshCw, CircleCheck, Gauge, Filter } from 'lucide-react';
|
|
7
7
|
import { PageLayout } from '../components/PageLayout';
|
|
8
8
|
import { MetricCard, MetricGrid } from '../components/MetricCard';
|
|
9
9
|
import { DataTable, type Column } from '../components/DataTable';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
Badge,
|
|
12
|
+
Progress,
|
|
13
|
+
Card,
|
|
14
|
+
CardContent,
|
|
15
|
+
Select,
|
|
16
|
+
SelectContent,
|
|
17
|
+
SelectItem,
|
|
18
|
+
SelectTrigger,
|
|
19
|
+
SelectValue,
|
|
20
|
+
Button,
|
|
21
|
+
} from '../components/ui';
|
|
11
22
|
import { CTAOverflowMenu, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
12
23
|
import { useWarehouses, useWarehouseStats } from '../hooks/useInventory';
|
|
13
24
|
import { useDebouncedValue } from '../hooks/useDebouncedValue';
|
|
@@ -15,17 +26,26 @@ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
|
15
26
|
import { joinPath } from '../utils/navigation';
|
|
16
27
|
import type { Warehouse as WarehouseType } from '../types';
|
|
17
28
|
|
|
29
|
+
const STATUS_OPTIONS = [
|
|
30
|
+
{ value: 'all', label: 'All statuses' },
|
|
31
|
+
{ value: 'ACTIVE', label: 'Active' },
|
|
32
|
+
{ value: 'INACTIVE', label: 'Inactive' },
|
|
33
|
+
];
|
|
34
|
+
|
|
18
35
|
export default function WarehousesPage() {
|
|
19
36
|
const { basePath, navigate } = useMoveTheWheels();
|
|
20
37
|
const [page, setPage] = useState(0);
|
|
21
38
|
const [pageSize, setPageSize] = useState(25);
|
|
22
39
|
const [search, setSearch] = useState('');
|
|
40
|
+
const [statusFilter, setStatusFilter] = useState('all');
|
|
23
41
|
const debouncedSearch = useDebouncedValue(search);
|
|
24
42
|
const { warehouses, totalWarehouses, hasMore, isLoading, error, refetch } = useWarehouses({
|
|
25
43
|
limit: pageSize,
|
|
26
44
|
offset: page * pageSize,
|
|
27
45
|
search: debouncedSearch,
|
|
46
|
+
status: statusFilter === 'all' ? undefined : statusFilter,
|
|
28
47
|
});
|
|
48
|
+
const filtersActive = statusFilter !== 'all' || search.length > 0;
|
|
29
49
|
// Bounded aggregate for the tiles + subtitle — not the truncated page.
|
|
30
50
|
const { stats } = useWarehouseStats();
|
|
31
51
|
|
|
@@ -70,7 +90,10 @@ export default function WarehousesPage() {
|
|
|
70
90
|
id: 'utilization',
|
|
71
91
|
header: 'Utilization',
|
|
72
92
|
cell: (row) => {
|
|
73
|
-
const pct =
|
|
93
|
+
const pct =
|
|
94
|
+
row.capacity.totalSpace > 0
|
|
95
|
+
? (row.capacity.usedSpace / row.capacity.totalSpace) * 100
|
|
96
|
+
: 0;
|
|
74
97
|
return (
|
|
75
98
|
<div className="flex items-center gap-2">
|
|
76
99
|
<Progress value={pct} className="w-16 h-2" />
|
|
@@ -83,25 +106,23 @@ export default function WarehousesPage() {
|
|
|
83
106
|
id: 'capacity',
|
|
84
107
|
header: 'Capacity',
|
|
85
108
|
cell: (row) => (
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
</p>
|
|
91
|
-
<p className="text-text-secondary">
|
|
92
|
-
{row.capacity.usedPallets} / {row.capacity.totalPallets} pallets
|
|
93
|
-
</p>
|
|
94
|
-
</div>
|
|
109
|
+
<p className="text-sm">
|
|
110
|
+
{row.capacity.usedSpace.toLocaleString()} / {row.capacity.totalSpace.toLocaleString()} sq
|
|
111
|
+
ft
|
|
112
|
+
</p>
|
|
95
113
|
),
|
|
96
114
|
},
|
|
97
115
|
{
|
|
98
116
|
id: 'status',
|
|
99
117
|
header: 'Status',
|
|
100
|
-
cell: (row) =>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
cell: (row) => {
|
|
119
|
+
const isActive = row.status.toUpperCase() === 'ACTIVE';
|
|
120
|
+
return (
|
|
121
|
+
<Badge variant={isActive ? 'default' : 'secondary'}>
|
|
122
|
+
{isActive ? 'Active' : 'Inactive'}
|
|
123
|
+
</Badge>
|
|
124
|
+
);
|
|
125
|
+
},
|
|
105
126
|
},
|
|
106
127
|
];
|
|
107
128
|
|
|
@@ -152,6 +173,49 @@ export default function WarehousesPage() {
|
|
|
152
173
|
</MetricGrid>
|
|
153
174
|
</div>
|
|
154
175
|
|
|
176
|
+
{/* Filters */}
|
|
177
|
+
<Card>
|
|
178
|
+
<CardContent className="p-4">
|
|
179
|
+
<div className="flex flex-wrap items-center gap-4">
|
|
180
|
+
<div className="flex items-center gap-2">
|
|
181
|
+
<Filter size={16} className="text-text-secondary" />
|
|
182
|
+
<span className="text-sm font-medium">Filters:</span>
|
|
183
|
+
</div>
|
|
184
|
+
<Select
|
|
185
|
+
value={statusFilter}
|
|
186
|
+
onValueChange={(value) => {
|
|
187
|
+
setStatusFilter(value);
|
|
188
|
+
setPage(0);
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
191
|
+
<SelectTrigger className="w-[180px]">
|
|
192
|
+
<SelectValue placeholder="Status" />
|
|
193
|
+
</SelectTrigger>
|
|
194
|
+
<SelectContent>
|
|
195
|
+
{STATUS_OPTIONS.map((option) => (
|
|
196
|
+
<SelectItem key={option.value} value={option.value}>
|
|
197
|
+
{option.label}
|
|
198
|
+
</SelectItem>
|
|
199
|
+
))}
|
|
200
|
+
</SelectContent>
|
|
201
|
+
</Select>
|
|
202
|
+
{filtersActive && (
|
|
203
|
+
<Button
|
|
204
|
+
variant="ghost"
|
|
205
|
+
size="sm"
|
|
206
|
+
onClick={() => {
|
|
207
|
+
setStatusFilter('all');
|
|
208
|
+
setSearch('');
|
|
209
|
+
setPage(0);
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
212
|
+
Clear filters
|
|
213
|
+
</Button>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
</CardContent>
|
|
217
|
+
</Card>
|
|
218
|
+
|
|
155
219
|
<DataTable
|
|
156
220
|
columns={columns}
|
|
157
221
|
data={warehouses}
|
|
@@ -171,11 +171,6 @@ export interface Customer {
|
|
|
171
171
|
address: Address;
|
|
172
172
|
businessLicense?: string;
|
|
173
173
|
taxId?: string;
|
|
174
|
-
website?: string;
|
|
175
|
-
preferredContactMethod: 'email' | 'phone' | 'both';
|
|
176
|
-
marketingOptIn: boolean;
|
|
177
|
-
notes?: string;
|
|
178
|
-
isActive: boolean;
|
|
179
174
|
rating: number;
|
|
180
175
|
createdAt: Date;
|
|
181
176
|
updatedAt: Date;
|
|
@@ -313,8 +308,18 @@ export interface ProofOfDelivery {
|
|
|
313
308
|
}
|
|
314
309
|
|
|
315
310
|
// Vehicle type
|
|
316
|
-
export type VehicleType =
|
|
317
|
-
|
|
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';
|
|
321
|
+
export type VehicleStatus =
|
|
322
|
+
'available' | 'assigned' | 'maintenance' | 'out_of_service' | 'decommissioned';
|
|
318
323
|
|
|
319
324
|
export interface VehicleDocument {
|
|
320
325
|
id: string;
|
|
@@ -333,10 +338,10 @@ export interface Vehicle {
|
|
|
333
338
|
licensePlate: string;
|
|
334
339
|
/** MtwVehicle.ownershipType — free-text, e.g. "owned", "leased". */
|
|
335
340
|
ownershipType?: string;
|
|
336
|
-
year
|
|
341
|
+
year?: number;
|
|
337
342
|
capacity: {
|
|
338
|
-
weight
|
|
339
|
-
volume
|
|
343
|
+
weight?: number;
|
|
344
|
+
volume?: number;
|
|
340
345
|
pallets?: number;
|
|
341
346
|
};
|
|
342
347
|
status: VehicleStatus;
|
|
@@ -346,7 +351,7 @@ export interface Vehicle {
|
|
|
346
351
|
nextMaintenance?: Date;
|
|
347
352
|
/** Odometer reading (km) at which the next service is due. */
|
|
348
353
|
nextMaintenanceMileage?: number;
|
|
349
|
-
fuelType?:
|
|
354
|
+
fuelType?: VehicleFuelType;
|
|
350
355
|
/** Fuel consumption, litres per 100km. */
|
|
351
356
|
fuelEfficiencyLPer100Km?: number;
|
|
352
357
|
fuelLevel?: number;
|
|
@@ -375,7 +380,7 @@ export interface Driver {
|
|
|
375
380
|
employeeId: string;
|
|
376
381
|
licenseNumber: string;
|
|
377
382
|
licenseType: string;
|
|
378
|
-
licenseExpiry
|
|
383
|
+
licenseExpiry?: Date;
|
|
379
384
|
vehicleIds: string[];
|
|
380
385
|
assignedVehicle?: string;
|
|
381
386
|
status: DriverStatus;
|
|
@@ -396,44 +401,18 @@ export interface Driver {
|
|
|
396
401
|
createdAt: Date;
|
|
397
402
|
}
|
|
398
403
|
|
|
399
|
-
// Warehouse zone type
|
|
400
|
-
export interface WarehouseZone {
|
|
401
|
-
id: string;
|
|
402
|
-
name: string;
|
|
403
|
-
type: 'receiving' | 'storage' | 'picking' | 'packing' | 'shipping';
|
|
404
|
-
location: string;
|
|
405
|
-
capacity: number;
|
|
406
|
-
currentOccupancy: number;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Warehouse equipment type
|
|
410
|
-
export interface WarehouseEquipment {
|
|
411
|
-
id: string;
|
|
412
|
-
type: string;
|
|
413
|
-
model: string;
|
|
414
|
-
status: 'operational' | 'maintenance' | 'out_of_service';
|
|
415
|
-
lastMaintenance: Date;
|
|
416
|
-
nextMaintenance: Date;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
404
|
// Warehouse type
|
|
420
405
|
export interface Warehouse {
|
|
421
406
|
id: string;
|
|
422
407
|
name: string;
|
|
423
408
|
code: string;
|
|
424
409
|
address: Address;
|
|
425
|
-
type: 'distribution' | 'fulfillment' | 'cold_storage' | 'cross_dock';
|
|
410
|
+
type: 'distribution' | 'fulfillment' | 'cold_storage' | 'cross_dock' | 'retail';
|
|
411
|
+
/** Raw backend value (free-text, e.g. "ACTIVE"/"INACTIVE" — defaults to ACTIVE). */
|
|
412
|
+
status: string;
|
|
426
413
|
capacity: {
|
|
427
414
|
totalSpace: number;
|
|
428
415
|
usedSpace: number;
|
|
429
|
-
totalPallets: number;
|
|
430
|
-
usedPallets: number;
|
|
431
|
-
specialAreas: Array<{
|
|
432
|
-
type: string;
|
|
433
|
-
capacity: number;
|
|
434
|
-
currentUsage: number;
|
|
435
|
-
temperature?: { min: number; max: number; unit: string };
|
|
436
|
-
}>;
|
|
437
416
|
};
|
|
438
417
|
operatingHours: Record<
|
|
439
418
|
string,
|
|
@@ -442,28 +421,12 @@ export interface Warehouse {
|
|
|
442
421
|
close: string;
|
|
443
422
|
} | null
|
|
444
423
|
>;
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}>;
|
|
449
|
-
zones: WarehouseZone[];
|
|
450
|
-
equipment: WarehouseEquipment[];
|
|
451
|
-
staff: Array<{
|
|
452
|
-
userId: string;
|
|
453
|
-
role: string;
|
|
454
|
-
shift: string;
|
|
455
|
-
permissions: string[];
|
|
456
|
-
}>;
|
|
457
|
-
metrics: {
|
|
458
|
-
throughput: number;
|
|
459
|
-
accuracy: number;
|
|
460
|
-
productivity: number;
|
|
461
|
-
utilization: number;
|
|
462
|
-
ordersFulfilled: number;
|
|
463
|
-
averageProcessingTime: number;
|
|
464
|
-
lastUpdated: Date;
|
|
424
|
+
contactInfo?: {
|
|
425
|
+
email?: string;
|
|
426
|
+
phone?: string;
|
|
465
427
|
};
|
|
466
|
-
|
|
428
|
+
/** Real computed value: usedSpace / totalSpace. No other backend metrics exist for a warehouse. */
|
|
429
|
+
utilization: number;
|
|
467
430
|
createdAt: Date;
|
|
468
431
|
}
|
|
469
432
|
|
|
@@ -487,20 +450,13 @@ export interface Inventory {
|
|
|
487
450
|
productSku?: string;
|
|
488
451
|
warehouseId: string;
|
|
489
452
|
warehouseName?: string;
|
|
490
|
-
zoneId: string;
|
|
491
453
|
quantity: number;
|
|
492
454
|
reservedQuantity: number;
|
|
493
455
|
availableQuantity: number;
|
|
494
|
-
|
|
495
|
-
|
|
456
|
+
damagedQuantity: number;
|
|
457
|
+
/** Undefined means no reorder point is configured — never treat as 0 (matches the backend's low-stock rule, which requires one to be set). */
|
|
458
|
+
reorderLevel?: number;
|
|
496
459
|
lastRestocked: Date;
|
|
497
|
-
location: {
|
|
498
|
-
aisle: string;
|
|
499
|
-
shelf: string;
|
|
500
|
-
position: string;
|
|
501
|
-
level: number;
|
|
502
|
-
};
|
|
503
|
-
movements: InventoryMovement[];
|
|
504
460
|
createdAt: Date;
|
|
505
461
|
updatedAt: Date;
|
|
506
462
|
}
|
|
@@ -540,10 +496,7 @@ export interface Route {
|
|
|
540
496
|
/** Estimated fuel cost for the route (svc MtwRoute.fuelCost). */
|
|
541
497
|
fuelCost?: number;
|
|
542
498
|
actualDuration?: number;
|
|
543
|
-
startTime?: Date;
|
|
544
|
-
endTime?: Date;
|
|
545
499
|
optimized: boolean;
|
|
546
|
-
trafficConditions: TrafficCondition[];
|
|
547
500
|
createdAt: Date;
|
|
548
501
|
}
|
|
549
502
|
|