@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.
Files changed (43) hide show
  1. package/dist/components/StatusBadge.js +34 -31
  2. package/dist/constants/index.js +12 -5
  3. package/dist/hooks/useCustomers.js +0 -3
  4. package/dist/hooks/useFleet.js +148 -135
  5. package/dist/hooks/useInventory.js +146 -146
  6. package/dist/hooks/useRoutes.js +5 -14
  7. package/dist/movethewheels/components/StatusBadge.d.ts +3 -1
  8. package/dist/movethewheels/constants/index.d.ts +1 -0
  9. package/dist/movethewheels/hooks/useFleet.d.ts +17 -4
  10. package/dist/movethewheels/hooks/useInventory.d.ts +11 -0
  11. package/dist/movethewheels/types/index.d.ts +19 -75
  12. package/dist/pages/CustomerDetailsPage.js +666 -344
  13. package/dist/pages/CustomersPage.js +71 -80
  14. package/dist/pages/DriverDetailsPage.js +2 -2
  15. package/dist/pages/DriversPage.js +2 -2
  16. package/dist/pages/FleetPage.js +1 -3
  17. package/dist/pages/InventoryPage.js +5 -5
  18. package/dist/pages/RouteDetailsPage.js +273 -248
  19. package/dist/pages/RoutesPage.js +130 -67
  20. package/dist/pages/VehicleDetailsPage.js +527 -389
  21. package/dist/pages/VehiclesPage.js +120 -71
  22. package/dist/pages/WarehouseDetailsPage.js +411 -368
  23. package/dist/pages/WarehousesPage.js +132 -83
  24. package/package.json +1 -1
  25. package/src/movethewheels/components/StatusBadge.tsx +15 -7
  26. package/src/movethewheels/constants/index.ts +9 -0
  27. package/src/movethewheels/hooks/useCustomers.ts +0 -3
  28. package/src/movethewheels/hooks/useFleet.ts +89 -25
  29. package/src/movethewheels/hooks/useInventory.ts +77 -33
  30. package/src/movethewheels/hooks/useRoutes.ts +17 -12
  31. package/src/movethewheels/pages/CustomerDetailsPage.tsx +376 -109
  32. package/src/movethewheels/pages/CustomersPage.tsx +6 -15
  33. package/src/movethewheels/pages/DriverDetailsPage.tsx +3 -1
  34. package/src/movethewheels/pages/DriversPage.tsx +5 -1
  35. package/src/movethewheels/pages/FleetPage.tsx +2 -1
  36. package/src/movethewheels/pages/InventoryPage.tsx +18 -6
  37. package/src/movethewheels/pages/RouteDetailsPage.tsx +74 -34
  38. package/src/movethewheels/pages/RoutesPage.tsx +66 -2
  39. package/src/movethewheels/pages/VehicleDetailsPage.tsx +185 -23
  40. package/src/movethewheels/pages/VehiclesPage.tsx +72 -5
  41. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +217 -138
  42. package/src/movethewheels/pages/WarehousesPage.tsx +81 -17
  43. package/src/movethewheels/types/index.ts +28 -75
@@ -17,6 +17,7 @@ import {
17
17
  Save,
18
18
  Package,
19
19
  X,
20
+ Trash2,
20
21
  } from 'lucide-react';
21
22
  import { PageLayout, LoadingState, ErrorState } from '../components/PageLayout';
22
23
  import { TypeBadge, StatusBadge } from '../components/StatusBadge';
