@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.
Files changed (38) hide show
  1. package/dist/components/StatusBadge.js +30 -30
  2. package/dist/constants/index.js +12 -5
  3. package/dist/hooks/useCustomers.js +0 -3
  4. package/dist/hooks/useFleet.js +144 -131
  5. package/dist/hooks/useInventory.js +144 -145
  6. package/dist/hooks/useRoutes.js +5 -14
  7. package/dist/movethewheels/constants/index.d.ts +1 -0
  8. package/dist/movethewheels/hooks/useFleet.d.ts +13 -1
  9. package/dist/movethewheels/hooks/useInventory.d.ts +10 -0
  10. package/dist/movethewheels/types/index.d.ts +13 -70
  11. package/dist/pages/CustomerDetailsPage.js +666 -344
  12. package/dist/pages/CustomersPage.js +71 -80
  13. package/dist/pages/FleetPage.js +1 -3
  14. package/dist/pages/InventoryPage.js +4 -4
  15. package/dist/pages/RouteDetailsPage.js +273 -248
  16. package/dist/pages/RoutesPage.js +130 -67
  17. package/dist/pages/VehicleDetailsPage.js +506 -391
  18. package/dist/pages/VehiclesPage.js +119 -70
  19. package/dist/pages/WarehouseDetailsPage.js +410 -370
  20. package/dist/pages/WarehousesPage.js +131 -82
  21. package/package.json +1 -1
  22. package/src/movethewheels/components/StatusBadge.tsx +8 -3
  23. package/src/movethewheels/constants/index.ts +9 -0
  24. package/src/movethewheels/hooks/useCustomers.ts +0 -3
  25. package/src/movethewheels/hooks/useFleet.ts +74 -19
  26. package/src/movethewheels/hooks/useInventory.ts +76 -32
  27. package/src/movethewheels/hooks/useRoutes.ts +17 -12
  28. package/src/movethewheels/pages/CustomerDetailsPage.tsx +376 -109
  29. package/src/movethewheels/pages/CustomersPage.tsx +6 -15
  30. package/src/movethewheels/pages/FleetPage.tsx +2 -1
  31. package/src/movethewheels/pages/InventoryPage.tsx +14 -5
  32. package/src/movethewheels/pages/RouteDetailsPage.tsx +74 -34
  33. package/src/movethewheels/pages/RoutesPage.tsx +66 -2
  34. package/src/movethewheels/pages/VehicleDetailsPage.tsx +151 -11
  35. package/src/movethewheels/pages/VehiclesPage.tsx +68 -3
  36. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +213 -135
  37. package/src/movethewheels/pages/WarehousesPage.tsx +77 -16
  38. package/src/movethewheels/types/index.ts +14 -70
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { useEffect, useState } from 'react';
6
6
  import { useLocation, useParams } from 'react-router';
7
- import { Warehouse, ArrowLeft, Edit, MapPin, Clock, Users, Box, Save } from 'lucide-react';
7
+ import { Warehouse, ArrowLeft, Edit, MapPin, Clock, Box, Save, Trash2 } from 'lucide-react';
8
8
  import { PageLayout, LoadingState, ErrorState } from '../components/PageLayout';
