@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.
Files changed (50) hide show
  1. package/dist/generated/wspace-types.d.ts +1547 -64
  2. package/dist/hooks/useCustomers.js +3 -9
  3. package/dist/hooks/useFleet.js +146 -149
  4. package/dist/hooks/useInventory.js +178 -153
  5. package/dist/hooks/useRoutes.js +9 -9
  6. package/dist/hooks/useTracking.js +14 -16
  7. package/dist/movethewheels/hooks/useFleet.d.ts +9 -2
  8. package/dist/movethewheels/hooks/useInventory.d.ts +13 -12
  9. package/dist/movethewheels/hooks/useRoutes.d.ts +4 -1
  10. package/dist/movethewheels/types/index.d.ts +33 -23
  11. package/dist/movethewheels/utils/formatters.d.ts +1 -1
  12. package/dist/pages/CustomerDetailsPage.js +6 -6
  13. package/dist/pages/CustomersPage.js +2 -2
  14. package/dist/pages/DashboardPage.js +1 -1
  15. package/dist/pages/DriverDetailsPage.js +7 -3
  16. package/dist/pages/DriversPage.js +4 -4
  17. package/dist/pages/FleetPage.js +1 -1
  18. package/dist/pages/InventoryPage.js +11 -12
  19. package/dist/pages/LiveTrackingPage.js +4 -4
  20. package/dist/pages/ProductDetailsPage.js +408 -249
  21. package/dist/pages/ProductsPage.js +5 -5
  22. package/dist/pages/RouteDetailsPage.js +8 -10
  23. package/dist/pages/RoutesPage.js +2 -2
  24. package/dist/pages/TrackingPage.js +14 -8
  25. package/dist/pages/WarehouseDetailsPage.js +166 -163
  26. package/dist/pages/WarehousesPage.js +29 -21
  27. package/package.json +1 -1
  28. package/src/generated/wspace-types.ts +1762 -64
  29. package/src/movethewheels/hooks/useCustomers.ts +7 -9
  30. package/src/movethewheels/hooks/useFleet.ts +89 -31
  31. package/src/movethewheels/hooks/useInventory.ts +143 -49
  32. package/src/movethewheels/hooks/useRoutes.ts +36 -16
  33. package/src/movethewheels/hooks/useTracking.ts +23 -15
  34. package/src/movethewheels/pages/CustomerDetailsPage.tsx +14 -8
  35. package/src/movethewheels/pages/CustomersPage.tsx +5 -2
  36. package/src/movethewheels/pages/DashboardPage.tsx +3 -1
  37. package/src/movethewheels/pages/DriverDetailsPage.tsx +13 -3
  38. package/src/movethewheels/pages/DriversPage.tsx +1 -1
  39. package/src/movethewheels/pages/FleetPage.tsx +1 -1
  40. package/src/movethewheels/pages/InventoryPage.tsx +20 -16
  41. package/src/movethewheels/pages/LiveTrackingPage.tsx +6 -4
  42. package/src/movethewheels/pages/ProductDetailsPage.tsx +228 -49
  43. package/src/movethewheels/pages/ProductsPage.tsx +13 -5
  44. package/src/movethewheels/pages/RouteDetailsPage.tsx +16 -8
  45. package/src/movethewheels/pages/RoutesPage.tsx +4 -2
  46. package/src/movethewheels/pages/TrackingPage.tsx +25 -10
  47. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +42 -29
  48. package/src/movethewheels/pages/WarehousesPage.tsx +23 -15
  49. package/src/movethewheels/types/index.ts +36 -24
  50. 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: 'ACTIVE',
64
+ status: '',
65
65
  street: '',
66
66
  city: '',
67
67
  state: '',
68
68
  postalCode: '',
69
- country: 'USA',
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 || 'ACTIVE',
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 || 'USA',
93
- totalCapacity: warehouse.capacity.totalSpace ? String(warehouse.capacity.totalSpace) : '',
94
- usedCapacity: warehouse.capacity.usedSpace ? String(warehouse.capacity.usedSpace) : '',
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 warehouseInput = {
121
- code: form.code.trim(),
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.toUpperCase() === 'ACTIVE';
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
- <div className="flex justify-between text-sm mb-2">
507
- <span>Space Utilization</span>
508
- <span>{warehouse.utilization.toFixed(0)}%</span>
509
- </div>
510
- <Progress value={warehouse.utilization} />
511
- <p className="text-sm text-text-secondary mt-1">
512
- {warehouse.capacity.usedSpace.toLocaleString()} /{' '}
513
- {warehouse.capacity.totalSpace.toLocaleString()} sq ft
514
- </p>
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}, {row.address.state}
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 pct =
94
- row.capacity.totalSpace > 0
95
- ? (row.capacity.usedSpace / row.capacity.totalSpace) * 100
96
- : 0;
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
- <p className="text-sm">
110
- {row.capacity.usedSpace.toLocaleString()} / {row.capacity.totalSpace.toLocaleString()} sq
111
- ft
112
- </p>
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.toUpperCase() === 'ACTIVE';
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: number;
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: string;
185
- category: string;
192
+ description?: string;
193
+ category?: string;
186
194
  brand?: string;
187
- dimensions: Dimensions;
188
- weight: number;
189
- value: number;
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: number;
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" — defaults to ACTIVE). */
412
- status: string;
422
+ /** Raw backend value (free-text, e.g. "ACTIVE"/"INACTIVE"). */
423
+ status?: string;
413
424
  capacity: {
414
- totalSpace: number;
415
- usedSpace: number;
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. No other backend metrics exist for a warehouse. */
429
- utilization: number;
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: Date;
482
+ scheduledTime?: Date;
472
483
  actualTime?: Date;
473
- duration: number;
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: string;
491
- vehicleId: string;
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
- optimized: boolean;
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: Date;
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: Location;
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: Date;
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,