@burdenoff/microfe-movethewheels 2026.703.2 → 2026.703.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/{AIAssistantPage-C3cPsWt-.js → AIAssistantPage-3YOnHO9Y.js} +3 -3
  2. package/dist/{AnalyticsPage-Dz6g2MvK.js → AnalyticsPage-CMpI1070.js} +11 -11
  3. package/dist/CreateOrderPage-DRAgGo9i.js +490 -0
  4. package/dist/CustomerDetailsPage-BCeMmOFe.js +615 -0
  5. package/dist/{CustomersPage-2Y05LVyh.js → CustomersPage-CaNBMXmM.js} +64 -53
  6. package/dist/{DashboardPage-BVjQi0EB.js → DashboardPage-DIKlXde3.js} +16 -16
  7. package/dist/DataTable-XvUcTjwi.js +340 -0
  8. package/dist/DocumentsCard-U2lSe-cn.js +169 -0
  9. package/dist/{DriverDetailsPage-CN3WQ0B9.js → DriverDetailsPage-BQrYd_Lp.js} +178 -195
  10. package/dist/{DriversPage-B0Zpx8OQ.js → DriversPage-PYmdFiby.js} +65 -54
  11. package/dist/FinancePage-Cwh9Bw4M.js +136 -0
  12. package/dist/{FleetPage-DqkFi3s3.js → FleetPage-3ZHsB-TN.js} +23 -22
  13. package/dist/{FormError-BlOjnXOq.js → FormError-cuLV2jVD.js} +1 -1
  14. package/dist/{ImportExportPage-DKJRCezl.js → ImportExportPage-C6f7DqhM.js} +50 -70
  15. package/dist/{InventoryPage-DwfgCUQq.js → InventoryPage-DpiqVu1v.js} +9 -9
  16. package/dist/{LiveTrackingPage-DCX-K-LR.js → LiveTrackingPage-FAzzlVnX.js} +9 -9
  17. package/dist/MarketplacePage-Bh_2ns4v.js +426 -0
  18. package/dist/Notice-CSoDYKw9.js +46 -0
  19. package/dist/OrderDetailsPage-zCj0lW0j.js +584 -0
  20. package/dist/{OrdersListPage-BjDlCKJd.js → OrdersListPage-CAMsuUck.js} +166 -89
  21. package/dist/{ProductDetailsPage-C9uOiYLp.js → ProductDetailsPage-wsxAQkA0.js} +12 -12
  22. package/dist/{ProductsPage-C1G9h7Xd.js → ProductsPage-DC5qDLfz.js} +60 -49
  23. package/dist/ReportsPage-BwUJ37eO.js +356 -0
  24. package/dist/RouteDetailsPage-DyMJE9AS.js +583 -0
  25. package/dist/{RoutesPage-DMfoicM1.js → RoutesPage-Dtc669yc.js} +57 -46
  26. package/dist/{SettingsPage-Bn-v0mbQ.js → SettingsPage-CpH9xhQg.js} +5 -27
  27. package/dist/{TrackingPage-W-jtdM3Y.js → TrackingPage-DaNEFG_h.js} +10 -10
  28. package/dist/{VehicleDetailsPage-Bceo5oz3.js → VehicleDetailsPage-DldYNsYV.js} +125 -142
  29. package/dist/{VehiclesPage-VBtOep91.js → VehiclesPage-BFw13iMt.js} +61 -50
  30. package/dist/{WarehouseDetailsPage-L_6sWigM.js → WarehouseDetailsPage-CMG7or6G.js} +17 -17
  31. package/dist/{WarehousesPage-DCbXXXWE.js → WarehousesPage-Wkytnt86.js} +58 -47
  32. package/dist/arrow-right-Q3XQpTPR.js +11 -0
  33. package/dist/arrow-up-xNF1zJUG.js +17 -0
  34. package/dist/chevron-left-B2_r0Elx.js +8 -0
  35. package/dist/{wrench-CANXWxF-.js → fuel-B6_lnKDs.js} +2 -5
  36. package/dist/index.js +163 -174
  37. package/dist/upload-CHZofOqb.js +21 -0
  38. package/dist/{useAnalytics-DS8n0xgg.js → useAnalytics-D7HW2XRb.js} +52 -2
  39. package/dist/{useCustomers-BmTNGxiI.js → useCustomers-BPW4__PR.js} +29 -26
  40. package/dist/{useFleet-DKqXZ16m.js → useFleet-ChBkxAd6.js} +81 -69
  41. package/dist/{useInventory-DkyiVY_A.js → useInventory-CAAVMRp6.js} +83 -38
  42. package/dist/{useOrders-Cfk1GLiq.js → useOrders-B465S2nt.js} +58 -47
  43. package/dist/{useReports-CTqZr-AB.js → useReports-jQ3GpNOm.js} +40 -34
  44. package/dist/{useRoutes-BQWO607N.js → useRoutes-hS3d5-xr.js} +49 -41
  45. package/dist/{useTracking-B-va1kO3.js → useTracking-RjGz9PZG.js} +2 -2
  46. package/dist/user-check-BEKRCi7v.js +20 -0
  47. package/dist/wrench-q_rgJfaP.js +8 -0
  48. package/dist/x-tMeDMcCw.js +11 -0
  49. package/package.json +1 -1
  50. package/src/movethewheels/components/DataTable.tsx +252 -84
  51. package/src/movethewheels/components/DocumentsCard.tsx +102 -0
  52. package/src/movethewheels/components/Notice.tsx +61 -0
  53. package/src/movethewheels/components/index.ts +7 -0
  54. package/src/movethewheels/hooks/index.ts +9 -0
  55. package/src/movethewheels/hooks/useAnalytics.ts +95 -0
  56. package/src/movethewheels/hooks/useCustomers.ts +11 -4
  57. package/src/movethewheels/hooks/useEntityDocuments.ts +132 -0
  58. package/src/movethewheels/hooks/useFleet.ts +26 -7
  59. package/src/movethewheels/hooks/useInventory.ts +70 -7
  60. package/src/movethewheels/hooks/useOrders.ts +32 -2
  61. package/src/movethewheels/hooks/useReports.ts +14 -4
  62. package/src/movethewheels/hooks/useRoutes.ts +20 -3
  63. package/src/movethewheels/pages/AnalyticsPage.tsx +2 -2
  64. package/src/movethewheels/pages/CreateOrderPage.tsx +44 -16
  65. package/src/movethewheels/pages/CustomerDetailsPage.tsx +239 -33
  66. package/src/movethewheels/pages/CustomersPage.tsx +27 -11
  67. package/src/movethewheels/pages/DriverDetailsPage.tsx +16 -30
  68. package/src/movethewheels/pages/DriversPage.tsx +26 -9
  69. package/src/movethewheels/pages/FinancePage.tsx +67 -84
  70. package/src/movethewheels/pages/InventoryPage.tsx +1 -1
  71. package/src/movethewheels/pages/MarketplacePage.tsx +265 -50
  72. package/src/movethewheels/pages/OrderDetailsPage.tsx +214 -10
  73. package/src/movethewheels/pages/OrdersListPage.tsx +77 -14
  74. package/src/movethewheels/pages/ProductsPage.tsx +27 -7
  75. package/src/movethewheels/pages/ReportsPage.tsx +65 -4
  76. package/src/movethewheels/pages/RouteDetailsPage.tsx +74 -13
  77. package/src/movethewheels/pages/RoutesPage.tsx +22 -6
  78. package/src/movethewheels/pages/SettingsPage.tsx +3 -19
  79. package/src/movethewheels/pages/VehicleDetailsPage.tsx +3 -33
  80. package/src/movethewheels/pages/VehiclesPage.tsx +26 -5
  81. package/src/movethewheels/pages/WarehousesPage.tsx +22 -17
  82. package/src/movethewheels/types/index.ts +8 -0
  83. package/src/movethewheels/utils/download.ts +62 -0
  84. package/dist/CreateOrderPage-C5iwnjp4.js +0 -479
  85. package/dist/CustomerDetailsPage-B6nwFyLO.js +0 -406
  86. package/dist/DataTable-DmciIKKM.js +0 -249
  87. package/dist/FinancePage-DEDykSOP.js +0 -158
  88. package/dist/MarketplacePage-CIQ97_zp.js +0 -247
  89. package/dist/OrderDetailsPage-DYiZ-9UK.js +0 -429
  90. package/dist/ReportsPage-Zp53q3_8.js +0 -284
  91. package/dist/RouteDetailsPage-DUStXSf9.js +0 -531
  92. /package/dist/{MetricCard-BY0QrMD4.js → MetricCard-CVBVpu7o.js} +0 -0
  93. /package/dist/{StatusBadge-BZurYHMy.js → StatusBadge-C87mDpDP.js} +0 -0
  94. /package/dist/{box-D9Pal4HM.js → box-Eu05PXDh.js} +0 -0
  95. /package/dist/{chart-column-BFIJysDR.js → chart-column-ouzgxIv_.js} +0 -0
  96. /package/dist/{chevron-right-EMIIyyJ1.js → chevron-right-eV4LjaSi.js} +0 -0
  97. /package/dist/{circle-alert-iY24lSlp.js → circle-alert-Bzh2DBSF.js} +0 -0
  98. /package/dist/{circle-check-big-w0TrLKMF.js → circle-check-big-CGMSpQmD.js} +0 -0
  99. /package/dist/{clock-DJZtdl8u.js → clock-Bl3ptexL.js} +0 -0
  100. /package/dist/{dollar-sign-CgKQRz_e.js → dollar-sign-CAVU95SO.js} +0 -0
  101. /package/dist/{download-DRXxoiQR.js → download-C5NbvVDc.js} +0 -0
  102. /package/dist/{file-text-DWWY6_f5.js → file-text-D-1uvLZG.js} +0 -0
  103. /package/dist/{filter-DloLC2eE.js → filter-DUfUg6BO.js} +0 -0
  104. /package/dist/{formatters-CF2S2l1m.js → formatters-BQr-aR5W.js} +0 -0
  105. /package/dist/{map-CkJaULb2.js → map-C-M-FQLR.js} +0 -0
  106. /package/dist/{map-pin-BGiCvyCs.js → map-pin-Dznl5l40.js} +0 -0
  107. /package/dist/{navigation-D36prnAn.js → navigation-BJSJl-VG.js} +0 -0
  108. /package/dist/{package-BChUzpJS.js → package-CcRttV2N.js} +0 -0
  109. /package/dist/{phone-Ac9s3mo_.js → phone-DVSZHt_j.js} +0 -0
  110. /package/dist/{plus-Z76q-FZm.js → plus-C_XZDwBz.js} +0 -0
  111. /package/dist/{refresh-cw-DKt6f5T6.js → refresh-cw-CfgN67l3.js} +0 -0
  112. /package/dist/{save-CojywNhG.js → save-DJE8Fpdv.js} +0 -0
  113. /package/dist/{search-CDGefqb3.js → search-D_VyGcZT.js} +0 -0
  114. /package/dist/{square-pen-CopE9GmB.js → square-pen-D-6JYrVQ.js} +0 -0
  115. /package/dist/{star-nHlLjDKR.js → star-BpRlUZuU.js} +0 -0
  116. /package/dist/{store-BR9Voi5E.js → store-B-8HMuyZ.js} +0 -0
  117. /package/dist/{trash-2-BKhar6Hs.js → trash-2-8vmv-XV_.js} +0 -0
  118. /package/dist/{trending-up-Dohgd4t_.js → trending-up-Dz6PqPRj.js} +0 -0
  119. /package/dist/{triangle-alert-i-XQL9Rd.js → triangle-alert-D7Y3J_4z.js} +0 -0
  120. /package/dist/{truck-C0v_hbLR.js → truck-D-y2IDzv.js} +0 -0
  121. /package/dist/{useAssistant-D4v3oxD3.js → useAssistant-DVW4QgQb.js} +0 -0
  122. /package/dist/{useImportExport-DHr-SWSw.js → useImportExport-DrPDHEDK.js} +0 -0
  123. /package/dist/{useSettings-CkDH_-gM.js → useSettings-BB8X2RLh.js} +0 -0
  124. /package/dist/{user-jyFU3lXk.js → user-DzfTn4dE.js} +0 -0
  125. /package/dist/{users-DZ-ilS6B.js → users-CCrq-w0C.js} +0 -0
  126. /package/dist/{warehouse-6m5kXMLu.js → warehouse-CaqfmMKh.js} +0 -0