@@ -26,7 +27,6 @@ import {
26
27
  CardHeader,
27
28
  CardTitle,
28
29
  Button,
29
- Badge,
30
30
  Input,
31
31
  Label,
32
32
  Select,
@@ -35,7 +35,17 @@ import {
35
35
  SelectTrigger,
36
36
  SelectValue,
37
37
  } from '../components/ui';
38
- import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
38
+ import {
39
+ EmphasisPanel,
40
+ NextSteps,
41
+ PagePurpose,
42
+ ResponsiveDialog,
43
+ ResponsiveDialogContent,
44
+ ResponsiveDialogHeader,
45
+ ResponsiveDialogFooter,
46
+ ResponsiveDialogTitle,
47
+ ResponsiveDialogDescription,
48
+ } from '@burdenoff/fe-libs/ui';
39
49
  import { useCustomer, useCustomers } from '../hooks/useCustomers';
40
50
  import { useOrders } from '../hooks/useOrders';
41
51
  import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
@@ -44,34 +54,44 @@ import { joinPath, buildOrderCustomerPath, getSafeCustomerReturnPath } from '../
44
54
  import { FormError } from '../components/FormError';
45
55
  import { Notice, useNotice } from '../components/Notice';
46
56
 
47
- /**
48
- * Only surface http(s) links. Anything else (javascript:, data:, mailto:, etc.)
49
- * is unsafe to render as a raw href, so we drop it.
50
- */
51
- function safeHttpUrl(raw: string | undefined): string | null {
52
- if (!raw) return null;
53
- const value = raw.trim();
54
- const candidate = /^https?:\/\//i.test(value) ? value : `https://${value}`;
55
- try {
56
- const parsed = new URL(candidate);
57
- return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? parsed.href : null;
58
- } catch {
59
- return null;
60
- }
57
+ interface CustomerFormState {
58
+ name: string;
59
+ type: 'b2b' | 'b2c';
60
+ email: string;
61
+ phone: string;
62
+ company: string;
63
+ street: string;
64
+ city: string;
65
+ state: string;
66
+ postalCode: string;
67
+ country: string;
68
+ businessLicense: string;
69
+ taxId: string;
70
+ rating: string;
61
71
  }
62
72
 
73
+ const emptyCustomerForm: CustomerFormState = {
74
+ name: '',
75
+ type: 'b2c',
76
+ email: '',
77
+ phone: '',
78
+ company: '',
79
+ street: '',
80
+ city: '',
81
+ state: '',
82
+ postalCode: '',
83
+ country: '',
84
+ businessLicense: '',
85
+ taxId: '',
86
+ rating: '',
87
+ };
88
+
63
89
  function NewCustomerForm() {
64
90
  const { basePath, navigate } = useMoveTheWheels();
65
91
  const [searchParams] = useSearchParams();
66
92
  const returnTo = getSafeCustomerReturnPath(searchParams.get('returnTo'));
67
93
  const { createCustomer } = useCustomers();
68
- const [form, setForm] = useState({
69
- name: '',
70
- type: 'b2c' as 'b2b' | 'b2c',
71
- email: '',
72
- phone: '',
73
- company: '',
74
- });
94
+ const [form, setForm] = useState<CustomerFormState>(emptyCustomerForm);
75
95
  const [submitting, setSubmitting] = useState(false);
76
96
  const [error, setError] = useState<string | null>(null);
77
97
 
@@ -92,6 +112,19 @@ function NewCustomerForm() {
92
112
  email: form.email.trim() || undefined,
93
113
  phone: form.phone.trim() || undefined,
94
114
  company: form.company.trim() || undefined,
115
+ address:
116
+ form.street || form.city || form.state || form.postalCode || form.country
117
+ ? {
118
+ street: form.street.trim(),
119
+ city: form.city.trim(),
120
+ state: form.state.trim(),
121
+ postalCode: form.postalCode.trim(),
122
+ country: form.country.trim(),
123
+ }
124
+ : undefined,
125
+ businessLicense: form.businessLicense.trim() || undefined,
126
+ taxId: form.taxId.trim() || undefined,
127
+ rating: form.rating ? Number(form.rating) : undefined,
95
128
  });
96
129
  const destination = returnTo
97
130
  ? buildOrderCustomerPath(returnTo, created.id)
@@ -194,37 +227,153 @@ function NewCustomerForm() {
194
227
  </div>
195
228
  </div>
196
229
  </EmphasisPanel>
230
+
231
+ <Card>
232
+ <CardHeader>
233
+ <CardTitle className="flex items-center gap-2">
234
+ <MapPin size={18} /> Address
235
+ </CardTitle>
236
+ </CardHeader>
237
+ <CardContent className="space-y-4">
238
+ <div className="space-y-2">
239
+ <Label htmlFor="street">Street</Label>
240
+ <Input
241
+ id="street"
242
+ value={form.street}
243
+ onChange={(e) => setForm((prev) => ({ ...prev, street: e.target.value }))}
244
+ placeholder="123 Main St"
245
+ />
246
+ </div>
247
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
248
+ <div className="space-y-2">
249
+ <Label htmlFor="city">City</Label>
250
+ <Input
251
+ id="city"
252
+ value={form.city}
253
+ onChange={(e) => setForm((prev) => ({ ...prev, city: e.target.value }))}
254
+ />
255
+ </div>
256
+ <div className="space-y-2">
257
+ <Label htmlFor="state">State</Label>
258
+ <Input
259
+ id="state"
260
+ value={form.state}
261
+ onChange={(e) => setForm((prev) => ({ ...prev, state: e.target.value }))}
262
+ />
263
+ </div>
264
+ <div className="space-y-2">
265
+ <Label htmlFor="postalCode">Postal Code</Label>
266
+ <Input
267
+ id="postalCode"
268
+ value={form.postalCode}
269
+ onChange={(e) => setForm((prev) => ({ ...prev, postalCode: e.target.value }))}
270
+ />
271
+ </div>
272
+ <div className="space-y-2">
273
+ <Label htmlFor="country">Country</Label>
274
+ <Input
275
+ id="country"
276
+ value={form.country}
277
+ onChange={(e) => setForm((prev) => ({ ...prev, country: e.target.value }))}
278
+ />
279
+ </div>
280
+ </div>
281
+ </CardContent>
282
+ </Card>
283
+
284
+ <Card>
285
+ <CardHeader>
286
+ <CardTitle className="flex items-center gap-2">
287
+ <FileText size={18} /> Business Details
288
+ </CardTitle>
289
+ </CardHeader>
290
+ <CardContent>
291
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
292
+ <div className="space-y-2">
293
+ <Label htmlFor="businessLicense">Business License</Label>
294
+ <Input
295
+ id="businessLicense"
296
+ value={form.businessLicense}
297
+ onChange={(e) =>
298
+ setForm((prev) => ({ ...prev, businessLicense: e.target.value }))
299
+ }
300
+ />
301
+ </div>
302
+ <div className="space-y-2">
303
+ <Label htmlFor="taxId">Tax ID</Label>
304
+ <Input
305
+ id="taxId"
306
+ value={form.taxId}
307
+ onChange={(e) => setForm((prev) => ({ ...prev, taxId: e.target.value }))}
308
+ />
309
+ </div>
310
+ <div className="space-y-2">
311
+ <Label htmlFor="rating">Rating (0–5)</Label>
312
+ <Input
313
+ id="rating"
314
+ type="number"
315
+ min={0}
316
+ max={5}
317
+ step={0.1}
318
+ value={form.rating}
319
+ onChange={(e) => setForm((prev) => ({ ...prev, rating: e.target.value }))}
320
+ />
321
+ </div>
322
+ </div>
323
+ </CardContent>
324
+ </Card>
197
325
  </form>
198
326
  </PageLayout>
199
327
  );
200
328
  }
201
329
 
330
+ function customerToForm(customer: {
331
+ name: string;
332
+ email: string;
333
+ phone: string;
334
+ company?: string;
335
+ type: 'b2b' | 'b2c';
336
+ address: { street: string; city: string; state: string; postalCode: string; country: string };
337
+ businessLicense?: string;
338
+ taxId?: string;
339
+ rating: number;
340
+ }): CustomerFormState {
341
+ return {
342
+ name: customer.name,
343
+ email: customer.email,
344
+ phone: customer.phone,
345
+ company: customer.company ?? '',
346
+ type: customer.type,
347
+ street: customer.address.street,
348
+ city: customer.address.city,
349
+ state: customer.address.state,
350
+ postalCode: customer.address.postalCode,
351
+ country: customer.address.country,
352
+ businessLicense: customer.businessLicense ?? '',
353
+ taxId: customer.taxId ?? '',
354
+ rating: customer.rating ? String(customer.rating) : '',
355
+ };
356
+ }
357
+
202
358
  export default function CustomerDetailsPage() {
203
359
  const { customerId } = useParams<{ customerId: string }>();
204
360
  const { basePath, navigate } = useMoveTheWheels();
205
361
  const { customer, isLoading, error, refetch } = useCustomer(customerId);
206
- const { updateCustomer } = useCustomers();
362
+ const { updateCustomer, deleteCustomer } = useCustomers();
207
363
  const { orders, isLoading: ordersLoading } = useOrders({ filters: { customerId } });
208
364
  const { message: notice, notify } = useNotice();
209
365
 
210
366
  const [editing, setEditing] = useState(false);
211
367
  const [saving, setSaving] = useState(false);
212
368
  const [editError, setEditError] = useState<string | null>(null);
213
- const [form, setForm] = useState({
214
- name: '',
215
- email: '',
216
- phone: '',
217
- company: '',
218
- });
369
+ const [form, setForm] = useState<CustomerFormState>(emptyCustomerForm);
370
+ const [deleteOpen, setDeleteOpen] = useState(false);
371
+ const [deleting, setDeleting] = useState(false);
372
+ const [deleteError, setDeleteError] = useState<string | null>(null);
219
373
 
220
374
  useEffect(() => {
221
375
  if (customer) {
222
- setForm({
223
- name: customer.name,
224
- email: customer.email,
225
- phone: customer.phone,
226
- company: customer.company ?? '',
227
- });
376
+ setForm(customerToForm(customer));
228
377
  }
229
378
  }, [customer]);
230
379
 
@@ -240,12 +389,7 @@ export default function CustomerDetailsPage() {
240
389
  setEditError(null);
241
390
  setEditing(false);
242
391
  if (customer) {
243
- setForm({
244
- name: customer.name,
245
- email: customer.email,
246
- phone: customer.phone,
247
- company: customer.company ?? '',
248
- });
392
+ setForm(customerToForm(customer));
249
393
  }
250
394
  };
251
395
 
@@ -263,6 +407,17 @@ export default function CustomerDetailsPage() {
263
407
  email: form.email.trim(),
264
408
  phone: form.phone.trim(),
265
409
  company: form.company.trim() || undefined,
410
+ type: form.type,
411
+ address: {
412
+ street: form.street.trim(),
413
+ city: form.city.trim(),
414
+ state: form.state.trim(),
415
+ postalCode: form.postalCode.trim(),
416
+ country: form.country.trim(),
417
+ },
418
+ businessLicense: form.businessLicense.trim() || undefined,
419
+ taxId: form.taxId.trim() || undefined,
420
+ rating: form.rating ? Number(form.rating) : undefined,
266
421
  });
267
422
  notify('Customer updated.');
268
423
  setEditing(false);
@@ -274,6 +429,20 @@ export default function CustomerDetailsPage() {
274
429
  }
275
430
  };
