@burdenoff/microfe-movethewheels 2026.703.4 → 2026.703.6

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 (150) hide show
  1. package/dist/{AIAssistantPage-3YOnHO9Y.js → AIAssistantPage-BmILdHgO.js} +16 -15
  2. package/dist/AnalyticsPage-BSlsiSDE.js +194 -0
  3. package/dist/{CreateOrderPage-DRAgGo9i.js → CreateOrderPage-DaCgVaO3.js} +40 -40
  4. package/dist/{CustomerDetailsPage-BCeMmOFe.js → CustomerDetailsPage-DErpbafW.js} +71 -71
  5. package/dist/CustomersPage-C1lRU6xs.js +205 -0
  6. package/dist/DashboardPage-Q5-5EKAU.js +542 -0
  7. package/dist/{DataTable-XvUcTjwi.js → DataTable-KjzbwnRL.js} +31 -31
  8. package/dist/{DocumentsCard-U2lSe-cn.js → DocumentsCard-T9y6cv4-.js} +25 -25
  9. package/dist/{DriverDetailsPage-BQrYd_Lp.js → DriverDetailsPage-DnHGkDC7.js} +64 -64
  10. package/dist/DriversPage-C0ujWVOn.js +221 -0
  11. package/dist/FinancePage-CA2XEgsQ.js +175 -0
  12. package/dist/{FleetPage-3ZHsB-TN.js → FleetPage-DrvXW7wK.js} +22 -22
  13. package/dist/{FormError-cuLV2jVD.js → FormError-CRsMP7TQ.js} +1 -1
  14. package/dist/{ImportExportPage-C6f7DqhM.js → ImportExportPage-Cac9HjFx.js} +12 -12
  15. package/dist/InventoryPage-Ezf4gqKI.js +259 -0
  16. package/dist/LiveTrackingPage-CzbVwDqK.js +429 -0
  17. package/dist/{MarketplacePage-Bh_2ns4v.js → MarketplacePage-BHDmSQmo.js} +116 -116
  18. package/dist/MetricCard-DQlDNqmK.js +191 -0
  19. package/dist/{Notice-CSoDYKw9.js → Notice-GmA7JDR3.js} +2 -2
  20. package/dist/{OrderDetailsPage-zCj0lW0j.js → OrderDetailsPage-CR0Wc1aN.js} +58 -58
  21. package/dist/OrdersListPage-C8Bu6wXM.js +386 -0
  22. package/dist/{PageLayout-D4LDHJJq.js → PageLayout-DX1Ou6V6.js} +164 -134
  23. package/dist/{ProductDetailsPage-wsxAQkA0.js → ProductDetailsPage-B_ns2MOe.js} +55 -55
  24. package/dist/ProductsPage-c92BsEoA.js +186 -0
  25. package/dist/ReportsPage-CPLXtLT2.js +377 -0
  26. package/dist/{RouteDetailsPage-DyMJE9AS.js → RouteDetailsPage-DOfcRiFv.js} +208 -181
  27. package/dist/{RoutesPage-Dtc669yc.js → RoutesPage-BZGublby.js} +12 -12
  28. package/dist/{SettingsPage-CpH9xhQg.js → SettingsPage-5cweX_xh.js} +15 -15
  29. package/dist/Sparkline-zSXvG6vA.js +16729 -0
  30. package/dist/{StatusBadge-C87mDpDP.js → StatusBadge-oZjownr1.js} +7 -7
  31. package/dist/TrackingPage-Ln9qVflK.js +368 -0
  32. package/dist/{VehicleDetailsPage-DldYNsYV.js → VehicleDetailsPage-CrkzE0dN.js} +53 -53
  33. package/dist/VehiclesPage-CTQWH7af.js +184 -0
  34. package/dist/{WarehouseDetailsPage-CMG7or6G.js → WarehouseDetailsPage-CwDLaG65.js} +64 -64
  35. package/dist/WarehousesPage-KW02DvZg.js +182 -0
  36. package/dist/{arrow-left-n48Y-Wis.js → arrow-left-DJDw_Yqv.js} +1 -1
  37. package/dist/{arrow-right-Q3XQpTPR.js → arrow-right-Cb2fLTPJ.js} +1 -1
  38. package/dist/{arrow-up-xNF1zJUG.js → arrow-up-PxaOIyiT.js} +1 -1
  39. package/dist/{box-Eu05PXDh.js → box-hSJVtvGf.js} +1 -1
  40. package/dist/boxes-DDnxEkT5.js +54 -0
  41. package/dist/{chart-column-ouzgxIv_.js → chart-column-xXgAZvRl.js} +1 -1
  42. package/dist/charts-CfCwQg6o.js +2018 -0
  43. package/dist/{chevron-left-B2_r0Elx.js → chevron-left-dhEGJYdh.js} +1 -1
  44. package/dist/{chevron-right-eV4LjaSi.js → chevron-right-Cud2NDvm.js} +1 -1
  45. package/dist/chunk-BdsCSvBP.js +20 -0
  46. package/dist/{circle-alert-Bzh2DBSF.js → circle-alert-Rx0p3D5w.js} +1 -1
  47. package/dist/circle-check-BCxRFdFo.js +13 -0
  48. package/dist/{circle-check-big-CGMSpQmD.js → circle-check-big-BcIJ8xjx.js} +1 -1
  49. package/dist/{clock-Bl3ptexL.js → clock-B2_YxJgB.js} +1 -1
  50. package/dist/{dollar-sign-CAVU95SO.js → dollar-sign-CQmAICv4.js} +1 -1
  51. package/dist/{download-C5NbvVDc.js → download-CG4Tponq.js} +1 -1
  52. package/dist/{file-text-D-1uvLZG.js → file-text-CzH-zMub.js} +1 -1
  53. package/dist/{filter-DUfUg6BO.js → filter-C0u2Lwfn.js} +1 -1
  54. package/dist/{fuel-B6_lnKDs.js → fuel-C2KELcM-.js} +1 -1
  55. package/dist/index.css +2 -0
  56. package/dist/index.js +214 -225
  57. package/dist/{map-C-M-FQLR.js → map-FuPu5aTw.js} +1 -1
  58. package/dist/{map-pin-Dznl5l40.js → map-pin-czpLv9uy.js} +1 -1
  59. package/dist/mapMarkers-NCV9MRKR.js +28 -0
  60. package/dist/maps-D9m7PKrs.js +5070 -0
  61. package/dist/{navigation-BJSJl-VG.js → navigation-D5_pdCSS.js} +1 -1
  62. package/dist/{package-CcRttV2N.js → package-B0sdi6lU.js} +1 -1
  63. package/dist/{phone-DVSZHt_j.js → phone-V2nuzPu9.js} +1 -1
  64. package/dist/{plus-C_XZDwBz.js → plus-BqQxkfoU.js} +1 -1
  65. package/dist/{refresh-cw-CfgN67l3.js → refresh-cw-CEOAPEcV.js} +1 -1
  66. package/dist/route-CZWcW-Dw.js +22 -0
  67. package/dist/{save-DJE8Fpdv.js → save-DDFvrxWl.js} +1 -1
  68. package/dist/{search-D_VyGcZT.js → search-D7Ya6jxj.js} +1 -1
  69. package/dist/{square-pen-D-6JYrVQ.js → square-pen-DoZZS4Lw.js} +1 -1
  70. package/dist/{star-BpRlUZuU.js → star-B7PgVu5j.js} +1 -1
  71. package/dist/{store-B-8HMuyZ.js → store-YVzcIG4A.js} +1 -1
  72. package/dist/{trash-2-8vmv-XV_.js → trash-2-BegnBzwI.js} +1 -1
  73. package/dist/{trending-up-Dz6PqPRj.js → trending-up-DBd3XNdb.js} +1 -1
  74. package/dist/{triangle-alert-D7Y3J_4z.js → triangle-alert-Bxk3Kgs6.js} +1 -1
  75. package/dist/{truck-D-y2IDzv.js → truck-Clrn0oHp.js} +1 -1
  76. package/dist/{upload-CHZofOqb.js → upload-Df5JmMwH.js} +1 -1
  77. package/dist/{useAssistant-DVW4QgQb.js → useAssistant-Cb8nnIVC.js} +11 -28
  78. package/dist/{useCustomers-BPW4__PR.js → useCustomers-Dq2IR2HQ.js} +1 -1
  79. package/dist/{useFleet-ChBkxAd6.js → useFleet-CapnWBcA.js} +1 -1
  80. package/dist/{useImportExport-DrPDHEDK.js → useImportExport-BoGzrlJy.js} +1 -1
  81. package/dist/{useInventory-CAAVMRp6.js → useInventory-Ci6M2d9f.js} +1 -1
  82. package/dist/{useOrders-B465S2nt.js → useOrders-BuQU1OHX.js} +1 -1
  83. package/dist/{useReports-jQ3GpNOm.js → useReports-DIx8W7CO.js} +1 -1
  84. package/dist/{useRoutes-hS3d5-xr.js → useRoutes-D7EvI_pm.js} +1 -1
  85. package/dist/{useSettings-BB8X2RLh.js → useSettings-BhX251UQ.js} +6 -6
  86. package/dist/{useTracking-RjGz9PZG.js → useTracking-C40jmZ6W.js} +3 -3
  87. package/dist/{user-DzfTn4dE.js → user-BlcJrbtE.js} +1 -1
  88. package/dist/{user-check-BEKRCi7v.js → user-check-DtnQmimP.js} +1 -1
  89. package/dist/{users-CCrq-w0C.js → users-Cvwko-Dx.js} +1 -1
  90. package/dist/{warehouse-CaqfmMKh.js → warehouse-LWLVTk6G.js} +1 -1
  91. package/dist/{wrench-q_rgJfaP.js → wrench-Cme0RbyR.js} +1 -1
  92. package/dist/{x-tMeDMcCw.js → x-D8C9Pg5V.js} +1 -1
  93. package/package.json +5 -1
  94. package/src/movethewheels/components/GlassPanel.tsx +71 -0
  95. package/src/movethewheels/components/MetricCard.tsx +171 -73
  96. package/src/movethewheels/components/StatusBadge.tsx +7 -6
  97. package/src/movethewheels/components/charts/ChartShell.tsx +78 -0
  98. package/src/movethewheels/components/charts/Sparkline.tsx +85 -0
  99. package/src/movethewheels/components/charts/StatusDonut.tsx +115 -0
  100. package/src/movethewheels/components/charts/TrendChart.tsx +179 -0
  101. package/src/movethewheels/components/charts/VolumeBar.tsx +97 -0
  102. package/src/movethewheels/components/charts/chartTokens.ts +43 -0
  103. package/src/movethewheels/components/charts/index.ts +9 -0
  104. package/src/movethewheels/components/index.ts +44 -1
  105. package/src/movethewheels/components/maps/LogisticsMap.tsx +94 -0
  106. package/src/movethewheels/components/maps/MapCanvas.tsx +118 -0
  107. package/src/movethewheels/components/maps/RouteMap.tsx +71 -0
  108. package/src/movethewheels/components/maps/TrackingMap.tsx +102 -0
  109. package/src/movethewheels/components/maps/index.ts +16 -0
  110. package/src/movethewheels/components/maps/leaflet-setup.ts +124 -0
  111. package/src/movethewheels/components/maps/map-markers.css +121 -0
  112. package/src/movethewheels/hooks/useAnalytics.ts +14 -4
  113. package/src/movethewheels/pages/AnalyticsPage.tsx +93 -73
  114. package/src/movethewheels/pages/CreateOrderPage.tsx +4 -4
  115. package/src/movethewheels/pages/CustomersPage.tsx +29 -20
  116. package/src/movethewheels/pages/DashboardPage.tsx +238 -12
  117. package/src/movethewheels/pages/DriverDetailsPage.tsx +1 -1
  118. package/src/movethewheels/pages/DriversPage.tsx +35 -24
  119. package/src/movethewheels/pages/FinancePage.tsx +68 -30
  120. package/src/movethewheels/pages/InventoryPage.tsx +15 -3
  121. package/src/movethewheels/pages/LiveTrackingPage.tsx +124 -16
  122. package/src/movethewheels/pages/OrdersListPage.tsx +46 -38
  123. package/src/movethewheels/pages/ProductsPage.tsx +28 -24
  124. package/src/movethewheels/pages/ReportsPage.tsx +23 -0
  125. package/src/movethewheels/pages/RouteDetailsPage.tsx +46 -7
  126. package/src/movethewheels/pages/SettingsPage.tsx +2 -2
  127. package/src/movethewheels/pages/TrackingPage.tsx +76 -6
  128. package/src/movethewheels/pages/VehicleDetailsPage.tsx +1 -1
  129. package/src/movethewheels/pages/VehiclesPage.tsx +34 -24
  130. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +1 -1
  131. package/src/movethewheels/pages/WarehousesPage.tsx +28 -22
  132. package/src/movethewheels/types/index.ts +1 -1
  133. package/src/movethewheels/utils/graphqlClient.ts +81 -31
  134. package/src/movethewheels/utils/mapMarkers.ts +58 -0
  135. package/dist/AnalyticsPage-CMpI1070.js +0 -215
  136. package/dist/CustomersPage-CaNBMXmM.js +0 -172
  137. package/dist/DashboardPage-DIKlXde3.js +0 -387
  138. package/dist/DriversPage-PYmdFiby.js +0 -176
  139. package/dist/FinancePage-Cwh9Bw4M.js +0 -136
  140. package/dist/InventoryPage-DpiqVu1v.js +0 -253
  141. package/dist/LiveTrackingPage-FAzzlVnX.js +0 -379
  142. package/dist/MetricCard-CVBVpu7o.js +0 -146
  143. package/dist/OrdersListPage-CAMsuUck.js +0 -384
  144. package/dist/ProductsPage-DC5qDLfz.js +0 -180
  145. package/dist/ReportsPage-BwUJ37eO.js +0 -356
  146. package/dist/TrackingPage-DaNEFG_h.js +0 -325
  147. package/dist/VehiclesPage-BFw13iMt.js +0 -176
  148. package/dist/WarehousesPage-Wkytnt86.js +0 -173
  149. package/dist/useAnalytics-D7HW2XRb.js +0 -347
  150. /package/dist/{formatters-BQr-aR5W.js → formatters-FWJDwTFv.js} +0 -0
