@burdenoff/microfe-movethewheels 2026.626.2 → 2026.703.2

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 (135) hide show
  1. package/dist/{AIAssistantPage-AvsMbunM.js → AIAssistantPage-C3cPsWt-.js} +100 -73
  2. package/dist/AnalyticsPage-Dz6g2MvK.js +215 -0
  3. package/dist/{CreateOrderPage-CjlnEtk3.js → CreateOrderPage-C5iwnjp4.js} +36 -67
  4. package/dist/CustomerDetailsPage-B6nwFyLO.js +406 -0
  5. package/dist/{CustomersPage-CGGfD4ki.js → CustomersPage-2Y05LVyh.js} +26 -28
  6. package/dist/{DashboardPage-D2HRqwg9.js → DashboardPage-BVjQi0EB.js} +85 -83
  7. package/dist/{DataTable-CD9ZhpLB.js → DataTable-DmciIKKM.js} +3 -3
  8. package/dist/DriverDetailsPage-CN3WQ0B9.js +507 -0
  9. package/dist/{DriversPage-DKiynISH.js → DriversPage-B0Zpx8OQ.js} +25 -27
  10. package/dist/{FinancePage-Vg8WPs38.js → FinancePage-DEDykSOP.js} +9 -9
  11. package/dist/FleetPage-DqkFi3s3.js +365 -0
  12. package/dist/FormError-BlOjnXOq.js +15 -0
  13. package/dist/ImportExportPage-DKJRCezl.js +290 -0
  14. package/dist/InventoryPage-DwfgCUQq.js +253 -0
  15. package/dist/{LiveTrackingPage-DQ1q4Id3.js → LiveTrackingPage-DCX-K-LR.js} +60 -56
  16. package/dist/MarketplacePage-CIQ97_zp.js +247 -0
  17. package/dist/{MetricCard-B7yMyvMX.js → MetricCard-BY0QrMD4.js} +1 -1
  18. package/dist/{OrderDetailsPage-CFASgsYX.js → OrderDetailsPage-DYiZ-9UK.js} +83 -85
  19. package/dist/{OrdersListPage-BHeT8S5o.js → OrdersListPage-BjDlCKJd.js} +31 -33
  20. package/dist/{PageLayout-D3N8MCTm.js → PageLayout-D4LDHJJq.js} +483 -422
  21. package/dist/ProductDetailsPage-C9uOiYLp.js +350 -0
  22. package/dist/{ProductsPage--xEpbQ8A.js → ProductsPage-C1G9h7Xd.js} +22 -24
  23. package/dist/ReportsPage-Zp53q3_8.js +284 -0
  24. package/dist/RouteDetailsPage-DUStXSf9.js +531 -0
  25. package/dist/RoutesPage-DMfoicM1.js +144 -0
  26. package/dist/SettingsPage-Bn-v0mbQ.js +421 -0
  27. package/dist/StatusBadge-BZurYHMy.js +152 -0
  28. package/dist/{TrackingPage-CcRc_dva.js → TrackingPage-W-jtdM3Y.js} +59 -61
  29. package/dist/VehicleDetailsPage-Bceo5oz3.js +410 -0
  30. package/dist/{VehiclesPage-UlxPWP9K.js → VehiclesPage-VBtOep91.js} +20 -22
  31. package/dist/WarehouseDetailsPage-L_6sWigM.js +493 -0
  32. package/dist/{WarehousesPage-CuTO-OPp.js → WarehousesPage-DCbXXXWE.js} +16 -18
  33. package/dist/{arrow-left-CBpZKkpq.js → arrow-left-n48Y-Wis.js} +1 -1
  34. package/dist/{box-CAaiElsb.js → box-D9Pal4HM.js} +1 -1
  35. package/dist/{chart-column-D1Ym15-S.js → chart-column-BFIJysDR.js} +1 -1
  36. package/dist/{chevron-right-BIFGi0UA.js → chevron-right-EMIIyyJ1.js} +1 -1
  37. package/dist/{circle-alert-DjmNo5_L.js → circle-alert-iY24lSlp.js} +1 -1
  38. package/dist/{circle-check-big-CjuPVo3_.js → circle-check-big-w0TrLKMF.js} +1 -1
  39. package/dist/{clock-BjkdnddD.js → clock-DJZtdl8u.js} +1 -1
  40. package/dist/{dollar-sign-IB4O0wuQ.js → dollar-sign-CgKQRz_e.js} +1 -1
  41. package/dist/{download-Dpa9vqoG.js → download-DRXxoiQR.js} +1 -1
  42. package/dist/{file-text-C8Uioy11.js → file-text-DWWY6_f5.js} +1 -1
  43. package/dist/{filter-Da5OZaHI.js → filter-DloLC2eE.js} +1 -1
  44. package/dist/index.js +186 -162
  45. package/dist/{map-_Per4sT9.js → map-CkJaULb2.js} +1 -1
  46. package/dist/{map-pin-BSaf5IDL.js → map-pin-BGiCvyCs.js} +1 -1
  47. package/dist/{navigation-DaTbrhw2.js → navigation-D36prnAn.js} +1 -1
  48. package/dist/{package-BE7U0wQE.js → package-BChUzpJS.js} +1 -1
  49. package/dist/{phone-DV4StZiA.js → phone-Ac9s3mo_.js} +1 -1
  50. package/dist/{plus-Dl1rLeJR.js → plus-Z76q-FZm.js} +1 -1
  51. package/dist/{refresh-cw-CWfERAit.js → refresh-cw-DKt6f5T6.js} +1 -1
  52. package/dist/{save-XH8vydOn.js → save-CojywNhG.js} +1 -1
  53. package/dist/{search-OxoIiL_C.js → search-CDGefqb3.js} +1 -1
  54. package/dist/{square-pen-hLIbFQFV.js → square-pen-CopE9GmB.js} +1 -1
  55. package/dist/{star-YsVV3MNQ.js → star-nHlLjDKR.js} +1 -1
  56. package/dist/{store-RCBl7HB-.js → store-BR9Voi5E.js} +1 -1
  57. package/dist/trash-2-BKhar6Hs.js +32 -0
  58. package/dist/{trending-up-C-85AwGT.js → trending-up-Dohgd4t_.js} +1 -1
  59. package/dist/{triangle-alert-BOhfGgmh.js → triangle-alert-i-XQL9Rd.js} +1 -1
  60. package/dist/{truck-CtDjNFbi.js → truck-C0v_hbLR.js} +1 -1
  61. package/dist/{useAnalytics-ph7eTIK6.js → useAnalytics-DS8n0xgg.js} +40 -40
  62. package/dist/useAssistant-D4v3oxD3.js +84 -0
  63. package/dist/{useCustomers-bS3a4ytk.js → useCustomers-BmTNGxiI.js} +24 -24
  64. package/dist/{useFleet-BdETplNE.js → useFleet-DKqXZ16m.js} +133 -109
  65. package/dist/useImportExport-DHr-SWSw.js +55 -0
  66. package/dist/{useInventory-Dwn18FPz.js → useInventory-DkyiVY_A.js} +95 -75
  67. package/dist/{useOrders-D_3_hGMp.js → useOrders-Cfk1GLiq.js} +42 -42
  68. package/dist/useReports-CTqZr-AB.js +78 -0
  69. package/dist/{useRoutes-v4aBaS-E.js → useRoutes-BQWO607N.js} +31 -31
  70. package/dist/useSettings-CkDH_-gM.js +81 -0
  71. package/dist/{useTracking-De2KIUNu.js → useTracking-B-va1kO3.js} +22 -22
  72. package/dist/{user-Dz5TJZFs.js → user-jyFU3lXk.js} +1 -1
  73. package/dist/{users-g2N0eL-Z.js → users-DZ-ilS6B.js} +1 -1
  74. package/dist/{warehouse-D3WWDeTX.js → warehouse-6m5kXMLu.js} +1 -1
  75. package/dist/{wrench-CmxY1n4I.js → wrench-CANXWxF-.js} +1 -1
  76. package/package.json +1 -1
  77. package/src/movethewheels/MoveTheWheelsRoutes.tsx +31 -3
  78. package/src/movethewheels/components/FormError.tsx +25 -0
  79. package/src/movethewheels/components/StatusBadge.tsx +18 -37
  80. package/src/movethewheels/constants/index.ts +67 -26
  81. package/src/movethewheels/hooks/index.ts +12 -0
  82. package/src/movethewheels/hooks/useAnalytics.ts +20 -6
  83. package/src/movethewheels/hooks/useAssistant.ts +74 -0
  84. package/src/movethewheels/hooks/useFleet.ts +170 -59
  85. package/src/movethewheels/hooks/useImportExport.ts +101 -0
  86. package/src/movethewheels/hooks/useInventory.ts +107 -0
  87. package/src/movethewheels/hooks/useReports.ts +158 -0
  88. package/src/movethewheels/hooks/useSettings.ts +120 -0
  89. package/src/movethewheels/pages/AIAssistantPage.tsx +53 -15
  90. package/src/movethewheels/pages/AnalyticsPage.tsx +67 -64
  91. package/src/movethewheels/pages/CustomerDetailsPage.tsx +154 -21
  92. package/src/movethewheels/pages/CustomersPage.tsx +1 -1
  93. package/src/movethewheels/pages/DashboardPage.tsx +42 -22
  94. package/src/movethewheels/pages/DriverDetailsPage.tsx +160 -3
  95. package/src/movethewheels/pages/DriversPage.tsx +6 -2
  96. package/src/movethewheels/pages/FleetPage.tsx +96 -38
  97. package/src/movethewheels/pages/ImportExportPage.tsx +174 -68
  98. package/src/movethewheels/pages/InventoryPage.tsx +66 -28
  99. package/src/movethewheels/pages/LiveTrackingPage.tsx +12 -3
  100. package/src/movethewheels/pages/MarketplacePage.tsx +87 -45
  101. package/src/movethewheels/pages/OrderDetailsPage.tsx +2 -2
  102. package/src/movethewheels/pages/ProductDetailsPage.tsx +146 -3
  103. package/src/movethewheels/pages/ProductsPage.tsx +5 -1
  104. package/src/movethewheels/pages/ReportsPage.tsx +194 -90
  105. package/src/movethewheels/pages/RouteDetailsPage.tsx +290 -36
  106. package/src/movethewheels/pages/RoutesPage.tsx +4 -9
  107. package/src/movethewheels/pages/SettingsPage.tsx +234 -56
  108. package/src/movethewheels/pages/TrackingPage.tsx +6 -6
  109. package/src/movethewheels/pages/VehicleDetailsPage.tsx +158 -2
  110. package/src/movethewheels/pages/VehiclesPage.tsx +5 -1
  111. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +224 -2
  112. package/src/movethewheels/pages/WarehousesPage.tsx +5 -1
  113. package/src/movethewheels/providers/MoveTheWheelsProvider.tsx +7 -6
  114. package/src/movethewheels/store/movethewheelsStore.ts +1 -1
  115. package/dist/AnalyticsPage-CCLfHgQZ.js +0 -212
  116. package/dist/CustomerDetailsPage-I6cdDHUi.js +0 -265
  117. package/dist/DriverDetailsPage-rg_pGNuP.js +0 -330
  118. package/dist/FleetPage-DddXt8r8.js +0 -332
  119. package/dist/ImportExportPage-BW6qO3kc.js +0 -248
  120. package/dist/InventoryPage-rMGuaJN-.js +0 -222
  121. package/dist/MarketplacePage-DKrXAv62.js +0 -219
  122. package/dist/ProductDetailsPage-CFGcbaHo.js +0 -195
  123. package/dist/ReportsPage-Bb7_90rK.js +0 -235
  124. package/dist/RouteDetailsPage-CVtDVb1N.js +0 -282
  125. package/dist/RoutesPage-s57PtgcQ.js +0 -163
  126. package/dist/SettingsPage-BYNCBOpN.js +0 -298
  127. package/dist/StatusBadge-CijYQXtb.js +0 -209
  128. package/dist/VehicleDetailsPage-3YDMPsPs.js +0 -220
  129. package/dist/WarehouseDetailsPage-Db7KeN-d.js +0 -238
  130. package/dist/graphqlClient-CdJyR_ed.js +0 -55
  131. package/dist/navigation-BgnOfsVd.js +0 -6
  132. package/dist/route-Dp9JiN_E.js +0 -51
  133. package/dist/settings-3Rw5NwFF.js +0 -28
  134. package/src/movethewheels/constants/mockData.ts +0 -1342
  135. /package/dist/{formatters-_vJlC-47.js → formatters-CF2S2l1m.js} +0 -0