276
431
 
432
+ const handleDelete = async () => {
433
+ if (!customerId) return;
434
+ setDeleting(true);
435
+ setDeleteError(null);
436
+ try {
437
+ await deleteCustomer(customerId);
438
+ setDeleteOpen(false);
439
+ goBack();
440
+ } catch (err) {
441
+ setDeleteError(err instanceof Error ? err.message : 'Failed to delete customer.');
442
+ setDeleting(false);
443
+ }
444
+ };
445
+
277
446
  // Handle new customer form
278
447
  if (!customerId) {
279
448
  return <NewCustomerForm />;
@@ -311,13 +480,12 @@ export default function CustomerDetailsPage() {
311
480
  headerContent={
312
481
  <div className="flex items-center gap-4">
313
482
  <TypeBadge type={customer.type} />
314
- <Badge variant={customer.isActive ? 'default' : 'secondary'}>
315
- {customer.isActive ? 'Active' : 'Inactive'}
316
- </Badge>
317
- <div className="flex items-center gap-1">
318
- <Star size={16} className="text-status-warning-text fill-current" />
319
- <span className="font-medium">{customer.rating.toFixed(1)}</span>
320
- </div>
483
+ {customer.rating > 0 && (
484
+ <div className="flex items-center gap-1">
485
+ <Star size={16} className="text-status-warning-text fill-current" />
486
+ <span className="font-medium">{customer.rating.toFixed(1)}</span>
487
+ </div>
488
+ )}
321
489
  </div>
322
490
  }
323
491
  >
@@ -398,6 +566,95 @@ export default function CustomerDetailsPage() {
398
566
  onChange={(e) => setForm((prev) => ({ ...prev, phone: e.target.value }))}
399
567
  />
400
568
  </div>
569
+ <div className="space-y-2">
570
+ <Label htmlFor="edit-type">Customer Type</Label>
571
+ <Select
572
+ value={form.type}
573
+ onValueChange={(value) =>
574
+ setForm((prev) => ({ ...prev, type: value as 'b2b' | 'b2c' }))
575
+ }
576
+ >
577
+ <SelectTrigger id="edit-type">
578
+ <SelectValue />
579
+ </SelectTrigger>
580
+ <SelectContent>
581
+ <SelectItem value="b2c">B2C (Individual)</SelectItem>
582
+ <SelectItem value="b2b">B2B (Business)</SelectItem>
583
+ </SelectContent>
584
+ </Select>
585
+ </div>
586
+ <div className="space-y-2">
587
+ <Label htmlFor="edit-rating">Rating (0–5)</Label>
588
+ <Input
589
+ id="edit-rating"
590
+ type="number"
591
+ min={0}
592
+ max={5}
593
+ step={0.1}
594
+ value={form.rating}
595
+ onChange={(e) => setForm((prev) => ({ ...prev, rating: e.target.value }))}
596
+ />
597
+ </div>
598
+ </div>
599
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
600
+ <div className="space-y-2">
601
+ <Label htmlFor="edit-street">Street</Label>
602
+ <Input
603
+ id="edit-street"
604
+ value={form.street}
605
+ onChange={(e) => setForm((prev) => ({ ...prev, street: e.target.value }))}
606
+ />
607
+ </div>
608
+ <div className="space-y-2">
609
+ <Label htmlFor="edit-city">City</Label>
610
+ <Input
611
+ id="edit-city"
612
+ value={form.city}
613
+ onChange={(e) => setForm((prev) => ({ ...prev, city: e.target.value }))}
614
+ />
615
+ </div>
616
+ <div className="space-y-2">
617
+ <Label htmlFor="edit-state">State</Label>
618
+ <Input
619
+ id="edit-state"
620
+ value={form.state}
621
+ onChange={(e) => setForm((prev) => ({ ...prev, state: e.target.value }))}
622
+ />
623
+ </div>
624
+ <div className="space-y-2">
625
+ <Label htmlFor="edit-postalCode">Postal Code</Label>
626
+ <Input
627
+ id="edit-postalCode"
628
+ value={form.postalCode}
629
+ onChange={(e) => setForm((prev) => ({ ...prev, postalCode: e.target.value }))}
630
+ />
631
+ </div>
632
+ <div className="space-y-2">
633
+ <Label htmlFor="edit-country">Country</Label>
634
+ <Input
635
+ id="edit-country"
636
+ value={form.country}
637
+ onChange={(e) => setForm((prev) => ({ ...prev, country: e.target.value }))}
638
+ />
639
+ </div>
640
+ <div className="space-y-2">
641
+ <Label htmlFor="edit-businessLicense">Business License</Label>
642
+ <Input
643
+ id="edit-businessLicense"
644
+ value={form.businessLicense}
645
+ onChange={(e) =>
646
+ setForm((prev) => ({ ...prev, businessLicense: e.target.value }))
647
+ }
648
+ />
649
+ </div>
650
+ <div className="space-y-2">
651
+ <Label htmlFor="edit-taxId">Tax ID</Label>
652
+ <Input
653
+ id="edit-taxId"
654
+ value={form.taxId}
655
+ onChange={(e) => setForm((prev) => ({ ...prev, taxId: e.target.value }))}
656
+ />
657
+ </div>
401
658
  </div>
402
659
  </div>
403
660
  ) : (
@@ -428,23 +685,6 @@ export default function CustomerDetailsPage() {
428
685
  </div>
429
686
  </div>
430
687
  )}
431
- {(() => {
432
- const website = safeHttpUrl(customer.website);
433
- if (!website) return null;
434
- return (
435
- <div>
436
- <p className="text-sm text-text-secondary">Website</p>
437
- <a
438
- href={website}
439
- target="_blank"
440
- rel="noopener noreferrer"
441
- className="text-primary hover:underline"
442
- >
443
- {website}
444
- </a>
445
- </div>
446
- );
447
- })()}
448
688
  </div>
449
689
  </div>
450
690
  )}