@@ -2,13 +2,14 @@
2
2
  * Customers Page
3
3
  */
4
4
 
5
+ import { useState } from 'react';
5
6
  import { Users, Plus, RefreshCw, Download, Star } from 'lucide-react';
6
7
  import { PageLayout } from '../components/PageLayout';
7
8
  import { DataTable, type Column } from '../components/DataTable';
8
9
  import { TypeBadge } from '../components/StatusBadge';
9
10
  import { Badge, Avatar, AvatarFallback, Button } from '../components/ui';
10
11
  import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
11
- import { useCustomers } from '../hooks/useCustomers';
12
+ import { useCustomers, useCustomerStats } from '../hooks/useCustomers';
12
13
  import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
13
14
  import { formatDate } from '../utils/formatters';
14
15
  import { joinPath } from '../utils/navigation';
@@ -16,7 +17,14 @@ import type { Customer } from '../types';
16
17
 
17
18
  export default function CustomersPage() {
18
19
  const { basePath, navigate } = useMoveTheWheels();
19
- const { customers, totalCustomers, isLoading, refetch } = useCustomers({ limit: 100 });
20
+ const [page, setPage] = useState(0);
21
+ const [pageSize, setPageSize] = useState(25);
22
+ const { customers, hasMore, isLoading, refetch } = useCustomers({
23
+ limit: pageSize,
24
+ offset: page * pageSize,
25
+ });
26
+ // Bounded aggregate for the tiles + subtitle — never the truncated page.
27
+ const { stats } = useCustomerStats();
20
28
 
21
29
  const goTo = (path: string) => navigate?.(joinPath(basePath, path));
22
30
 
@@ -85,7 +93,7 @@ export default function CustomersPage() {
85
93
  return (
86
94
  <PageLayout
87
95
  title="Customers"
88
- subtitle={`${totalCustomers} customers`}
96
+ subtitle={`${stats.total} customers`}
89
97
  icon={<Users size={24} />}
90
98
  headerActions={
91
99
  <CTAOverflowMenu
@@ -96,7 +104,11 @@ export default function CustomersPage() {
96
104
  }}
97
105
  actions={[
98
106
  { label: 'Refresh', icon: <RefreshCw size={16} />, onSelect: () => refetch() },
99
- { label: 'Export', icon: <Download size={16} />, onSelect: () => {} },
107
+ {
108
+ label: 'Export',
109
+ icon: <Download size={16} />,
110
+ onSelect: () => goTo('import-export'),
111
+ },
100
112
  ]}
101
113
  />
102
114
  }
@@ -112,19 +124,15 @@ export default function CustomersPage() {
112
124
  <p className="text-sm font-medium text-text-secondary">Customer base at a glance</p>
113
125
  <div className="mt-3 grid grid-cols-3 gap-4">
114
126
  <div>
115
- <p className="text-2xl font-bold text-text-primary tabular-nums">{totalCustomers}</p>
127
+ <p className="text-2xl font-bold text-text-primary tabular-nums">{stats.total}</p>
116
128
  <p className="text-xs text-text-secondary">Total customers</p>
117
129
  </div>
118
130
  <div>
119
- <p className="text-2xl font-bold text-text-primary tabular-nums">
120
- {customers.filter((c) => c.isActive).length}
121
- </p>
131
+ <p className="text-2xl font-bold text-text-primary tabular-nums">{stats.active}</p>
122
132
  <p className="text-xs text-text-secondary">Active</p>
123
133
  </div>
124
134
  <div>
125
- <p className="text-2xl font-bold text-text-primary tabular-nums">
126
- {customers.filter((c) => c.type === 'b2b').length}
127
- </p>
135
+ <p className="text-2xl font-bold text-text-primary tabular-nums">{stats.b2b}</p>
128
136
  <p className="text-xs text-text-secondary">Business accounts</p>
129
137
  </div>
130
138
  </div>
@@ -139,6 +147,14 @@ export default function CustomersPage() {
139
147
  searchFields={['name', 'email', 'company'] as (keyof Customer)[]}
140
148
  onRowClick={(row) => goTo(`customers/${row.id}`)}
141
149
  getRowId={(row) => row.id}
150
+ pageIndex={page}
151
+ pageSize={pageSize}
152
+ onPageChange={setPage}
153
+ onPageSizeChange={(size) => {
154
+ setPageSize(size);
155
+ setPage(0);
156
+ }}
157
+ hasNextPage={hasMore}
142
158
  emptyIllustration="empty-people"
143
159
  emptyTitle="No customers yet"
144
160
  emptyDescription="Add your first customer to start booking deliveries for them."
@@ -34,6 +34,7 @@ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
34
34
  import { formatCurrency, formatDate } from '../utils/formatters';
35
35
  import { joinPath } from '../utils/navigation';
36
36
  import { FormError } from '../components/FormError';
37
+ import { DocumentsCard } from '../components/DocumentsCard';
37
38
 
38
39
  function NewDriverForm() {
39
40
  const { basePath, navigate } = useMoveTheWheels();
@@ -310,46 +311,31 @@ export default function DriverDetailsPage() {
310
311
  </CardContent>
311
312
  </Card>
312
313
 
313
- {/* Documents */}
314
+ {/* License — real driver credential fields */}
314
315
  <Card>
315
316
  <CardHeader>
316
317
  <CardTitle className="flex items-center gap-2">
317
- <FileText size={18} /> Documents
318
+ <FileText size={18} /> License
318
319
  </CardTitle>
319
320
  </CardHeader>
320
321
  <CardContent>
321
- <div className="space-y-3">
322
- <div className="flex items-center justify-between p-3 border rounded-lg">
323
- <div>
324
- <p className="font-medium">Driver's License</p>
325
- <p className="text-sm text-text-secondary">
326
- {driver.licenseNumber} - {driver.licenseType}
327
- </p>
328
- <p className="text-sm text-text-secondary">
329
- Expires: {formatDate(driver.licenseExpiry)}
330
- </p>
331
- </div>
332
- <Badge variant="default">Valid</Badge>
322
+ <div className="flex items-center justify-between p-3 border rounded-lg">
323
+ <div>
324
+ <p className="font-medium">Driver&apos;s License</p>
325
+ <p className="text-sm text-text-secondary">
326
+ {driver.licenseNumber || '—'}
327
+ {driver.licenseType ? ` - ${driver.licenseType}` : ''}
328
+ </p>
329
+ <p className="text-sm text-text-secondary">
330
+ Expires: {formatDate(driver.licenseExpiry)}
331
+ </p>
333
332
  </div>
334
- {driver.documents.map((doc) => (
335
- <div
336
- key={doc.id}
337
- className="flex items-center justify-between p-3 border rounded-lg"
338
- >
339
- <div>
340
- <p className="font-medium capitalize">{doc.type.replace('_', ' ')}</p>
341
- <p className="text-sm text-text-secondary">
342
- Expires: {formatDate(doc.expiryDate)}
343
- </p>
344
- </div>
345
- <Badge variant={doc.verified ? 'default' : 'secondary'}>
346
- {doc.verified ? 'Verified' : 'Pending'}
347
- </Badge>
348
- </div>
349
- ))}
350
333
  </div>
351
334
  </CardContent>
352
335
  </Card>
336
+
337
+ {/* Documents — real files via wspace-files-svc */}
338
+ <DocumentsCard entityType="driver" entityId={driver.id} />
353
339
  </div>
354
340
 
355
341
  <div className="space-y-6">
@@ -2,13 +2,14 @@
2
2
  * Drivers Page - List of all drivers
3
3
  */
4
4
 
5
+ import { useState } from 'react';
5
6
  import { Users, Plus, RefreshCw, Download, Star } from 'lucide-react';
6
7
  import { PageLayout } from '../components/PageLayout';
7
8
  import { DataTable, type Column } from '../components/DataTable';
8
9
  import { StatusBadge } from '../components/StatusBadge';
9
10
  import { Avatar, AvatarFallback } from '../components/ui';
10
11
  import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
11
- import { useDrivers } from '../hooks/useFleet';
12
+ import { useDrivers, useFleetStats } from '../hooks/useFleet';
12
13
  import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
13
14
  import { formatDate } from '../utils/formatters';
14
15
  import { joinPath } from '../utils/navigation';
@@ -16,7 +17,15 @@ import type { Driver } from '../types';
16
17
 
17
18
  export default function DriversPage() {
18
19
  const { basePath, navigate } = useMoveTheWheels();
19
- const { drivers, totalDrivers, isLoading, refetch } = useDrivers();
20
+ const [page, setPage] = useState(0);
21
+ const [pageSize, setPageSize] = useState(25);
22
+ const { drivers, isLoading, refetch } = useDrivers({
23
+ limit: pageSize,
24
+ offset: page * pageSize,
25
+ });
26
+ // Workforce totals + tiles from the live aggregate, not the current page.
27
+ const { stats } = useFleetStats();
28
+ const totalDrivers = stats.totalDrivers;
20
29
 
21
30
  const goTo = (path: string) => navigate?.(joinPath(basePath, path));
22
31
 
@@ -99,7 +108,11 @@ export default function DriversPage() {
99
108
  }}
100
109
  actions={[
101
110
  { label: 'Refresh', icon: <RefreshCw size={16} />, onSelect: () => refetch() },
102
- { label: 'Export', icon: <Download size={16} />, onSelect: () => {} },
111
+ {
112
+ label: 'Export',
113
+ icon: <Download size={16} />,
114
+ onSelect: () => goTo('import-export'),
115
+ },
103
116
  ]}
104
117
  />
105
118
  }
@@ -120,17 +133,13 @@ export default function DriversPage() {
120
133
  </div>
121
134
  <div>
122
135
  <p className="text-2xl font-bold text-text-primary tabular-nums">
123
- {drivers.filter((d) => d.status === 'available' || d.status === 'busy').length}
136
+ {stats.driversByStatus.available + stats.driversByStatus.busy}
124
137
  </p>
125
138
  <p className="text-xs text-text-secondary">On duty / available</p>
126
139
  </div>
127
140
  <div>
128
141
  <p className="text-2xl font-bold text-text-primary tabular-nums">
129
- {drivers.length > 0
130
- ? (drivers.reduce((sum, d) => sum + d.ratings.overall, 0) / drivers.length).toFixed(
131
- 1
132
- )
133
- : '—'}
142
+ {stats.avgDriverRating > 0 ? stats.avgDriverRating.toFixed(1) : '—'}
134
143
  </p>
135
144
  <p className="text-xs text-text-secondary">Avg rating</p>
136
145
  </div>
@@ -146,6 +155,14 @@ export default function DriversPage() {
146
155
  searchFields={['fullName', 'employeeId'] as (keyof Driver)[]}
147
156
  onRowClick={(row) => goTo(`fleet/drivers/${row.id}`)}
148
157
  getRowId={(row) => row.id}
158
+ pageIndex={page}
159
+ pageSize={pageSize}
160
+ onPageChange={setPage}
161
+ onPageSizeChange={(size) => {
162
+ setPageSize(size);
163
+ setPage(0);
164
+ }}
165
+ totalItems={totalDrivers}
149
166
  emptyIllustration="empty-people"
150
167
  emptyTitle="No drivers yet"
151
168
  emptyDescription="Add drivers to start assigning deliveries and routes."
@@ -1,22 +1,28 @@
1
1
  /**
2
2
  * Finance Page
3
+ *
4
+ * Shows only REAL figures returned by Query.financeMetrics (revenue, fuel cost,
5
+ * open invoices) — no simulated paid/pending splits. Invoicing and settlement
6
+ * are handled by the platform Billing module (global-billing-svc), which this
7
+ * page deep-links to rather than re-implementing an Invoice model here.
3
8
  */
4
9
 
5
- import { DollarSign, TrendingUp, CreditCard, FileText, Download } from 'lucide-react';
10
+ import { DollarSign, TrendingUp, CreditCard, FileText, Fuel, ArrowRight } from 'lucide-react';
6
11
  import { PageLayout } from '../components/PageLayout';
7
12
  import { MetricCard, MetricGrid } from '../components/MetricCard';
8
13
  import { Card, CardContent, CardHeader, CardTitle, Button } from '../components/ui';
9
- import { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';
10
- import { useAnalytics } from '../hooks/useAnalytics';
11
- import { useOrderStats } from '../hooks/useOrders';
14
+ import { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
15
+ import { useAnalytics, useFinanceMetrics } from '../hooks/useAnalytics';
16
+ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
12
17
  import { formatCurrency } from '../utils/formatters';
13
18
 
14
19
  export default function FinancePage() {
15
20
  const { analytics } = useAnalytics();
16
- const { stats } = useOrderStats();
21
+ const { metrics } = useFinanceMetrics();
22
+ const { navigate } = useMoveTheWheels();
17
23
 
18
- const pendingPayments = stats.totalRevenue * 0.15; // Simulated
19
- const completedPayments = stats.totalRevenue * 0.85;
24
+ // Deep-link to the host Billing module (mounted at /billing in the app shell).
25
+ const openBilling = () => navigate('/billing');
20
26
 
21
27
  return (
22
28
  <PageLayout
@@ -24,20 +30,15 @@ export default function FinancePage() {
24
30
  subtitle="Financial overview and reports"
25
31
  icon={<DollarSign size={24} />}
26
32
  headerActions={
27
- <div className="flex gap-2">
28
- <Button variant="outline">
29
- <FileText size={16} /> Invoices
30
- </Button>
31
- <Button variant="outline">
32
- <Download size={16} /> Export
33
- </Button>
34
- </div>
33
+ <Button variant="outline" onClick={openBilling}>
34
+ <FileText size={16} /> Invoices & Billing
35
+ </Button>
35
36
  }
36
37
  >
37
38
  <PagePurpose icon={<DollarSign size={18} />}>
38
- Follow the money behind your deliveries. See revenue earned, what&apos;s been collected
39
- versus still owed, and average order value — use it to chase pending payments, forecast cash
40
- flow, and confirm which shipments have actually been paid for.
39
+ Follow the money behind your deliveries. See total revenue, how much has already been
40
+ collected on delivered orders, fuel cost and how many invoices are still open — then head to
41
+ Billing to raise and settle invoices.
41
42
  </PagePurpose>
42
43
 
43
44
  <MetricGrid columns={4}>
@@ -45,28 +46,25 @@ export default function FinancePage() {
45
46
  treatment="glass"
46
47
  glow
47
48
  label="Total Revenue"
48
- value={formatCurrency(analytics.overview.totalRevenue)}
49
+ value={formatCurrency(metrics.totalRevenue)}
49
50
  icon={<DollarSign size={20} />}
50
- change="+12.5%"
51
- changeType="positive"
52
- trend="up"
53
51
  />
54
52
  <MetricCard
55
- label="Completed"
56
- value={formatCurrency(completedPayments)}
53
+ label="Collected (Delivered)"
54
+ value={formatCurrency(metrics.deliveredRevenue)}
57
55
  icon={<TrendingUp size={20} />}
58
56
  changeType="positive"
59
57
  />
60
58
  <MetricCard
61
- label="Pending"
62
- value={formatCurrency(pendingPayments)}
63
- icon={<CreditCard size={20} />}
59
+ label="Fuel Cost"
60
+ value={formatCurrency(metrics.fuelCost)}
61
+ icon={<Fuel size={20} />}
64
62
  changeType="neutral"
65
63
  />
66
64
  <MetricCard
67
- label="Avg Order Value"
68
- value={formatCurrency(stats.avgOrderValue)}
69
- icon={<DollarSign size={20} />}
65
+ label="Open Invoices"
66
+ value={String(metrics.openInvoices)}
67
+ icon={<CreditCard size={20} />}
70
68
  />
71
69
  </MetricGrid>
72
70
 
@@ -76,70 +74,55 @@ export default function FinancePage() {
76
74
  <CardTitle>Revenue by Month</CardTitle>
77
75
  </CardHeader>
78
76
  <CardContent>
79
- <div className="space-y-4">
80
- {analytics.trends.revenue.map((r) => (
81
- <div
82
- key={r.month}
83
- className="flex items-center justify-between p-3 bg-bg-sunken/50 rounded-lg"
84
- >
85
- <span className="font-medium">{r.month} 2024</span>
86
- <span className="text-lg font-bold">{formatCurrency(r.revenue)}</span>
87
- </div>
88
- ))}
89
- </div>
77
+ {analytics.trends.revenue.length > 0 ? (
78
+ <div className="space-y-4">
79
+ {analytics.trends.revenue.map((r) => (
80
+ <div
81
+ key={r.month}
82
+ className="flex items-center justify-between p-3 bg-bg-sunken/50 rounded-lg"
83
+ >
84
+ <span className="font-medium">{r.month}</span>
85
+ <span className="text-lg font-bold">{formatCurrency(r.revenue)}</span>
86
+ </div>
87
+ ))}
88
+ </div>
89
+ ) : (
90
+ <p className="text-text-secondary text-center py-6">
91
+ No revenue recorded yet. Revenue appears here as orders are delivered.
92
+ </p>
93
+ )}
90
94
  </CardContent>
91
95
  </Card>
92
96
 
93
- {/* Payment Status — the page's single emphasis zone */}
97
+ {/* Invoices & Settlement — reuse of the Billing module */}
94
98
  <EmphasisPanel className="space-y-4">
95
- <h2 className="text-base font-semibold text-text-primary">Payment Status</h2>
96
- <div className="space-y-4">
97
- <div className="flex items-center justify-between p-4 bg-status-success-bg/10 rounded-lg">
98
- <div className="flex items-center gap-3">
99
- <div className="w-10 h-10 rounded-full bg-status-success-bg/20 flex items-center justify-center">
100
- <TrendingUp size={20} className="text-status-success-text" />
101
- </div>
102
- <div>
103
- <p className="font-medium">Paid</p>
104
- <p className="text-sm text-text-secondary">{stats.byStatus.delivered} orders</p>
105
- </div>
99
+ <h2 className="text-base font-semibold text-text-primary">Invoices & Settlement</h2>
100
+ <p className="text-sm text-text-secondary">
101
+ Invoicing, payments and settlement run through the platform Billing module — the single
102
+ source of truth for what&apos;s been billed and paid. MoveTheWheels doesn&apos;t keep its
103
+ own invoice ledger.
104
+ </p>
105
+ <div className="flex items-center justify-between p-4 bg-status-warning-bg/10 rounded-lg">
106
+ <div className="flex items-center gap-3">
107
+ <div className="w-10 h-10 rounded-full bg-status-warning-bg/20 flex items-center justify-center">
108
+ <CreditCard size={20} className="text-status-warning-text" />
106
109
  </div>
107
- <span className="text-xl font-bold text-status-success-text">
108
- {formatCurrency(completedPayments)}
109
- </span>
110
- </div>
111
- <div className="flex items-center justify-between p-4 bg-status-warning-bg/10 rounded-lg">
112
- <div className="flex items-center gap-3">
113
- <div className="w-10 h-10 rounded-full bg-status-warning-bg/20 flex items-center justify-center">
114
- <CreditCard size={20} className="text-status-warning-text" />
115
- </div>
116
- <div>
117
- <p className="font-medium">Pending</p>
118
- <p className="text-sm text-text-secondary">
119
- {stats.byStatus.pending + stats.byStatus.in_transit} orders
120
- </p>
121
- </div>
110
+ <div>
111
+ <p className="font-medium">Open invoices</p>
112
+ <p className="text-sm text-text-secondary">
113
+ {metrics.openInvoices} awaiting settlement
114
+ </p>
122
115
  </div>
123
- <span className="text-xl font-bold text-status-warning-text">
124
- {formatCurrency(pendingPayments)}
125
- </span>
126
116
  </div>
117
+ <span className="text-xl font-bold text-status-warning-text">
118
+ {metrics.openInvoices}
119
+ </span>
127
120
  </div>
121
+ <Button className="w-full" onClick={openBilling}>
122
+ Open Billing <ArrowRight size={16} />
123
+ </Button>
128
124
  </EmphasisPanel>
129
125
  </div>
130
-
131
- <Card>
132
- <CardHeader>
133
- <CardTitle>Recent Transactions</CardTitle>
134
- </CardHeader>
135
- <CardContent>
136
- <IllustratedEmptyState
137
- illustration="empty-data"
138
- title="No transactions yet"
139
- description="Once payments are processed, your transaction history will appear here. Connect a payment gateway to start recording transactions."
140
- />
141
- </CardContent>
142
- </Card>
143
126
  </PageLayout>
144
127
  );
145
128
  }
@@ -42,7 +42,7 @@ export default function InventoryPage() {
42
42
  primary={{
43
43
  label: t('movethewheels.inventory.actions.addProduct', 'Add Product'),
44
44
  icon: <Plus size={16} />,
45
- onSelect: () => {},
45
+ onSelect: () => goTo('inventory/products/new'),
46
46
  }}
47
47
  actions={[
48
48
  {