@@ -4,14 +4,14 @@
4
4
 
5
5
  import { BarChart3, TrendingUp, Package, Truck, DollarSign, Users } from 'lucide-react';
6
6
  import { PageLayout } from '../components/PageLayout';
7
- import { MetricCard, MetricGrid } from '../components/MetricCard';
8
- import { Card, CardContent, CardHeader, CardTitle, Progress } from '../components/ui';
7
+ import { MetricCard, MetricGrid, MetricCardSkeleton } from '../components/MetricCard';
8
+ import { Card, CardContent, CardHeader, CardTitle, Progress, Button } from '../components/ui';
9
9
  import { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
10
10
  import { useAnalytics, useOrderAnalytics, useFleetAnalytics } from '../hooks/useAnalytics';
11
11
  import { formatCurrency } from '../utils/formatters';
12
12
 
13
13
  export default function AnalyticsPage() {
14
- const { analytics } = useAnalytics();
14
+ const { analytics, isLoading, error, refetch } = useAnalytics();
15
15
  const { orderData } = useOrderAnalytics();
16
16
  const { fleetData } = useFleetAnalytics();
17
17
 
@@ -27,43 +27,55 @@ export default function AnalyticsPage() {
27
27
  to invest — whether that's more vehicles, better routes, or new customers.
28
28
  </PagePurpose>
29
29
 
30
- <MetricGrid columns={5}>
31
- <MetricCard
32
- label="Total Orders"
33
- value={analytics.overview.totalOrders.toLocaleString()}
34
- icon={<Package size={20} />}
35
- change="+8.2%"
36
- changeType="positive"
37
- trend="up"
38
- />
39
- <MetricCard
40
- treatment="glass"
41
- glow
42
- label="Revenue"
43
- value={formatCurrency(analytics.overview.totalRevenue)}
44
- icon={<DollarSign size={20} />}
45
- change="+12.5%"
46
- changeType="positive"
47
- trend="up"
48
- />
49
- <MetricCard
50
- label="Active Vehicles"
51
- value={analytics.overview.activeVehicles}
52
- icon={<Truck size={20} />}
53
- />
54
- <MetricCard
55
- label="On-Time Rate"
56
- value={`${analytics.overview.onTimeDeliveryRate}%`}
57
- icon={<TrendingUp size={20} />}
58
- changeType={analytics.overview.onTimeDeliveryRate >= 90 ? 'positive' : 'negative'}
59
- />
60
- <MetricCard
61
- label="Satisfaction"
62
- value={analytics.overview.customerSatisfaction.toFixed(1)}
63
- icon={<Users size={20} />}
64
- subtitle="out of 5"
65
- />
66
- </MetricGrid>
30
+ {error && !isLoading ? (
31
+ <Card className="border-status-error-border">
32
+ <CardContent className="p-6 text-center space-y-3">
33
+ <p className="text-status-error-text font-medium">Couldn&apos;t load analytics</p>
34
+ <p className="text-sm text-text-secondary">{error.message}</p>
35
+ <Button variant="outline" onClick={refetch}>
36
+ Retry
37
+ </Button>
38
+ </CardContent>
39
+ </Card>
40
+ ) : isLoading ? (
41
+ <MetricGrid columns={5}>
42
+ {Array.from({ length: 5 }).map((_, i) => (
43
+ <MetricCardSkeleton key={i} />
44
+ ))}
45
+ </MetricGrid>
46
+ ) : (
47
+ <MetricGrid columns={5}>
48
+ <MetricCard
49
+ label="Total Orders"
50
+ value={analytics.overview.totalOrders.toLocaleString()}
51
+ icon={<Package size={20} />}
52
+ />
53
+ <MetricCard
54
+ treatment="glass"
55
+ glow
56
+ label="Revenue"
57
+ value={formatCurrency(analytics.overview.totalRevenue)}
58
+ icon={<DollarSign size={20} />}
59
+ />
60
+ <MetricCard
61
+ label="Active Vehicles"
62
+ value={analytics.overview.activeVehicles}
63
+ icon={<Truck size={20} />}
64
+ />
65
+ <MetricCard
66
+ label="On-Time Rate"
67
+ value={`${analytics.overview.onTimeDeliveryRate.toFixed(1)}%`}
68
+ icon={<TrendingUp size={20} />}
69
+ changeType={analytics.overview.onTimeDeliveryRate >= 90 ? 'positive' : 'neutral'}
70
+ />
71
+ <MetricCard
72
+ label="Satisfaction"
73
+ value={analytics.overview.customerSatisfaction.toFixed(1)}
74
+ icon={<Users size={20} />}
75
+ subtitle="out of 5"
76
+ />
77
+ </MetricGrid>
78
+ )}
67
79
 
68
80
  <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
69
81
  <Card>
@@ -149,45 +161,36 @@ export default function AnalyticsPage() {
149
161
  </div>
150
162
  <Progress value={fleetData.vehicleUtilization} />
151
163
  </div>
152
- <div>
153
- <div className="flex justify-between text-sm mb-1">
154
- <span>Avg Fuel Level</span>
155
- <span>{fleetData.avgFuelLevel.toFixed(0)}%</span>
164
+ {fleetData.vehiclesByType.length > 0 && (
165
+ <div className="pt-2">
166
+ <p className="text-sm text-text-secondary mb-2">Vehicles by Type</p>
167
+ {fleetData.vehiclesByType.map(({ type, count }) => (
168
+ <div key={type} className="flex justify-between text-sm py-1">
169
+ <span className="capitalize">{type}</span>
170
+ <span className="font-medium">{count}</span>
171
+ </div>
172
+ ))}
156
173
  </div>
157
- <Progress value={fleetData.avgFuelLevel} />
158
- </div>
159
- <div className="pt-2">
160
- <p className="text-sm text-text-secondary mb-2">Vehicles by Type</p>
161
- {fleetData.vehiclesByType.map(({ type, count }) => (
162
- <div key={type} className="flex justify-between text-sm py-1">
163
- <span className="capitalize">{type}</span>
164
- <span className="font-medium">{count}</span>
165
- </div>
166
- ))}
167
- </div>
174
+ )}
168
175
  </CardContent>
169
176
  </Card>
170
177
 
171
178
  {/* Performance Metrics — the page's single emphasis zone */}
172
179
  <EmphasisPanel className="space-y-4">
173
180
  <h2 className="text-base font-semibold text-text-primary">Performance Metrics</h2>
174
- <div>
175
- <p className="text-sm text-text-secondary">On-Time Pickup</p>
176
- <p className="text-2xl font-bold">{analytics.performance.onTimePickup}%</p>
177
- </div>
178
181
  <div>
179
182
  <p className="text-sm text-text-secondary">On-Time Delivery</p>
180
- <p className="text-2xl font-bold">{analytics.performance.onTimeDelivery}%</p>
183
+ <p className="text-2xl font-bold">{analytics.performance.onTimeDelivery.toFixed(1)}%</p>
181
184
  </div>
182
185
  <div>
183
- <p className="text-sm text-text-secondary">Damaged Items</p>
184
- <p className="text-2xl font-bold text-status-success-text">
185
- {analytics.performance.damagedItems}%
186
+ <p className="text-sm text-text-secondary">Customer Satisfaction</p>
187
+ <p className="text-2xl font-bold">
188
+ {analytics.overview.customerSatisfaction.toFixed(1)}/5
186
189
  </p>
187
190
  </div>
188
191
  <div>
189
192
  <p className="text-sm text-text-secondary">Driver Ratings</p>
190
- <p className="text-2xl font-bold">{analytics.performance.driverRatings}/5</p>
193
+ <p className="text-2xl font-bold">{analytics.performance.driverRatings.toFixed(1)}/5</p>
191
194
  </div>
192
195
  </EmphasisPanel>
193
196
  </div>
@@ -2,6 +2,7 @@
2
2
  * Customer Details Page
3
3
  */
4
4
 
5
+ import { useState } from 'react';
5
6
  import { useParams } from 'react-router-dom';
6
7
  import {
7
8
  Users,
@@ -13,15 +14,164 @@ import {
13
14
  Star,
14
15
  Building,
15
16
  FileText,
17
+ Save,
16
18
  } from 'lucide-react';
17
19
  import { PageLayout, LoadingState, ErrorState } from '../components/PageLayout';
18
20
  import { TypeBadge } from '../components/StatusBadge';
19
- import { Card, CardContent, CardHeader, CardTitle, Button, Badge } from '../components/ui';
21
+ import {
22
+ Card,
23
+ CardContent,
24
+ CardHeader,
25
+ CardTitle,
26
+ Button,
27
+ Badge,
28
+ Input,
29
+ Label,
30
+ Select,
31
+ SelectContent,
32
+ SelectItem,
33
+ SelectTrigger,
34
+ SelectValue,
35
+ } from '../components/ui';
20
36
  import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
21
- import { useCustomer } from '../hooks/useCustomers';
37
+ import { useCustomer, useCustomers } from '../hooks/useCustomers';
22
38
  import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
23
39
  import { formatAddress, formatDate } from '../utils/formatters';
24
40
  import { joinPath } from '../utils/navigation';
41
+ import { FormError } from '../components/FormError';
42
+
43
+ function NewCustomerForm() {
44
+ const { basePath, navigate } = useMoveTheWheels();
45
+ const { createCustomer } = useCustomers();
46
+ const [form, setForm] = useState({
47
+ name: '',
48
+ type: 'b2c' as 'b2b' | 'b2c',
49
+ email: '',
50
+ phone: '',
51
+ company: '',
52
+ });
53
+ const [submitting, setSubmitting] = useState(false);
54
+ const [error, setError] = useState<string | null>(null);
55
+
56
+ const goBack = () => navigate?.(joinPath(basePath, 'customers'));
57
+
58
+ const handleSubmit = async (e: React.FormEvent) => {
59
+ e.preventDefault();
60
+ if (!form.name.trim()) {
61
+ setError('Customer name is required.');
62
+ return;
63
+ }
64
+ setSubmitting(true);
65
+ setError(null);
66
+ try {
67
+ const created = await createCustomer({
68
+ name: form.name.trim(),
69
+ type: form.type,
70
+ email: form.email.trim() || undefined,
71
+ phone: form.phone.trim() || undefined,
72
+ company: form.company.trim() || undefined,
73
+ });
74
+ navigate?.(joinPath(basePath, `customers/${created.id}`));
75
+ } catch (err) {
76
+ console.error('Failed to create customer:', err);
77
+ setError(err instanceof Error ? err.message : 'Failed to create customer. Please try again.');
78
+ setSubmitting(false);
79
+ }
80
+ };
81
+
82
+ return (
83
+ <PageLayout
84
+ title="New Customer"
85
+ subtitle="Add a new customer to your account"
86
+ icon={<Users size={24} />}
87
+ headerActions={
88
+ <div className="flex gap-2">
89
+ <Button variant="outline" onClick={goBack} disabled={submitting}>
90
+ <ArrowLeft size={16} /> Cancel
91
+ </Button>
92
+ <Button onClick={handleSubmit} disabled={submitting}>
93
+ <Save size={16} /> {submitting ? 'Saving...' : 'Create Customer'}
94
+ </Button>
95
+ </div>
96
+ }
97
+ >
98
+ <PagePurpose icon={<Users size={18} />}>
99
+ Add a customer once and their contact details auto-fill every future order you book for them.
100
+ Only a name is required — add email, phone and company to make deliveries easier to arrange.
101
+ </PagePurpose>
102
+
103
+ <form onSubmit={handleSubmit} className="max-w-2xl space-y-6">
104
+ <FormError message={error} />
105
+ <EmphasisPanel>
106
+ <h2 className="text-base font-semibold text-text-primary mb-4">Customer Details</h2>
107
+ <div className="space-y-4">
108
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
109
+ <div className="space-y-2">
110
+ <Label htmlFor="name">
111
+ Name <span className="text-status-error-text">*</span>
112
+ </Label>
113
+ <Input
114
+ id="name"
115
+ value={form.name}
116
+ onChange={(e) => setForm((prev) => ({ ...prev, name: e.target.value }))}
117
+ placeholder="Acme Corp or Jane Doe"
118
+ required
119
+ />
120
+ </div>
121
+ <div className="space-y-2">
122
+ <Label htmlFor="type">Customer Type</Label>
123
+ <Select
124
+ value={form.type}
125
+ onValueChange={(value) =>
126
+ setForm((prev) => ({ ...prev, type: value as 'b2b' | 'b2c' }))
127
+ }
128
+ >
129
+ <SelectTrigger id="type">
130
+ <SelectValue />
131
+ </SelectTrigger>
132
+ <SelectContent>
133
+ <SelectItem value="b2c">B2C (Individual)</SelectItem>
134
+ <SelectItem value="b2b">B2B (Business)</SelectItem>
135
+ </SelectContent>
136
+ </Select>
137
+ </div>
138
+ </div>
139
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
140
+ <div className="space-y-2">
141
+ <Label htmlFor="email">Email</Label>
142
+ <Input
143
+ id="email"
144
+ type="email"
145
+ value={form.email}
146
+ onChange={(e) => setForm((prev) => ({ ...prev, email: e.target.value }))}
147
+ placeholder="customer@example.com"
148
+ />
149
+ </div>
150
+ <div className="space-y-2">
151
+ <Label htmlFor="phone">Phone</Label>
152
+ <Input
153
+ id="phone"
154
+ value={form.phone}
155
+ onChange={(e) => setForm((prev) => ({ ...prev, phone: e.target.value }))}
156
+ placeholder="+1-555-0100"
157
+ />
158
+ </div>
159
+ </div>
160
+ <div className="space-y-2">
161
+ <Label htmlFor="company">Company</Label>
162
+ <Input
163
+ id="company"
164
+ value={form.company}
165
+ onChange={(e) => setForm((prev) => ({ ...prev, company: e.target.value }))}
166
+ placeholder="Company name (for B2B accounts)"
167
+ />
168
+ </div>
169
+ </div>
170
+ </EmphasisPanel>
171
+ </form>
172
+ </PageLayout>
173
+ );
174
+ }
25
175
 
26
176
  export default function CustomerDetailsPage() {
27
177
  const { customerId } = useParams<{ customerId: string }>();
@@ -33,24 +183,7 @@ export default function CustomerDetailsPage() {
33
183
 
34
184
  // Handle new customer form
35
185
  if (!customerId) {
36
- return (
37
- <PageLayout
38
- title="New Customer"
39
- subtitle="Add a new customer"
40
- icon={<Users size={24} />}
41
- headerActions={
42
- <Button variant="outline" onClick={goBack}>
43
- <ArrowLeft size={16} /> Back
44
- </Button>
45
- }
46
- >
47
- <Card>
48
- <CardContent className="p-8 text-center">
49
- <p className="text-text-secondary">Customer creation form would go here</p>
50
- </CardContent>
51
- </Card>
52
- </PageLayout>
53
- );
186
+ return <NewCustomerForm />;
54
187
  }
55
188
 
56
189
  if (isLoading) return <LoadingState message="Loading customer details..." />;
@@ -78,7 +211,7 @@ export default function CustomerDetailsPage() {
78
211
  {customer.isActive ? 'Active' : 'Inactive'}
79
212
  </Badge>
80
213
  <div className="flex items-center gap-1">
81
- <Star size={16} className="text-yellow-500 fill-yellow-500" />
214
+ <Star size={16} className="text-status-warning-text fill-current" />
82
215
  <span className="font-medium">{customer.rating.toFixed(1)}</span>
83
216
  </div>
84
217
  </div>
@@ -59,7 +59,7 @@ export default function CustomersPage() {
59
59
  header: 'Rating',
60
60
  cell: (row) => (
61
61
  <div className="flex items-center gap-1">
62
- <Star size={14} className="text-yellow-500 fill-yellow-500" />
62
+ <Star size={14} className="text-status-warning-text fill-current" />
63
63
  <span>{row.rating.toFixed(1)}</span>
64
64
  </div>
65
65
  ),
@@ -56,6 +56,13 @@ export default function DashboardPage() {
56
56
  }
57
57
  };
58
58
 
59
+ const onRowKeyDown = (e: React.KeyboardEvent, action: () => void) => {
60
+ if (e.key === 'Enter' || e.key === ' ') {
61
+ e.preventDefault();
62
+ action();
63
+ }
64
+ };
65
+
59
66
  return (
60
67
  <PageLayout
61
68
  hero
@@ -93,9 +100,6 @@ export default function DashboardPage() {
93
100
  label={t('movethewheels.dashboard.metrics.totalOrders', 'Total Orders')}
94
101
  value={metrics.orders.total}
95
102
  icon={<Package size={20} />}
96
- change={`+${metrics.orders.today} ${t('movethewheels.dashboard.metrics.today', 'today')}`}
97
- changeType="positive"
98
- trend="up"
99
103
  onClick={() => goTo('orders')}
100
104
  />
101
105
  <MetricCard
@@ -118,9 +122,6 @@ export default function DashboardPage() {
118
122
  label={t('movethewheels.dashboard.metrics.revenue', 'Revenue')}
119
123
  value={formatCurrency(metrics.revenue.total)}
120
124
  icon={<DollarSign size={20} />}
121
- change="+12.5%"
122
- changeType="positive"
123
- trend="up"
124
125
  onClick={() => goTo('analytics')}
125
126
  />
126
127
  </MetricGrid>
@@ -167,8 +168,12 @@ export default function DashboardPage() {
167
168
  {orders.slice(0, 5).map((order) => (
168
169
  <div
169
170
  key={order.id}
170
- className="p-4 hover:bg-bg-sunken/50 cursor-pointer transition-colors"
171
+ role="button"
172
+ tabIndex={0}
173
+ aria-label={`View order ${order.orderNumber}`}
174
+ className="p-4 hover:bg-bg-sunken/50 cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg"
171
175
  onClick={() => goTo(`orders/${order.id}`)}
176
+ onKeyDown={(e) => onRowKeyDown(e, () => goTo(`orders/${order.id}`))}
172
177
  >
173
178
  <div className="flex items-center justify-between">
174
179
  <div>
@@ -211,8 +216,15 @@ export default function DashboardPage() {
211
216
  delivery.order ? (
212
217
  <div
213
218
  key={delivery.order.id}
214
- className="p-4 hover:bg-bg-sunken/50 cursor-pointer transition-colors"
219
+ role="button"
220
+ tabIndex={0}
221
+ aria-label={`Track order ${delivery.order.orderNumber}`}
222
+ className="p-4 hover:bg-bg-sunken/50 cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg"
215
223
  onClick={() => goTo(`tracking?order=${delivery.order.id}`)}
224
+ onKeyDown={(e) =>
225
+ delivery.order &&
226
+ onRowKeyDown(e, () => goTo(`tracking?order=${delivery.order.id}`))
227
+ }
216
228
  >
217
229
  <div className="flex items-center justify-between">
218
230
  <div>
@@ -257,18 +269,26 @@ export default function DashboardPage() {
257
269
  </h2>
258
270
  <div className="space-y-3">
259
271
  {[
260
- { status: 'pending', count: orderStats.byStatus.pending, color: 'bg-yellow-500' },
272
+ {
273
+ status: 'pending',
274
+ count: orderStats.byStatus.pending,
275
+ color: 'bg-status-warning-text',
276
+ },
261
277
  {
262
278
  status: 'in_transit',
263
279
  count: orderStats.byStatus.in_transit,
264
- color: 'bg-blue-500',
280
+ color: 'bg-status-info-text',
265
281
  },
266
282
  {
267
283
  status: 'delivered',
268
284
  count: orderStats.byStatus.delivered,
269
- color: 'bg-green-500',
285
+ color: 'bg-status-success-text',
286
+ },
287
+ {
288
+ status: 'cancelled',
289
+ count: orderStats.byStatus.cancelled,
290
+ color: 'bg-status-error-text',
270
291
  },
271
- { status: 'cancelled', count: orderStats.byStatus.cancelled, color: 'bg-red-500' },
272
292
  ].map(({ status, count, color }) => (
273
293
  <div key={status} className="flex items-center justify-between">
274
294
  <div className="flex items-center gap-2">
@@ -313,7 +333,7 @@ export default function DashboardPage() {
313
333
  <p className="text-text-secondary">
314
334
  {t('movethewheels.dashboard.fleetStatus.available', 'Available')}
315
335
  </p>
316
- <p className="text-lg font-semibold text-green-600">
336
+ <p className="text-lg font-semibold text-status-success-text">
317
337
  {fleetStats.vehiclesByStatus.available}
318
338
  </p>
319
339
  </div>
@@ -321,7 +341,7 @@ export default function DashboardPage() {
321
341
  <p className="text-text-secondary">
322
342
  {t('movethewheels.dashboard.fleetStatus.inUse', 'In Use')}
323
343
  </p>
324
- <p className="text-lg font-semibold text-blue-600">
344
+ <p className="text-lg font-semibold text-status-info-text">
325
345
  {fleetStats.vehiclesByStatus.assigned}
326
346
  </p>
327
347
  </div>
@@ -329,7 +349,7 @@ export default function DashboardPage() {
329
349
  <p className="text-text-secondary">
330
350
  {t('movethewheels.dashboard.fleetStatus.maintenance', 'Maintenance')}
331
351
  </p>
332
- <p className="text-lg font-semibold text-yellow-600">
352
+ <p className="text-lg font-semibold text-status-warning-text">
333
353
  {fleetStats.vehiclesByStatus.maintenance}
334
354
  </p>
335
355
  </div>
@@ -347,15 +367,15 @@ export default function DashboardPage() {
347
367
  <Card>
348
368
  <CardHeader>
349
369
  <CardTitle className="text-base flex items-center gap-2">
350
- <AlertTriangle size={16} className="text-yellow-500" />
370
+ <AlertTriangle size={16} className="text-status-warning-text" />
351
371
  {t('movethewheels.dashboard.alerts.title', 'Alerts')}
352
372
  </CardTitle>
353
373
  </CardHeader>
354
374
  <CardContent>
355
375
  <div className="space-y-3">
356
376
  {orderStats.byStatus.pending > 5 && (
357
- <div className="flex items-start gap-3 p-2 rounded bg-yellow-50 dark:bg-yellow-900/20">
358
- <Clock size={16} className="text-yellow-600 mt-0.5" />
377
+ <div className="flex items-start gap-3 p-2 rounded bg-status-warning-bg-subtle">
378
+ <Clock size={16} className="text-status-warning-text mt-0.5" />
359
379
  <div>
360
380
  <p className="text-sm font-medium">
361
381
  {t('movethewheels.dashboard.alerts.pendingOrders.title', 'Pending Orders')}
@@ -371,8 +391,8 @@ export default function DashboardPage() {
371
391
  </div>
372
392
  )}
373
393
  {fleetStats.vehiclesByStatus.maintenance > 0 && (
374
- <div className="flex items-start gap-3 p-2 rounded bg-orange-50 dark:bg-orange-900/20">
375
- <Truck size={16} className="text-orange-600 mt-0.5" />
394
+ <div className="flex items-start gap-3 p-2 rounded bg-status-warning-bg-subtle">
395
+ <Truck size={16} className="text-status-warning-text mt-0.5" />
376
396
  <div>
377
397
  <p className="text-sm font-medium">
378
398
  {t(
@@ -391,8 +411,8 @@ export default function DashboardPage() {
391
411
  </div>
392
412
  )}
393
413
  {fleetStats.driversByStatus.offline > 0 && (
394
- <div className="flex items-start gap-3 p-2 rounded bg-gray-50 dark:bg-gray-800">
395
- <Users size={16} className="text-gray-600 mt-0.5" />
414
+ <div className="flex items-start gap-3 p-2 rounded bg-bg-sunken">
415
+ <Users size={16} className="text-text-secondary mt-0.5" />
396
416
  <div>
397
417
  <p className="text-sm font-medium">
398
418
  {t('movethewheels.dashboard.alerts.offlineDrivers.title', 'Offline Drivers')}