@@ -2,7 +2,15 @@
2
2
  * Inventory Page - Overview of inventory management
3
3
  */
4
4
 
5
- import { Package, Warehouse, AlertTriangle, ChevronRight, Plus } from 'lucide-react';
5
+ import {
6
+ Package,
7
+ Warehouse,
8
+ AlertTriangle,
9
+ ChevronRight,
10
+ Plus,
11
+ Boxes,
12
+ DollarSign,
13
+ } from 'lucide-react';
6
14
  import { PageLayout, PageSection } from '../components/PageLayout';
7
15
  import { MetricCard, MetricGrid, MetricCardSkeleton } from '../components/MetricCard';
8
16
  import { Card, CardContent, Button, Badge, Progress } from '../components/ui';
@@ -88,22 +96,26 @@ export default function InventoryPage() {
88
96
  label={t('movethewheels.inventory.metrics.totalProducts', 'Total Products')}
89
97
  value={stats.totalProducts}
90
98
  icon={<Package size={20} />}
99
+ accent="brand"
91
100
  onClick={() => goTo('inventory/products')}
92
101
  />
93
102
  <MetricCard
94
103
  label={t('movethewheels.inventory.metrics.totalItems', 'Total Items')}
95
104
  value={stats.totalItems.toLocaleString()}
96
- icon={<Package size={20} />}
105
+ icon={<Boxes size={20} />}
106
+ accent="success"
97
107
  />
98
108
  <MetricCard
99
109
  label={t('movethewheels.inventory.metrics.inventoryValue', 'Inventory Value')}
100
110
  value={formatCurrency(stats.totalValue)}
101
- icon={<Package size={20} />}
111
+ icon={<DollarSign size={20} />}
112
+ accent="blue"
102
113
  />
103
114
  <MetricCard
104
115
  label={t('movethewheels.inventory.metrics.warehouses', 'Warehouses')}
105
116
  value={stats.totalWarehouses}
106
117
  icon={<Warehouse size={20} />}
118
+ accent="neutral"
107
119
  onClick={() => goTo('inventory/warehouses')}
108
120
  />
109
121
  </MetricGrid>
@@ -4,11 +4,13 @@
4
4
  * Real-time map view of all active deliveries.
5
5
  */
6
6
 
7
- import { useState } from 'react';
7
+ import { useMemo, useState } from 'react';
8
8
  import { MapPin, Truck, Package, RefreshCw, List, Map } from 'lucide-react';
9
9
  import { PageLayout, EmptyState } from '../components/PageLayout';
10
10
  import { StatusBadge } from '../components/StatusBadge';
11
11
  import { MetricCard, MetricGrid } from '../components/MetricCard';
12
+ import { LogisticsMap, type LogisticsMarker } from '../components/maps';
13
+ import { deliveryMarkerStatus, firstRealLatLng } from '../utils/mapMarkers';
12
14
  import {
13
15
  Card,
14
16
  CardContent,
@@ -27,12 +29,15 @@ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
27
29
  import { formatDate } from '../utils/formatters';
28
30
  import { joinPath } from '../utils/navigation';
29
31
 
32
+ type LiveFilter = 'all' | 'in_transit' | 'out_for_delivery' | 'other';
33
+
30
34
  export default function LiveTrackingPage() {
31
35
  const { basePath, navigate } = useMoveTheWheels();
32
36
  const { activeDeliveries, isLoading, refetch } = useLiveTracking();
33
37
  const { unacknowledgedAlerts } = useTrackingAlerts();
34
38
  const [viewMode, setViewMode] = useState<'map' | 'list'>('map');
35
39
  const [selectedDelivery, setSelectedDelivery] = useState<string | null>(null);
40
+ const [filter, setFilter] = useState<LiveFilter>('all');
36
41
 
37
42
  const goTo = (path: string) => {
38
43
  if (navigate) {
@@ -44,6 +49,70 @@ export default function LiveTrackingPage() {
44
49
  ? activeDeliveries.find((d) => d.order?.id === selectedDelivery)
45
50
  : null;
46
51
 
52
+ const filteredDeliveries = useMemo(
53
+ () =>
54
+ activeDeliveries.filter((d) => {
55
+ const status = d.order?.status;
56
+ if (filter === 'all') return true;
57
+ if (filter === 'in_transit') return status === 'in_transit';
58
+ if (filter === 'out_for_delivery') return status === 'out_for_delivery';
59
+ return status !== 'in_transit' && status !== 'out_for_delivery';
60
+ }),
61
+ [activeDeliveries, filter]
62
+ );
63
+
64
+ // Build brand markers from the best real coordinate we hold for each delivery —
65
+ // the vehicle GPS fix, else the driver's, else the latest tracking event.
66
+ // Deliveries with no real coordinate are honestly omitted (never plotted at 0,0).
67
+ const markers = useMemo<LogisticsMarker[]>(() => {
68
+ return filteredDeliveries.flatMap((d) => {
69
+ if (!d.order) return [];
70
+ const coord = firstRealLatLng(
71
+ d.vehicle?.currentLocation,
72
+ d.driver?.currentLocation,
73
+ d.tracking?.currentLocation
74
+ );
75
+ if (!coord) return [];
76
+ return [
77
+ {
78
+ id: d.order.id,
79
+ lat: coord.lat,
80
+ lng: coord.lng,
81
+ label: d.order.orderNumber,
82
+ status: deliveryMarkerStatus(d.order.status),
83
+ sublabel:
84
+ d.driver?.fullName ||
85
+ d.vehicle?.vehicleNumber ||
86
+ d.tracking?.currentLocation.address ||
87
+ undefined,
88
+ },
89
+ ];
90
+ });
91
+ }, [filteredDeliveries]);
92
+
93
+ const locatedCount = markers.length;
94
+
95
+ const filterChips: { value: LiveFilter; label: string; count: number }[] = [
96
+ { value: 'all', label: 'All', count: activeDeliveries.length },
97
+ {
98
+ value: 'in_transit',
99
+ label: 'In transit',
100
+ count: activeDeliveries.filter((d) => d.order?.status === 'in_transit').length,
101
+ },
102
+ {
103
+ value: 'out_for_delivery',
104
+ label: 'Out for delivery',
105
+ count: activeDeliveries.filter((d) => d.order?.status === 'out_for_delivery').length,
106
+ },
107
+ {
108
+ value: 'other',
109
+ label: 'Other',
110
+ count: activeDeliveries.filter(
111
+ (d) => d.order?.status !== 'in_transit' && d.order?.status !== 'out_for_delivery'
112
+ ).length,
113
+ },
114
+ ];
115
+
47
116
  return (
48
117
  <PageLayout
49
118
  title="Live Tracking"
@@ -134,20 +203,37 @@ export default function LiveTrackingPage() {
134
203
  {/* Map / List View */}
135
204
  <div className="lg:col-span-2">
136
205
  {viewMode === 'map' ? (
137
- <Card className="h-[600px]">
138
- <CardContent className="p-0 h-full">
139
- <div className="w-full h-full bg-bg-sunken rounded-lg flex items-center justify-center">
140
- <div className="text-center text-text-secondary">
141
- <MapPin size={64} className="mx-auto mb-4 opacity-50" />
142
- <p className="text-lg font-medium">Map View</p>
143
- <p className="text-sm">
144
- Interactive map showing {activeDeliveries.length} active deliveries
145
- </p>
146
- <p className="text-xs mt-2">
147
- (Map integration with Mapbox/Google Maps would go here)
148
- </p>
149
- </div>
206
+ <Card className="overflow-hidden">
207
+ <CardContent className="p-0">
208
+ <div className="flex flex-wrap items-center gap-2 border-b border-border-seam p-3">
209
+ {filterChips.map((chip) => (
210
+ <button
211
+ key={chip.value}
212
+ type="button"
213
+ onClick={() => setFilter(chip.value)}
214
+ className={`inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg ${
215
+ filter === chip.value
216
+ ? 'border-transparent bg-action-primary-bg text-action-primary-text'
217
+ : 'border-border-seam text-text-secondary hover:bg-bg-sunken/60'
218
+ }`}
219
+ >
220
+ {chip.label}
221
+ <span className="tabular-nums opacity-80">{chip.count}</span>
222
+ </button>
223
+ ))}
224
+ <span className="ml-auto text-xs text-text-secondary">
225
+ {locatedCount} of {filteredDeliveries.length} located
226
+ </span>
150
227
  </div>
228
+ <LogisticsMap
229
+ markers={markers}
230
+ height={560}
231
+ emptyMessage={
232
+ filteredDeliveries.length === 0
233
+ ? 'No active deliveries to plot'
234
+ : 'Awaiting live GPS positions for these deliveries'
235
+ }
236
+ />
151
237
  </CardContent>
152
238
  </Card>
153
239
  ) : (
@@ -158,7 +244,7 @@ export default function LiveTrackingPage() {
158
244
  <CardContent className="p-0">
159
245
  <ScrollArea className="h-[550px]">
160
246
  <div className="divide-y">
161
- {activeDeliveries.map((delivery) => {
247
+ {filteredDeliveries.map((delivery) => {
162
248
  if (!delivery.order) {
163
249
  return null;
164
250
  }
@@ -214,7 +300,7 @@ export default function LiveTrackingPage() {
214
300
  </div>
215
301
  );
216
302
  })}
217
- {activeDeliveries.length === 0 && (
303
+ {filteredDeliveries.length === 0 && (
218
304
  <EmptyState
219
305
  illustration="empty-sessions"
220
306
  title="No active deliveries"
@@ -268,6 +354,28 @@ export default function LiveTrackingPage() {
268
354
  <p className="text-sm">
269
355
  {selected.tracking?.currentLocation.address || 'Updating...'}
270
356
  </p>
357
+ {(() => {
358
+ const coord = firstRealLatLng(
359
+ selected.vehicle?.currentLocation,
360
+ selected.driver?.currentLocation,
361
+ selected.tracking?.currentLocation
362
+ );
363
+ return coord ? (
364
+ <LogisticsMap
365
+ className="mt-3 overflow-hidden rounded-lg border border-border-seam"
366
+ height={160}
367
+ markers={[
368
+ {
369
+ id: selected.order?.id ?? 'selected',
370
+ lat: coord.lat,
371
+ lng: coord.lng,
372
+ label: selected.order?.orderNumber ?? 'Delivery',
373
+ status: deliveryMarkerStatus(selected.order?.status),
374
+ },
375
+ ]}
376
+ />
377
+ ) : null;
378
+ })()}
271
379
  </div>
272
380
  <div>
273
381
  <p className="text-sm text-text-secondary">Estimated Arrival</p>
@@ -5,11 +5,23 @@
5
5
  */
6
6
 
7
7
  import { useState } from 'react';
8
- import { Package, Plus, Filter, Download, RefreshCw, UserPlus, UserCheck } from 'lucide-react';
8
+ import {
9
+ Package,
10
+ Plus,
11
+ Filter,
12
+ Download,
13
+ RefreshCw,
14
+ UserPlus,
15
+ UserCheck,
16
+ Clock,
17
+ Truck,
18
+ CheckCircle2,
19
+ } from 'lucide-react';
9
20
  import { PageLayout } from '../components/PageLayout';
21
+ import { MetricCard, MetricGrid } from '../components/MetricCard';
10
22
  import { DataTable, type Column } from '../components/DataTable';
11
23
  import { StatusBadge, PriorityBadge, TypeBadge } from '../components/StatusBadge';
12
- import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
24
+ import { CTAOverflowMenu, PagePurpose } from '@burdenoff/fe-libs/ui';
13
25
  import {
14
26
  Button,
15
27
  Card,
@@ -219,44 +231,40 @@ export default function OrdersListPage() {
219
231
  )}
220
232
  </PagePurpose>
221
233
 
222
- {/* Pipeline at a glance — the page's single emphasis zone */}
223
- <EmphasisPanel>
224
- <p className="text-sm font-medium text-text-secondary">
234
+ {/* Pipeline at a glance — premium stat cards */}
235
+ <div>
236
+ <p className="mb-3 text-sm font-medium text-text-secondary">
225
237
  {t('movethewheels.orders.glance.title', 'Pipeline at a glance')}
226
238
  </p>
227
- <div className="mt-3 grid grid-cols-2 sm:grid-cols-4 gap-4">
228
- <div>
229
- <p className="text-2xl font-bold text-text-primary tabular-nums">{totalOrders}</p>
230
- <p className="text-xs text-text-secondary">
231
- {t('movethewheels.orders.glance.total', 'Total orders')}
232
- </p>
233
- </div>
234
- <div>
235
- <p className="text-2xl font-bold text-text-primary tabular-nums">
236
- {stats.byStatus.pending}
237
- </p>
238
- <p className="text-xs text-text-secondary">
239
- {t('movethewheels.orders.glance.pending', 'Pending')}
240
- </p>
241
- </div>
242
- <div>
243
- <p className="text-2xl font-bold text-text-primary tabular-nums">
244
- {stats.byStatus.in_transit + stats.byStatus.out_for_delivery}
245
- </p>
246
- <p className="text-xs text-text-secondary">
247
- {t('movethewheels.orders.glance.inTransit', 'In transit')}
248
- </p>
249
- </div>
250
- <div>
251
- <p className="text-2xl font-bold text-text-primary tabular-nums">
252
- {stats.byStatus.delivered}
253
- </p>
254
- <p className="text-xs text-text-secondary">
255
- {t('movethewheels.orders.glance.delivered', 'Delivered')}
256
- </p>
257
- </div>
258
- </div>
259
- </EmphasisPanel>
239
+ <MetricGrid columns={4}>
240
+ <MetricCard
241
+ label={t('movethewheels.orders.glance.total', 'Total orders')}
242
+ value={totalOrders}
243
+ icon={<Package size={20} />}
244
+ accent="brand"
245
+ />
246
+ <MetricCard
247
+ label={t('movethewheels.orders.glance.pending', 'Pending')}
248
+ value={stats.byStatus.pending}
249
+ icon={<Clock size={20} />}
250
+ accent="warning"
251
+ subtitle={t('movethewheels.orders.glance.pendingSub', 'Awaiting fulfilment')}
252
+ />
253
+ <MetricCard
254
+ label={t('movethewheels.orders.glance.inTransit', 'In transit')}
255
+ value={stats.byStatus.in_transit + stats.byStatus.out_for_delivery}
256
+ icon={<Truck size={20} />}
257
+ accent="blue"
258
+ subtitle={t('movethewheels.orders.glance.inTransitSub', 'On the road now')}
259
+ />
260
+ <MetricCard
261
+ label={t('movethewheels.orders.glance.delivered', 'Delivered')}
262
+ value={stats.byStatus.delivered}
263
+ icon={<CheckCircle2 size={20} />}
264
+ accent="success"
265
+ />
266
+ </MetricGrid>
267
+ </div>
260
268
 
261
269
  {/* Filters */}
262
270
  <Card>
@@ -3,11 +3,12 @@
3
3
  */
4
4
 
5
5
  import { useState } from 'react';
6
- import { Package, Plus, RefreshCw, Download } from 'lucide-react';
6
+ import { Package, Plus, RefreshCw, Download, AlertTriangle, Thermometer } from 'lucide-react';
7
7
  import { PageLayout } from '../components/PageLayout';
8
+ import { MetricCard, MetricGrid } from '../components/MetricCard';
8
9
  import { DataTable, type Column } from '../components/DataTable';
9
10
  import { Badge } from '../components/ui';
10
- import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
11
+ import { CTAOverflowMenu, PagePurpose } from '@burdenoff/fe-libs/ui';
11
12
  import { useProducts, useProductStats } from '../hooks/useInventory';
12
13
  import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
13
14
  import { formatCurrency } from '../utils/formatters';
@@ -128,28 +129,31 @@ export default function ProductsPage() {
128
129
  routed — keep them accurate so shipments are quoted and handled correctly.
129
130
  </PagePurpose>
130
131
 
131
- {/* Catalog at a glance — the page's single emphasis zone */}
132
- <EmphasisPanel>
133
- <p className="text-sm font-medium text-text-secondary">Catalog at a glance</p>
134
- <div className="mt-3 grid grid-cols-3 gap-4">
135
- <div>
136
- <p className="text-2xl font-bold text-text-primary tabular-nums">{stats.total}</p>
137
- <p className="text-xs text-text-secondary">Total products</p>
138
- </div>
139
- <div>
140
- <p className="text-2xl font-bold text-text-primary tabular-nums">
141
- {stats.specialHandling}
142
- </p>
143
- <p className="text-xs text-text-secondary">Special handling</p>
144
- </div>
145
- <div>
146
- <p className="text-2xl font-bold text-text-primary tabular-nums">
147
- {stats.temperatureControlled}
148
- </p>
149
- <p className="text-xs text-text-secondary">Temp-controlled</p>
150
- </div>
151
- </div>
152
- </EmphasisPanel>
132
+ {/* Catalog at a glance — premium stat cards */}
133
+ <div>
134
+ <p className="mb-3 text-sm font-medium text-text-secondary">Catalog at a glance</p>
135
+ <MetricGrid columns={3}>
136
+ <MetricCard
137
+ label="Total products"
138
+ value={stats.total}
139
+ icon={<Package size={20} />}
140
+ accent="brand"
141
+ />
142
+ <MetricCard
143
+ label="Special handling"
144
+ value={stats.specialHandling}
145
+ icon={<AlertTriangle size={20} />}
146
+ accent="warning"
147
+ subtitle="Fragile or hazardous"
148
+ />
149
+ <MetricCard
150
+ label="Temp-controlled"
151
+ value={stats.temperatureControlled}
152
+ icon={<Thermometer size={20} />}
153
+ accent="blue"
154
+ />
155
+ </MetricGrid>
156
+ </div>
153
157
 
154
158
  <DataTable
155
159
  columns={columns}
@@ -33,7 +33,9 @@ import {
33
33
  Skeleton,
34
34
  } from '../components/ui';
35
35
  import { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';
36
+ import { VolumeBar } from '../components/charts';
36
37
  import { useReports, type CreateReportInput } from '../hooks/useReports';
38
+ import { useAnalytics } from '../hooks/useAnalytics';
37
39
  import { formatDate } from '../utils/formatters';
38
40
  import { downloadFile, downloadJson, recordToCsv, slugifyForFilename } from '../utils/download';
39
41
 
@@ -58,6 +60,8 @@ export default function ReportsPage() {
58
60
  limit: REPORTS_PAGE_SIZE,
59
61
  offset: page * REPORTS_PAGE_SIZE,
60
62
  });
63
+ const { analytics } = useAnalytics();
64
+ const previewData = analytics.trends.dailyOrders.slice(-7);
61
65
  const [reportType, setReportType] = useState('operations');
62
66
  const [busyId, setBusyId] = useState<string | null>(null);
63
67
  const [creating, setCreating] = useState(false);
@@ -276,6 +280,25 @@ export default function ReportsPage() {
276
280
  </div>
277
281
 
278
282
  <div className="space-y-6">
283
+ {/* Data preview — a real snapshot (recent order volume) of what a
284
+ generated report draws from. Honest empty state when no data. */}
285
+ <Card>
286
+ <CardHeader>
287
+ <CardTitle className="text-base">Data preview</CardTitle>
288
+ </CardHeader>
289
+ <CardContent>
290
+ <p className="mb-2 text-xs text-text-secondary">Order volume · last 7 days</p>
291
+ <VolumeBar
292
+ xKey="date"
293
+ height={160}
294
+ showLegend={false}
295
+ data={previewData}
296
+ series={[{ dataKey: 'orders', name: 'Orders' }]}
297
+ emptyMessage="No order data to preview yet"
298
+ />
299
+ </CardContent>
300
+ </Card>
301
+
279
302
  {/* Scheduled Reports — the page's single emphasis zone */}
280
303
  <EmphasisPanel className="space-y-3">
281
304
  <h2 className="flex items-center gap-2 text-base font-semibold text-text-primary">
@@ -37,6 +37,8 @@ import {
37
37
  SelectValue,
38
38
  } from '../components/ui';
39
39
  import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
40
+ import { RouteMap, type RouteStop as MapRouteStop } from '../components/maps';
41
+ import { firstRealLatLng } from '../utils/mapMarkers';
40
42
  import { useRoute, useRoutes } from '../hooks/useRoutes';
41
43
  import { useFleet } from '../hooks/useFleet';
42
44
  import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
@@ -165,8 +167,9 @@ function NewRouteForm() {
165
167
  }
166
168
  >
167
169
  <PagePurpose icon={<Navigation size={18} />}>
168
- Plan a new delivery route — name it, list the stops to visit, and optionally assign a vehicle
169
- and driver. Once created you can optimize it to sequence the stops into the shortest run.
170
+ Plan a new delivery route — name it, list the stops to visit, and optionally assign a
171
+ vehicle and driver. Once created you can optimize it to sequence the stops into the shortest
172
+ run.
170
173
  </PagePurpose>
171
174
 
172
175
  <form onSubmit={handleSubmit} className="max-w-3xl space-y-6">
@@ -363,6 +366,22 @@ export default function RouteDetailsPage() {
363
366
  if (isLoading) return <LoadingState message="Loading route details..." />;
364
367
  if (error || !route) return <ErrorState title="Route not found" onRetry={refetch} />;
365
368
 
369
+ // Parse coordinates off each waypoint's address; stops without a real coord are
370
+ // dropped from the map (but still listed below), so the plotted line is honest.
371
+ const mapStops: MapRouteStop[] = route.stops
372
+ .map((stop) => {
373
+ const coord = firstRealLatLng(stop.address);
374
+ return coord
375
+ ? {
376
+ lat: coord.lat,
377
+ lng: coord.lng,
378
+ sequence: stop.sequence,
379
+ label: formatAddress(stop.address) || `Order #${stop.orderId || stop.sequence}`,
380
+ }
381
+ : null;
382
+ })
383
+ .filter((stop): stop is MapRouteStop => stop !== null);
384
+
366
385
  return (
367
386
  <PageLayout
368
387
  title={route.name}
@@ -447,12 +466,32 @@ export default function RouteDetailsPage() {
447
466
  <CardTitle>Route Map</CardTitle>
448
467
  </CardHeader>
449
468
  <CardContent>
450
- <div className="aspect-video bg-bg-sunken rounded-lg flex items-center justify-center">
451
- <div className="text-center text-text-secondary">
452
- <MapPin size={48} className="mx-auto mb-2 opacity-50" />
453
- <p>Route map visualization</p>
469
+ {mapStops.length > 0 ? (
470
+ <RouteMap
471
+ className="overflow-hidden rounded-lg border border-border-seam"
472
+ height={360}
473
+ stops={mapStops}
474
+ />
475
+ ) : (
476
+ <div className="rounded-lg border border-border-seam bg-bg-sunken/40 p-4">
477
+ <p className="mb-3 text-sm text-text-secondary">
478
+ These stops don&apos;t have map coordinates yet — showing the planned sequence
479
+ instead.
480
+ </p>
481
+ <ol className="space-y-2">
482
+ {route.stops.map((stop) => (
483
+ <li key={stop.id} className="flex items-start gap-3">
484
+ <span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-action-primary-bg/10 text-xs font-semibold text-primary">
485
+ {stop.sequence}
486
+ </span>
487
+ <span className="text-sm">
488
+ {formatAddress(stop.address) || `Order #${stop.orderId || stop.sequence}`}
489
+ </span>
490
+ </li>
491
+ ))}
492
+ </ol>
454
493
  </div>
455
- </div>
494
+ )}
456
495
  </CardContent>
457
496
  </Card>
458
497
 
@@ -222,7 +222,7 @@ export default function SettingsPage() {
222
222
  <EmphasisPanel>
223
223
  <h2 className="text-base font-semibold text-text-primary">Company Information</h2>
224
224
  <p className="text-sm text-text-secondary mb-4">Basic company details</p>
225
- <div className="grid grid-cols-2 gap-4">
225
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
226
226
  <div>
227
227
  <Label>Company Name</Label>
228
228
  <Input
@@ -270,7 +270,7 @@ export default function SettingsPage() {
270
270
  </CardTitle>
271
271
  </CardHeader>
272
272
  <CardContent className="space-y-4">
273
- <div className="grid grid-cols-3 gap-4">
273
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
274
274
  <div>
275
275
  <Label>Timezone</Label>
276
276
  <Select