@@ -457,23 +697,10 @@ export default function CustomerDetailsPage() {
457
697
  </CardTitle>
458
698
  </CardHeader>
459
699
  <CardContent>
460
- <p>{formatAddress(customer.address)}</p>
700
+ <p>{formatAddress(customer.address) || '—'}</p>
461
701
  </CardContent>
462
702
  </Card>
463
703
 
464
- {customer.notes && (
465
- <Card>
466
- <CardHeader>
467
- <CardTitle className="flex items-center gap-2">
468
- <FileText size={18} /> Notes
469
- </CardTitle>
470
- </CardHeader>
471
- <CardContent>
472
- <p>{customer.notes}</p>
473
- </CardContent>
474
- </Card>
475
- )}
476
-
477
704
  {/* Orders for this customer */}
478
705
  <Card>
479
706
  <CardHeader className="flex flex-row items-center justify-between">
@@ -529,35 +756,27 @@ export default function CustomerDetailsPage() {
529
756
  </div>
530
757
 
531
758
  <div className="space-y-6">
532
- <Card>
533
- <CardHeader>
534
- <CardTitle>Business Details</CardTitle>
535
- </CardHeader>
536
- <CardContent className="space-y-3">
537
- {customer.businessLicense && (
538
- <div>
539
- <p className="text-sm text-text-secondary">Business License</p>
540
- <p className="font-mono">{customer.businessLicense}</p>
541
- </div>
542
- )}
543
- {customer.taxId && (
544
- <div>
545
- <p className="text-sm text-text-secondary">Tax ID</p>
546
- <p className="font-mono">{customer.taxId}</p>
547
- </div>
548
- )}
549
- <div>
550
- <p className="text-sm text-text-secondary">Preferred Contact</p>
551
- <p className="capitalize">{customer.preferredContactMethod}</p>
552
- </div>
553
- <div>
554
- <p className="text-sm text-text-secondary">Marketing Opt-in</p>
555
- <Badge variant={customer.marketingOptIn ? 'default' : 'secondary'}>
556
- {customer.marketingOptIn ? 'Yes' : 'No'}
557
- </Badge>
558
- </div>
559
- </CardContent>
560
- </Card>
759
+ {(customer.businessLicense || customer.taxId) && (
760
+ <Card>
761
+ <CardHeader>
762
+ <CardTitle>Business Details</CardTitle>
763
+ </CardHeader>
764
+ <CardContent className="space-y-3">
765
+ {customer.businessLicense && (
766
+ <div>
767
+ <p className="text-sm text-text-secondary">Business License</p>
768
+ <p className="font-mono">{customer.businessLicense}</p>
769
+ </div>
770
+ )}
771
+ {customer.taxId && (
772
+ <div>
773
+ <p className="text-sm text-text-secondary">Tax ID</p>
774
+ <p className="font-mono">{customer.taxId}</p>
775
+ </div>
776
+ )}
777
+ </CardContent>
778
+ </Card>
779
+ )}
561
780
 
562
781
  <Card>
563
782
  <CardHeader>
@@ -574,8 +793,56 @@ export default function CustomerDetailsPage() {
574
793
  </div>
575
794
  </CardContent>
576
795
  </Card>
796
+
797
+ <Card className="border-status-error-border">
798
+ <CardHeader>
799
+ <CardTitle className="flex items-center gap-2 text-status-error-text">
800
+ <Trash2 size={18} /> Delete Customer
801
+ </CardTitle>
802
+ </CardHeader>
803
+ <CardContent>
804
+ <p className="mb-3 text-sm text-text-secondary">
805
+ Removes this customer from your account. Their past orders stay on record. This
806
+ cannot be undone from here.
807
+ </p>
808
+ <Button
809
+ type="button"
810
+ variant="outline"
811
+ className="border-status-error-border text-status-error-text hover:bg-status-error-bg-subtle"
812
+ onClick={() => setDeleteOpen(true)}
813
+ >
814
+ <Trash2 size={16} /> Delete this customer
815
+ </Button>
816
+ </CardContent>
817
+ </Card>
577
818
  </div>
578
819
  </div>
820
+
821
+ <ResponsiveDialog open={deleteOpen} onOpenChange={setDeleteOpen}>
822
+ <ResponsiveDialogContent>
823
+ <ResponsiveDialogHeader>
824
+ <ResponsiveDialogTitle>Delete this customer?</ResponsiveDialogTitle>
825
+ <ResponsiveDialogDescription>
826
+ This removes {customer.name} from your account. It cannot be undone from here.
827
+ </ResponsiveDialogDescription>
828
+ </ResponsiveDialogHeader>
829
+ <div className="px-1">
830
+ <FormError message={deleteError} />
831
+ </div>
832
+ <ResponsiveDialogFooter>
833
+ <Button variant="outline" onClick={() => setDeleteOpen(false)} disabled={deleting}>
834
+ Cancel
835
+ </Button>
836
+ <Button
837
+ className="bg-status-error-text text-text-inverse hover:opacity-90"
838
+ onClick={handleDelete}
839
+ disabled={deleting}
840
+ >
841
+ {deleting ? 'Deleting...' : 'Delete customer'}
842
+ </Button>
843
+ </ResponsiveDialogFooter>
844
+ </ResponsiveDialogContent>
845
+ </ResponsiveDialog>
579
846
  </PageLayout>
580
847
  );
581
848
  }
@@ -3,12 +3,12 @@
3
3
  */
4
4
 
5
5
  import { useState } from 'react';
6
- import { Users, Plus, RefreshCw, Download, Star, UserCheck, Building2 } from 'lucide-react';
6
+ import { Users, Plus, RefreshCw, Download, Star, Building2 } 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
10
  import { TypeBadge } from '../components/StatusBadge';
11
- import { Badge, Avatar, AvatarFallback, Button } from '../components/ui';
11
+ import { Avatar, AvatarFallback, Button } from '../components/ui';
12
12
  import { CTAOverflowMenu, PagePurpose } from '@burdenoff/fe-libs/ui';
13
13
  import { useCustomers, useCustomerStats } from '../hooks/useCustomers';
14
14
  import { useDebouncedValue } from '../hooks/useDebouncedValue';
@@ -77,15 +77,6 @@ export default function CustomersPage() {
77
77
  </div>
78
78
  ),
79
79
  },