9
9
  import {
10
10
  Card,
@@ -22,7 +22,17 @@ import {
22
22
  SelectTrigger,
23
23
  SelectValue,
24
24
  } from '../components/ui';
25
- import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
25
+ import {
26
+ EmphasisPanel,
27
+ NextSteps,
28
+ PagePurpose,
29
+ ResponsiveDialog,
30
+ ResponsiveDialogContent,
31
+ ResponsiveDialogHeader,
32
+ ResponsiveDialogFooter,
33
+ ResponsiveDialogTitle,
34
+ ResponsiveDialogDescription,
35
+ } from '@burdenoff/fe-libs/ui';
26
36
  import { useWarehouse, useInventory } from '../hooks/useInventory';
27
37
  import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
28
38
  import { formatAddress } from '../utils/formatters';
@@ -37,24 +47,36 @@ const WAREHOUSE_TYPE_OPTIONS = [
37
47
  { value: 'RETAIL', label: 'Retail' },
38
48
  ];
39
49
 
50
+ const WAREHOUSE_STATUS_OPTIONS = [
51
+ { value: 'ACTIVE', label: 'Active' },
52
+ { value: 'INACTIVE', label: 'Inactive' },
53
+ ];
54
+
40
55
  function NewWarehouseForm({ editId }: { editId?: string }) {
41
56
  const { basePath, navigate } = useMoveTheWheels();
42
- const { createWarehouse, updateWarehouse } = useInventory();
57
+ const { createWarehouse, updateWarehouse, deleteWarehouse } = useInventory();
43
58
  const { warehouse } = useWarehouse(editId);
44
59
  const isEditing = !!editId;
45
60
  const [form, setForm] = useState({
46
61
  code: '',
47
62
  name: '',
48
63
  type: 'DISTRIBUTION',
64
+ status: 'ACTIVE',
49
65
  street: '',
50
66
  city: '',
51
67
  state: '',
52
68
  postalCode: '',
53
69
  country: 'USA',
54
70
  totalCapacity: '',
71
+ usedCapacity: '',
72
+ contactEmail: '',
73
+ contactPhone: '',
55
74
  });
56
75
  const [submitting, setSubmitting] = useState(false);
57
76
  const [error, setError] = useState<string | null>(null);
77
+ const [deleteOpen, setDeleteOpen] = useState(false);
78
+ const [deleting, setDeleting] = useState(false);
79
+ const [deleteError, setDeleteError] = useState<string | null>(null);
58
80
 
59
81
  useEffect(() => {
60
82
  if (!editId || !warehouse) return;
@@ -62,12 +84,16 @@ function NewWarehouseForm({ editId }: { editId?: string }) {
62
84
  code: warehouse.code || '',
63
85
  name: warehouse.name || '',
64
86
  type: warehouse.type ? warehouse.type.toUpperCase() : 'DISTRIBUTION',
87
+ status: warehouse.status || 'ACTIVE',
65
88
  street: warehouse.address.street || '',
66
89
  city: warehouse.address.city || '',
67
90
  state: warehouse.address.state || '',
68
91
  postalCode: warehouse.address.postalCode || '',
69
92
  country: warehouse.address.country || 'USA',
70
93
  totalCapacity: warehouse.capacity.totalSpace ? String(warehouse.capacity.totalSpace) : '',
94
+ usedCapacity: warehouse.capacity.usedSpace ? String(warehouse.capacity.usedSpace) : '',
95
+ contactEmail: warehouse.contactInfo?.email || '',
96
+ contactPhone: warehouse.contactInfo?.phone || '',
71
97
  });
72
98
  }, [editId, warehouse]);
73
99
 
@@ -90,10 +116,12 @@ function NewWarehouseForm({ editId }: { editId?: string }) {
90
116
  setError(null);
91
117
  try {
92
118
  const totalCapacity = form.totalCapacity.trim() ? Number(form.totalCapacity) : undefined;
119
+ const usedCapacity = form.usedCapacity.trim() ? Number(form.usedCapacity) : undefined;
93
120
  const warehouseInput = {
94
121
  code: form.code.trim(),
95
122
  name: form.name.trim(),
96
123
  type: form.type,
124
+ status: form.status,
97
125
  address: {
98
126
  street: form.street.trim(),
99
127
  city: form.city.trim(),
@@ -101,8 +129,17 @@ function NewWarehouseForm({ editId }: { editId?: string }) {
101
129
  postalCode: form.postalCode.trim(),
102
130
  country: form.country.trim(),
103
131
  },
132
+ contactInfo:
133
+ form.contactEmail.trim() || form.contactPhone.trim()
134
+ ? {
135
+ email: form.contactEmail.trim() || undefined,
136
+ phone: form.contactPhone.trim() || undefined,
137
+ }
138
+ : undefined,
104
139
  totalCapacity:
105
140
  totalCapacity !== undefined && !Number.isNaN(totalCapacity) ? totalCapacity : undefined,
141
+ usedCapacity:
142
+ usedCapacity !== undefined && !Number.isNaN(usedCapacity) ? usedCapacity : undefined,
106
143
  };
107
144
  if (editId) {
108
145
  await updateWarehouse(editId, warehouseInput);
@@ -120,6 +157,20 @@ function NewWarehouseForm({ editId }: { editId?: string }) {
120
157
  }
121
158
  };
122
159
 
160
+ const handleDelete = async () => {
161
+ if (!editId) return;
162
+ setDeleting(true);
163
+ setDeleteError(null);
164
+ try {
165
+ await deleteWarehouse(editId);
166
+ setDeleteOpen(false);
167
+ goBack();
168
+ } catch (err) {
169
+ setDeleteError(err instanceof Error ? err.message : 'Failed to delete warehouse.');
170
+ setDeleting(false);
171
+ }
172
+ };
173
+
123
174
  return (
124
175
  <PageLayout
125
176
  title={isEditing ? 'Edit Warehouse' : 'Add Warehouse'}
@@ -196,16 +247,65 @@ function NewWarehouseForm({ editId }: { editId?: string }) {
196
247
  </Select>
197
248
  </div>
198
249
  </div>
199
- <div className="space-y-2">
200
- <Label htmlFor="totalCapacity">Total Capacity (sq ft)</Label>
201
- <Input
202
- id="totalCapacity"
203
- type="number"
204
- min="0"
205
- value={form.totalCapacity}
206
- onChange={(e) => update('totalCapacity', e.target.value)}
207
- placeholder="50000"
208
- />
250
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
251
+ <div className="space-y-2">
252
+ <Label htmlFor="status">Status</Label>
253
+ <Select value={form.status} onValueChange={(value) => update('status', value)}>
254
+ <SelectTrigger id="status">
255
+ <SelectValue />
256
+ </SelectTrigger>
257
+ <SelectContent>
258
+ {WAREHOUSE_STATUS_OPTIONS.map((option) => (
259
+ <SelectItem key={option.value} value={option.value}>
260
+ {option.label}
261
+ </SelectItem>
262
+ ))}
263
+ </SelectContent>
264
+ </Select>
265
+ </div>
266
+ <div className="space-y-2">
267
+ <Label htmlFor="totalCapacity">Total Capacity (sq ft)</Label>
268
+ <Input
269
+ id="totalCapacity"
270
+ type="number"
271
+ min="0"
272
+ value={form.totalCapacity}
273
+ onChange={(e) => update('totalCapacity', e.target.value)}
274
+ placeholder="50000"
275
+ />
276
+ </div>
277
+ <div className="space-y-2">
278
+ <Label htmlFor="usedCapacity">Used Capacity (sq ft)</Label>
279
+ <Input
280
+ id="usedCapacity"
281
+ type="number"
282
+ min="0"
283
+ value={form.usedCapacity}
284
+ onChange={(e) => update('usedCapacity', e.target.value)}
285
+ placeholder="0"
286
+ />
287
+ </div>
288
+ </div>
289
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
290
+ <div className="space-y-2">
291
+ <Label htmlFor="contactEmail">Contact Email</Label>
292
+ <Input
293
+ id="contactEmail"
294
+ type="email"
295
+ value={form.contactEmail}
296
+ onChange={(e) => update('contactEmail', e.target.value)}
297
+ placeholder="dc-sf@example.com"
298
+ />
299
+ </div>
300
+ <div className="space-y-2">
301
+ <Label htmlFor="contactPhone">Contact Phone</Label>
302
+ <Input
303
+ id="contactPhone"
304
+ value={form.contactPhone}
305
+ onChange={(e) => update('contactPhone', e.target.value)}
306
+ placeholder="+1-555-0100"
307
+ />
308
+ </div>
209
309
  </div>
210
310
  </div>
211
311
  </EmphasisPanel>
@@ -268,7 +368,56 @@ function NewWarehouseForm({ editId }: { editId?: string }) {
268
368
  </div>
269
369
  </CardContent>
270
370
  </Card>
371
+
372
+ {isEditing && (
373
+ <Card className="border-status-error-border">
374
+ <CardHeader>
375
+ <CardTitle className="flex items-center gap-2 text-status-error-text">
376
+ <Trash2 size={18} /> Delete Warehouse
377
+ </CardTitle>
378
+ </CardHeader>
379
+ <CardContent>
380
+ <p className="mb-3 text-sm text-text-secondary">
381
+ Removes this warehouse from your network. This cannot be undone from here.
382
+ </p>
383
+ <Button
384
+ type="button"
385
+ variant="outline"
386
+ className="border-status-error-border text-status-error-text hover:bg-status-error-bg-subtle"
387
+ onClick={() => setDeleteOpen(true)}
388
+ >
389
+ <Trash2 size={16} /> Delete this warehouse
390
+ </Button>
391
+ </CardContent>
392
+ </Card>
393
+ )}
271
394
  </form>
395
+
396
+ <ResponsiveDialog open={deleteOpen} onOpenChange={setDeleteOpen}>
397
+ <ResponsiveDialogContent>
398
+ <ResponsiveDialogHeader>
399
+ <ResponsiveDialogTitle>Delete this warehouse?</ResponsiveDialogTitle>
400
+ <ResponsiveDialogDescription>
401
+ This removes {form.name || 'this warehouse'} and cannot be undone from here.
402
+ </ResponsiveDialogDescription>
403
+ </ResponsiveDialogHeader>
404
+ <div className="px-1">
405
+ <FormError message={deleteError} />
406
+ </div>
407
+ <ResponsiveDialogFooter>
408
+ <Button variant="outline" onClick={() => setDeleteOpen(false)} disabled={deleting}>
409
+ Cancel
410
+ </Button>
411
+ <Button
412
+ className="bg-status-error-text text-text-inverse hover:opacity-90"
413
+ onClick={handleDelete}
414
+ disabled={deleting}
415
+ >
416
+ {deleting ? 'Deleting...' : 'Delete warehouse'}
417
+ </Button>
418
+ </ResponsiveDialogFooter>
419
+ </ResponsiveDialogContent>
420
+ </ResponsiveDialog>
272
421
  </PageLayout>
273
422
  );
274
423
  }
@@ -287,7 +436,7 @@ export default function WarehouseDetailsPage() {
287
436
  if (isLoading) return <LoadingState message="Loading warehouse details..." />;
288
437
  if (error || !warehouse) return <ErrorState title="Warehouse not found" onRetry={refetch} />;
289
438
 
290
- const utilization = (warehouse.capacity.usedSpace / warehouse.capacity.totalSpace) * 100;
439
+ const isActive = warehouse.status.toUpperCase() === 'ACTIVE';
291
440
 
292
441
  return (
293
442
  <PageLayout
@@ -310,8 +459,8 @@ export default function WarehouseDetailsPage() {
310
459
  }
311
460
  headerContent={
312
461
  <div className="flex gap-2">
313
- <Badge variant={warehouse.isActive ? 'default' : 'secondary'}>
314
- {warehouse.isActive ? 'Active' : 'Inactive'}
462
+ <Badge variant={isActive ? 'default' : 'secondary'}>
463
+ {isActive ? 'Active' : 'Inactive'}
315
464
  </Badge>
316
465
  <Badge variant="outline" className="capitalize">
317
466
  {warehouse.type.replace('_', ' ')}
@@ -320,9 +469,9 @@ export default function WarehouseDetailsPage() {
320
469
  }
321
470
  >
322
471
  <PagePurpose icon={<Warehouse size={18} />}>
323
- A storage and fulfilment site at a glance — how full it is, its zones, operating hours,
324
- throughput and staff. Use it to decide whether this warehouse has the space and hours to
325
- fulfil an order, and where stock is running tight.
472
+ A storage and fulfilment site at a glance — how full it is, its operating hours and how to
473
+ reach it. Use it to decide whether this warehouse has the space and hours to fulfil an
474
+ order.
326
475
  </PagePurpose>
327
476
 
328
477
  <NextSteps
@@ -352,65 +501,19 @@ export default function WarehouseDetailsPage() {
352
501
  <h2 className="flex items-center gap-2 text-base font-semibold text-text-primary mb-4">
353
502
  <Box size={18} /> Capacity
354
503
  </h2>
355
- <div className="space-y-4">
356
- <div>
357
- <div className="flex justify-between text-sm mb-2">
358
- <span>Space Utilization</span>
359
- <span>{utilization.toFixed(0)}%</span>
360
- </div>
361
- <Progress value={utilization} />
362
- <p className="text-sm text-text-secondary mt-1">
363
- {warehouse.capacity.usedSpace.toLocaleString()} /{' '}
364
- {warehouse.capacity.totalSpace.toLocaleString()} sq ft
365
- </p>
366
- </div>
367
- <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
368
- <div>
369
- <p className="text-sm text-text-secondary">Pallets Used</p>
370
- <p className="text-2xl font-bold">
371
- {warehouse.capacity.usedPallets}
372
- <span className="text-sm font-normal text-text-secondary">
373
- {' '}
374
- / {warehouse.capacity.totalPallets}
375
- </span>
376
- </p>
377
- </div>
504
+ <div>
505
+ <div className="flex justify-between text-sm mb-2">
506
+ <span>Space Utilization</span>
507
+ <span>{warehouse.utilization.toFixed(0)}%</span>
378
508
  </div>
509
+ <Progress value={warehouse.utilization} />
510
+ <p className="text-sm text-text-secondary mt-1">
511
+ {warehouse.capacity.usedSpace.toLocaleString()} /{' '}
512
+ {warehouse.capacity.totalSpace.toLocaleString()} sq ft
513
+ </p>
379
514
  </div>
380
515
  </EmphasisPanel>
381
516
 
382
- <Card>
383
- <CardHeader>
384
- <CardTitle>Zones ({warehouse.zones.length})</CardTitle>
385
- </CardHeader>
386
- <CardContent>
387
- <div className="space-y-3">
388
- {warehouse.zones.map((zone) => (
389
- <div
390
- key={zone.id}
391
- className="flex items-center justify-between p-3 border rounded-lg"
392
- >
393
- <div>
394
- <p className="font-medium">{zone.name}</p>
395
- <p className="text-sm text-text-secondary capitalize">
396
- {zone.type} - {zone.location}
397
- </p>
398
- </div>
399
- <div className="text-right">
400
- <Progress
401
- value={(zone.currentOccupancy / zone.capacity) * 100}
402
- className="w-24 h-2"
403
- />
404
- <p className="text-sm text-text-secondary">
405
- {zone.currentOccupancy} / {zone.capacity}
406
- </p>
407
- </div>
408
- </div>
409
- ))}
410
- </div>
411
- </CardContent>
412
- </Card>
413
-
414
517
  <Card>
415
518
  <CardHeader>
416
519
  <CardTitle className="flex items-center gap-2">
@@ -418,16 +521,20 @@ export default function WarehouseDetailsPage() {
418
521
  </CardTitle>
419
522
  </CardHeader>
420
523
  <CardContent>
421
- <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
422
- {Object.entries(warehouse.operatingHours).map(([day, hours]) => (
423
- <div key={day}>
424
- <p className="text-sm font-medium">{day}</p>
425
- <p className="text-sm text-text-secondary">
426
- {hours ? `${hours.open} - ${hours.close}` : 'Closed'}
427
- </p>
428
- </div>
429
- ))}
430
- </div>
524
+ {Object.keys(warehouse.operatingHours).length > 0 ? (
525
+ <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
526
+ {Object.entries(warehouse.operatingHours).map(([day, hours]) => (
527
+ <div key={day}>
528
+ <p className="text-sm font-medium">{day}</p>
529
+ <p className="text-sm text-text-secondary">
530
+ {hours ? `${hours.open} - ${hours.close}` : 'Closed'}
531
+ </p>
532
+ </div>
533
+ ))}
534
+ </div>
535
+ ) : (
536
+ <p className="text-sm text-text-secondary">Not recorded</p>
537
+ )}
431
538
  </CardContent>
432
539
  </Card>
433
540
  </div>
@@ -440,60 +547,31 @@ export default function WarehouseDetailsPage() {
440
547
  </CardTitle>
441
548
  </CardHeader>
442
549
  <CardContent>
443
- <p className="text-sm">{formatAddress(warehouse.address)}</p>
550
+ <p className="text-sm">{formatAddress(warehouse.address) || '—'}</p>
444
551
  </CardContent>
445
552
  </Card>
446
553
 
447
- <Card>
448
- <CardHeader>
449
- <CardTitle>Metrics</CardTitle>
450
- </CardHeader>
451
- <CardContent className="space-y-3">
452
- <div>
453
- <p className="text-sm text-text-secondary">Throughput</p>
454
- <p className="text-2xl font-bold">
455
- {warehouse.metrics.throughput}
456
- <span className="text-sm font-normal"> orders/day</span>
457
- </p>
458
- </div>
459
- <div>
460
- <p className="text-sm text-text-secondary">Accuracy</p>
461
- <p className="text-2xl font-bold">{warehouse.metrics.accuracy}%</p>
462
- </div>
463
- <div>
464
- <p className="text-sm text-text-secondary">Productivity</p>
465
- <p className="text-2xl font-bold">{warehouse.metrics.productivity}%</p>
466
- </div>
467
- <div>
468
- <p className="text-sm text-text-secondary">Orders Fulfilled</p>
469
- <p className="text-2xl font-bold">
470
- {warehouse.metrics.ordersFulfilled.toLocaleString()}
471
- </p>
472
- </div>
473
- </CardContent>
474
- </Card>
475
-
476
- <Card>
477
- <CardHeader>
478
- <CardTitle className="flex items-center gap-2">
479
- <Users size={18} /> Staff ({warehouse.staff.length})
480
- </CardTitle>
481
- </CardHeader>
482
- <CardContent>
483
- {warehouse.staff.length > 0 ? (
484
- <div className="space-y-2">
485
- {warehouse.staff.map((member, i) => (
486
- <div key={i} className="flex items-center justify-between p-2 border rounded">
487
- <span className="text-sm capitalize">{member.role}</span>
488
- <Badge variant="outline">{member.shift}</Badge>
489
- </div>
490
- ))}
491
- </div>
492
- ) : (
493
- <p className="text-text-secondary">No staff assigned</p>
494
- )}
495
- </CardContent>
496
- </Card>
554
+ {(warehouse.contactInfo?.email || warehouse.contactInfo?.phone) && (
555
+ <Card>
556
+ <CardHeader>
557
+ <CardTitle>Contact</CardTitle>
558
+ </CardHeader>
559
+ <CardContent className="space-y-3">
560
+ {warehouse.contactInfo?.email && (
561
+ <div>
562
+ <p className="text-sm text-text-secondary">Email</p>
563
+ <p className="font-medium">{warehouse.contactInfo.email}</p>
564
+ </div>
565
+ )}
566
+ {warehouse.contactInfo?.phone && (
567
+ <div>
568
+ <p className="text-sm text-text-secondary">Phone</p>
569
+ <p className="font-medium">{warehouse.contactInfo.phone}</p>
570
+ </div>
571
+ )}
572
+ </CardContent>
573
+ </Card>
574
+ )}
497
575
  </div>
498
576
  </div>
499
577
  </PageLayout>
@@ -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 { Badge, Progress } from '../components/ui';
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
 
@@ -83,25 +103,23 @@ export default function WarehousesPage() {
83
103
  id: 'capacity',
84
104
  header: 'Capacity',
85
105
  cell: (row) => (
86
- <div className="text-sm">
87
- <p>
88
- {row.capacity.usedSpace.toLocaleString()} / {row.capacity.totalSpace.toLocaleString()}{' '}
89
- sq ft
90
- </p>
91
- <p className="text-text-secondary">
92
- {row.capacity.usedPallets} / {row.capacity.totalPallets} pallets
93
- </p>
94
- </div>
106
+ <p className="text-sm">
107
+ {row.capacity.usedSpace.toLocaleString()} / {row.capacity.totalSpace.toLocaleString()} sq
108
+ ft
109
+ </p>
95
110
  ),
96
111
  },
97
112
  {
98
113
  id: 'status',
99
114
  header: 'Status',
100
- cell: (row) => (
101
- <Badge variant={row.isActive ? 'default' : 'secondary'}>
102
- {row.isActive ? 'Active' : 'Inactive'}
103
- </Badge>
104
- ),
115
+ cell: (row) => {
116
+ const isActive = row.status.toUpperCase() === 'ACTIVE';
117
+ return (
118
+ <Badge variant={isActive ? 'default' : 'secondary'}>
119
+ {isActive ? 'Active' : 'Inactive'}
120
+ </Badge>
121
+ );
122
+ },
105
123
  },
106
124
  ];
107
125
 
@@ -152,6 +170,49 @@ export default function WarehousesPage() {
152
170
  </MetricGrid>
153
171
  </div>
154
172
 
173
+ {/* Filters */}
174
+ <Card>
175
+ <CardContent className="p-4">
176
+ <div className="flex flex-wrap items-center gap-4">
177
+ <div className="flex items-center gap-2">
178
+ <Filter size={16} className="text-text-secondary" />
179
+ <span className="text-sm font-medium">Filters:</span>
180
+ </div>
181
+ <Select
182
+ value={statusFilter}
183
+ onValueChange={(value) => {
184
+ setStatusFilter(value);
185
+ setPage(0);
186
+ }}
187
+ >
188
+ <SelectTrigger className="w-[180px]">
189
+ <SelectValue placeholder="Status" />
190
+ </SelectTrigger>
191
+ <SelectContent>
192
+ {STATUS_OPTIONS.map((option) => (
193
+ <SelectItem key={option.value} value={option.value}>
194
+ {option.label}
195
+ </SelectItem>
196
+ ))}
197
+ </SelectContent>
198
+ </Select>
199
+ {filtersActive && (
200
+ <Button
201
+ variant="ghost"
202
+ size="sm"
203
+ onClick={() => {
204
+ setStatusFilter('all');
205
+ setSearch('');
206
+ setPage(0);
207
+ }}
208
+ >
209
+ Clear filters
210
+ </Button>
211
+ )}
212
+ </div>
213
+ </CardContent>
214
+ </Card>
215
+
155
216
  <DataTable
156
217
  columns={columns}
157
218
  data={warehouses}