80
- {
81
- id: 'status',
82
- header: 'Status',
83
- cell: (row) => (
84
- <Badge variant={row.isActive ? 'default' : 'secondary'}>
85
- {row.isActive ? 'Active' : 'Inactive'}
86
- </Badge>
87
- ),
88
- },
89
80
  {
90
81
  id: 'created',
91
82
  header: 'Created',
@@ -135,11 +126,11 @@ export default function CustomersPage() {
135
126
  accent="brand"
136
127
  />
137
128
  <MetricCard
138
- label="Active"
139
- value={stats.active}
140
- icon={<UserCheck size={20} />}
129
+ label="Avg. rating"
130
+ value={stats.avgRating > 0 ? stats.avgRating.toFixed(1) : '—'}
131
+ icon={<Star size={20} />}
141
132
  accent="success"
142
- subtitle="Currently transacting"
133
+ subtitle="Across all customers"
143
134
  />
144
135
  <MetricCard
145
136
  label="Business accounts"
@@ -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
- Expires: {formatDate(driver.licenseExpiry)}
712
+ {driver.licenseExpiry
713
+ ? `Expires: ${formatDate(driver.licenseExpiry)}`
714
+ : 'Expiry not recorded'}
713
715
  </p>
714
716
  </div>
715
717
  </div>