@burdenoff/microfe-movethewheels 2026.624.1 → 2026.626.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.
- package/dist/{AIAssistantPage-BrHX2lQn.js → AIAssistantPage-AvsMbunM.js} +39 -33
- package/dist/{AnalyticsPage-DI6Wcaw5.js → AnalyticsPage-CCLfHgQZ.js} +44 -33
- package/dist/CreateOrderPage-CjlnEtk3.js +510 -0
- package/dist/CustomerDetailsPage-I6cdDHUi.js +265 -0
- package/dist/CustomersPage-CGGfD4ki.js +163 -0
- package/dist/{DashboardPage-3ZOnwg1a.js → DashboardPage-D2HRqwg9.js} +122 -111
- package/dist/DataTable-CD9ZhpLB.js +249 -0
- package/dist/DriverDetailsPage-rg_pGNuP.js +330 -0
- package/dist/DriversPage-DKiynISH.js +167 -0
- package/dist/FinancePage-Vg8WPs38.js +158 -0
- package/dist/{FleetPage-CPS7r4J3.js → FleetPage-DddXt8r8.js} +124 -85
- package/dist/ImportExportPage-BW6qO3kc.js +248 -0
- package/dist/InventoryPage-rMGuaJN-.js +222 -0
- package/dist/LiveTrackingPage-DQ1q4Id3.js +375 -0
- package/dist/MarketplacePage-DKrXAv62.js +219 -0
- package/dist/MetricCard-B7yMyvMX.js +146 -0
- package/dist/OrderDetailsPage-CFASgsYX.js +431 -0
- package/dist/OrdersListPage-BHeT8S5o.js +309 -0
- package/dist/{PageLayout-xDTiBTDS.js → PageLayout-D3N8MCTm.js} +213 -195
- package/dist/ProductDetailsPage-CFGcbaHo.js +195 -0
- package/dist/ProductsPage--xEpbQ8A.js +171 -0
- package/dist/{ReportsPage-B8OyWGzl.js → ReportsPage-Bb7_90rK.js} +57 -49
- package/dist/RouteDetailsPage-CVtDVb1N.js +282 -0
- package/dist/RoutesPage-s57PtgcQ.js +163 -0
- package/dist/SettingsPage-BYNCBOpN.js +298 -0
- package/dist/{StatusBadge-C--7NhoW.js → StatusBadge-CijYQXtb.js} +5 -2
- package/dist/{TrackingPage-BldUm_g7.js → TrackingPage-CcRc_dva.js} +88 -83
- package/dist/VehicleDetailsPage-3YDMPsPs.js +220 -0
- package/dist/VehiclesPage-UlxPWP9K.js +167 -0
- package/dist/WarehouseDetailsPage-Db7KeN-d.js +238 -0
- package/dist/WarehousesPage-CuTO-OPp.js +164 -0
- package/dist/{arrow-left-Bk5GsGNk.js → arrow-left-CBpZKkpq.js} +1 -1
- package/dist/{box-BAMv2-7M.js → box-CAaiElsb.js} +1 -1
- package/dist/{chart-column-CAfkf4o6.js → chart-column-D1Ym15-S.js} +1 -1
- package/dist/{chevron-right-Czrt_Qew.js → chevron-right-BIFGi0UA.js} +1 -1
- package/dist/{circle-alert-CEuV9Pij.js → circle-alert-DjmNo5_L.js} +1 -1
- package/dist/{circle-check-big-Cx8aO6B-.js → circle-check-big-CjuPVo3_.js} +1 -1
- package/dist/{clock-Be95bq7_.js → clock-BjkdnddD.js} +1 -1
- package/dist/{dollar-sign-kJqItPli.js → dollar-sign-IB4O0wuQ.js} +1 -1
- package/dist/{download-caab0D3V.js → download-Dpa9vqoG.js} +1 -1
- package/dist/{file-text-b1qhrJn1.js → file-text-C8Uioy11.js} +1 -1
- package/dist/{filter-BWmLTZB-.js → filter-Da5OZaHI.js} +1 -1
- package/dist/index.js +16 -16
- package/dist/{map-CS2aFgyO.js → map-_Per4sT9.js} +1 -1
- package/dist/{map-pin-96FeqL7H.js → map-pin-BSaf5IDL.js} +1 -1
- package/dist/{navigation-i6UXEjTC.js → navigation-DaTbrhw2.js} +1 -1
- package/dist/{package-KhSi3xaT.js → package-BE7U0wQE.js} +1 -1
- package/dist/{phone-B-h3tbsr.js → phone-DV4StZiA.js} +1 -1
- package/dist/{plus-D2BYAzMn.js → plus-Dl1rLeJR.js} +1 -1
- package/dist/{refresh-cw-D5Sy01s-.js → refresh-cw-CWfERAit.js} +1 -1
- package/dist/{route-CBDXuwH9.js → route-Dp9JiN_E.js} +1 -1
- package/dist/{save-CMc7h-Vi.js → save-XH8vydOn.js} +1 -1
- package/dist/{search-DVrUQbOg.js → search-OxoIiL_C.js} +1 -1
- package/dist/{settings-BwzIRYv3.js → settings-3Rw5NwFF.js} +1 -1
- package/dist/{square-pen-DmW36Bau.js → square-pen-hLIbFQFV.js} +1 -1
- package/dist/{star-Cv2MzxZL.js → star-YsVV3MNQ.js} +1 -1
- package/dist/{store-CLH957vl.js → store-RCBl7HB-.js} +1 -1
- package/dist/{trending-up-l8M0BO_w.js → trending-up-C-85AwGT.js} +1 -1
- package/dist/{triangle-alert-BI45s6bH.js → triangle-alert-BOhfGgmh.js} +1 -1
- package/dist/{truck-DX3lNQkz.js → truck-CtDjNFbi.js} +1 -1
- package/dist/{user-D8EY1uvt.js → user-Dz5TJZFs.js} +1 -1
- package/dist/{users-CfG0J8wP.js → users-g2N0eL-Z.js} +1 -1
- package/dist/{warehouse-Dt_bAr82.js → warehouse-D3WWDeTX.js} +1 -1
- package/dist/{wrench-C50eI_Iy.js → wrench-CmxY1n4I.js} +1 -1
- package/package.json +2 -2
- package/src/movethewheels/components/DataTable.tsx +41 -6
- package/src/movethewheels/components/MetricCard.tsx +51 -20
- package/src/movethewheels/components/PageLayout.tsx +60 -5
- package/src/movethewheels/components/StatusBadge.tsx +2 -1
- package/src/movethewheels/pages/AIAssistantPage.tsx +11 -3
- package/src/movethewheels/pages/AnalyticsPage.tsx +31 -25
- package/src/movethewheels/pages/CreateOrderPage.tsx +56 -17
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +63 -38
- package/src/movethewheels/pages/CustomersPage.tsx +51 -12
- package/src/movethewheels/pages/DashboardPage.tsx +62 -48
- package/src/movethewheels/pages/DriverDetailsPage.tsx +57 -26
- package/src/movethewheels/pages/DriversPage.tsx +45 -12
- package/src/movethewheels/pages/FinancePage.tsx +45 -41
- package/src/movethewheels/pages/FleetPage.tsx +82 -34
- package/src/movethewheels/pages/ImportExportPage.tsx +46 -29
- package/src/movethewheels/pages/InventoryPage.tsx +37 -33
- package/src/movethewheels/pages/LiveTrackingPage.tsx +58 -20
- package/src/movethewheels/pages/MarketplacePage.tsx +37 -3
- package/src/movethewheels/pages/OrderDetailsPage.tsx +74 -46
- package/src/movethewheels/pages/OrdersListPage.tsx +81 -14
- package/src/movethewheels/pages/ProductDetailsPage.tsx +36 -11
- package/src/movethewheels/pages/ProductsPage.tsx +41 -12
- package/src/movethewheels/pages/ReportsPage.tsx +23 -19
- package/src/movethewheels/pages/RouteDetailsPage.tsx +71 -31
- package/src/movethewheels/pages/RoutesPage.tsx +48 -9
- package/src/movethewheels/pages/SettingsPage.tsx +68 -26
- package/src/movethewheels/pages/TrackingPage.tsx +52 -49
- package/src/movethewheels/pages/VehicleDetailsPage.tsx +49 -24
- package/src/movethewheels/pages/VehiclesPage.tsx +41 -15
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +55 -30
- package/src/movethewheels/pages/WarehousesPage.tsx +46 -9
- package/dist/CreateOrderPage-DfkSi6JF.js +0 -471
- package/dist/CustomerDetailsPage-DzAIu_tH.js +0 -239
- package/dist/CustomersPage-d21ixOYa.js +0 -119
- package/dist/DataTable-CZGz3SJu.js +0 -239
- package/dist/DriverDetailsPage-YuZSlPSV.js +0 -297
- package/dist/DriversPage-BOhqEBTv.js +0 -127
- package/dist/FinancePage-CnjUNXOb.js +0 -154
- package/dist/ImportExportPage-zNjs7xsV.js +0 -232
- package/dist/InventoryPage-C9g5d3SW.js +0 -223
- package/dist/LiveTrackingPage-DoBvtdBG.js +0 -332
- package/dist/MarketplacePage-BEj5hcBF.js +0 -192
- package/dist/MetricCard-DeHVeVwl.js +0 -135
- package/dist/OrderDetailsPage-D-9lDZYU.js +0 -398
- package/dist/OrdersListPage-CJzHbgY3.js +0 -257
- package/dist/ProductDetailsPage-3rKtzXpy.js +0 -168
- package/dist/ProductsPage-B0P_wjBJ.js +0 -131
- package/dist/RouteDetailsPage-DGjKTUMj.js +0 -240
- package/dist/RoutesPage-CpopY8xN.js +0 -116
- package/dist/SettingsPage-BvXSa3gi.js +0 -247
- package/dist/VehicleDetailsPage-Dqv--MVW.js +0 -194
- package/dist/VehiclesPage-CCBHRDHu.js +0 -127
- package/dist/WarehouseDetailsPage-ZObC_c7W.js +0 -215
- package/dist/WarehousesPage-CpgZEVW9.js +0 -121
|
@@ -5,15 +5,8 @@
|
|
|
5
5
|
import { Package, Warehouse, AlertTriangle, ChevronRight, Plus } from 'lucide-react';
|
|
6
6
|
import { PageLayout, PageSection } from '../components/PageLayout';
|
|
7
7
|
import { MetricCard, MetricGrid } from '../components/MetricCard';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
CardContent,
|
|
11
|
-
CardHeader,
|
|
12
|
-
CardTitle,
|
|
13
|
-
Button,
|
|
14
|
-
Badge,
|
|
15
|
-
Progress,
|
|
16
|
-
} from '../components/ui';
|
|
8
|
+
import { Card, CardContent, Button, Badge, Progress } from '../components/ui';
|
|
9
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
17
10
|
import { useInventory, useInventoryStats } from '../hooks/useInventory';
|
|
18
11
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
19
12
|
import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';
|
|
@@ -39,19 +32,32 @@ export default function InventoryPage() {
|
|
|
39
32
|
subtitle={t('movethewheels.inventory.subtitle', 'Manage your products and warehouses')}
|
|
40
33
|
icon={<Package size={24} />}
|
|
41
34
|
headerActions={
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
35
|
+
<CTAOverflowMenu
|
|
36
|
+
primary={{
|
|
37
|
+
label: t('movethewheels.inventory.actions.addProduct', 'Add Product'),
|
|
38
|
+
icon: <Plus size={16} />,
|
|
39
|
+
onSelect: () => {},
|
|
40
|
+
}}
|
|
41
|
+
actions={[
|
|
42
|
+
{
|
|
43
|
+
label: t('movethewheels.inventory.actions.products', 'Products'),
|
|
44
|
+
onSelect: () => goTo('inventory/products'),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: t('movethewheels.inventory.actions.warehouses', 'Warehouses'),
|
|
48
|
+
onSelect: () => goTo('inventory/warehouses'),
|
|
49
|
+
},
|
|
50
|
+
]}
|
|
51
|
+
/>
|
|
53
52
|
}
|
|
54
53
|
>
|
|
54
|
+
<PagePurpose icon={<Package size={18} />}>
|
|
55
|
+
{t(
|
|
56
|
+
'movethewheels.inventory.purpose',
|
|
57
|
+
'A real-time view of what you have and where. Track stock value and quantities across warehouses, catch low-stock items before they run out, and drill into products or warehouses to manage what you can promise on an order.'
|
|
58
|
+
)}
|
|
59
|
+
</PagePurpose>
|
|
60
|
+
|
|
55
61
|
<MetricGrid columns={4}>
|
|
56
62
|
<MetricCard
|
|
57
63
|
label={t('movethewheels.inventory.metrics.totalProducts', 'Total Products')}
|
|
@@ -78,22 +84,20 @@ export default function InventoryPage() {
|
|
|
78
84
|
</MetricGrid>
|
|
79
85
|
|
|
80
86
|
{lowStockItems.length > 0 && (
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
</CardHeader>
|
|
89
|
-
<CardContent>
|
|
87
|
+
<EmphasisPanel className="space-y-4">
|
|
88
|
+
<h2 className="flex items-center gap-2 text-base font-semibold text-status-warning-text">
|
|
89
|
+
<AlertTriangle size={18} />
|
|
90
|
+
{t('movethewheels.inventory.lowStock.title', 'Low Stock Alert')} ({lowStockItems.length}{' '}
|
|
91
|
+
{t('movethewheels.inventory.lowStock.items', 'items')})
|
|
92
|
+
</h2>
|
|
93
|
+
<div>
|
|
90
94
|
<div className="space-y-3">
|
|
91
95
|
{lowStockItems.slice(0, 5).map((item) => {
|
|
92
96
|
const product = products.find((p) => p.id === item.productId);
|
|
93
97
|
return (
|
|
94
98
|
<div
|
|
95
99
|
key={item.id}
|
|
96
|
-
className="flex items-center justify-between p-3 bg-
|
|
100
|
+
className="flex items-center justify-between p-3 bg-status-warning-bg/10 rounded-lg"
|
|
97
101
|
>
|
|
98
102
|
<div>
|
|
99
103
|
<p className="font-medium">
|
|
@@ -105,7 +109,7 @@ export default function InventoryPage() {
|
|
|
105
109
|
</p>
|
|
106
110
|
</div>
|
|
107
111
|
<div className="text-right">
|
|
108
|
-
<p className="font-medium text-
|
|
112
|
+
<p className="font-medium text-status-warning-text">
|
|
109
113
|
{item.availableQuantity}{' '}
|
|
110
114
|
{t('movethewheels.inventory.lowStock.left', 'left')}
|
|
111
115
|
</p>
|
|
@@ -118,8 +122,8 @@ export default function InventoryPage() {
|
|
|
118
122
|
);
|
|
119
123
|
})}
|
|
120
124
|
</div>
|
|
121
|
-
</
|
|
122
|
-
</
|
|
125
|
+
</div>
|
|
126
|
+
</EmphasisPanel>
|
|
123
127
|
)}
|
|
124
128
|
|
|
125
129
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { useState } from 'react';
|
|
8
8
|
import { MapPin, Truck, Package, RefreshCw, List, Map } from 'lucide-react';
|
|
9
|
-
import { PageLayout } from '../components/PageLayout';
|
|
9
|
+
import { PageLayout, EmptyState } from '../components/PageLayout';
|
|
10
10
|
import { StatusBadge } from '../components/StatusBadge';
|
|
11
11
|
import { MetricCard, MetricGrid } from '../components/MetricCard';
|
|
12
12
|
import {
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
TabsTrigger,
|
|
22
22
|
ScrollArea,
|
|
23
23
|
} from '../components/ui';
|
|
24
|
+
import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
24
25
|
import { useLiveTracking, useTrackingAlerts } from '../hooks/useTracking';
|
|
25
26
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
26
27
|
import { formatDate } from '../utils/formatters';
|
|
@@ -67,6 +68,40 @@ export default function LiveTrackingPage() {
|
|
|
67
68
|
</div>
|
|
68
69
|
}
|
|
69
70
|
>
|
|
71
|
+
<PagePurpose icon={<MapPin size={18} />}>
|
|
72
|
+
A real-time operations board for every delivery currently on the road. Watch where drivers
|
|
73
|
+
are, which orders are out for delivery, and react to alerts — switch to the map for a
|
|
74
|
+
bird's-eye view or the list to drill into a single shipment.
|
|
75
|
+
</PagePurpose>
|
|
76
|
+
|
|
77
|
+
<NextSteps
|
|
78
|
+
storageKey="movethewheels-live-tracking"
|
|
79
|
+
steps={[
|
|
80
|
+
{
|
|
81
|
+
id: 'select',
|
|
82
|
+
label: 'Open a delivery',
|
|
83
|
+
description: 'Switch to the list and pick a shipment to see full details.',
|
|
84
|
+
icon: <List size={16} />,
|
|
85
|
+
onClick: () => setViewMode('list'),
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'alerts',
|
|
89
|
+
label: 'Clear outstanding alerts',
|
|
90
|
+
description: 'Review delays and exceptions that need attention.',
|
|
91
|
+
icon: <Truck size={16} />,
|
|
92
|
+
done: unacknowledgedAlerts.length === 0,
|
|
93
|
+
onClick: () => setViewMode('list'),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'new-order',
|
|
97
|
+
label: 'Create another order',
|
|
98
|
+
description: 'Add a new delivery to the pipeline.',
|
|
99
|
+
icon: <Package size={16} />,
|
|
100
|
+
onClick: () => goTo('orders/new'),
|
|
101
|
+
},
|
|
102
|
+
]}
|
|
103
|
+
/>
|
|
104
|
+
|
|
70
105
|
{/* Quick Stats */}
|
|
71
106
|
<MetricGrid columns={4}>
|
|
72
107
|
<MetricCard
|
|
@@ -171,9 +206,11 @@ export default function LiveTrackingPage() {
|
|
|
171
206
|
);
|
|
172
207
|
})}
|
|
173
208
|
{activeDeliveries.length === 0 && (
|
|
174
|
-
<
|
|
175
|
-
|
|
176
|
-
|
|
209
|
+
<EmptyState
|
|
210
|
+
illustration="empty-sessions"
|
|
211
|
+
title="No active deliveries"
|
|
212
|
+
description="When orders are dispatched they'll appear here in real time."
|
|
213
|
+
/>
|
|
177
214
|
)}
|
|
178
215
|
</div>
|
|
179
216
|
</ScrollArea>
|
|
@@ -184,18 +221,16 @@ export default function LiveTrackingPage() {
|
|
|
184
221
|
|
|
185
222
|
{/* Sidebar */}
|
|
186
223
|
<div className="space-y-6">
|
|
187
|
-
{/* Selected Delivery Details */}
|
|
224
|
+
{/* Selected Delivery Details — the page's single emphasis zone when a delivery is open */}
|
|
188
225
|
{selected ? (
|
|
189
|
-
<
|
|
190
|
-
<
|
|
191
|
-
<
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
</CardHeader>
|
|
198
|
-
<CardContent className="space-y-4">
|
|
226
|
+
<EmphasisPanel className="space-y-4">
|
|
227
|
+
<div className="flex items-center justify-between">
|
|
228
|
+
<h2 className="text-base font-semibold text-text-primary">Delivery Details</h2>
|
|
229
|
+
<Button variant="ghost" size="sm" onClick={() => setSelectedDelivery(null)}>
|
|
230
|
+
Clear
|
|
231
|
+
</Button>
|
|
232
|
+
</div>
|
|
233
|
+
<div className="space-y-4">
|
|
199
234
|
<div>
|
|
200
235
|
<p className="text-sm text-text-secondary">Order</p>
|
|
201
236
|
<p className="font-medium">{selected.order?.orderNumber ?? '-'}</p>
|
|
@@ -250,13 +285,16 @@ export default function LiveTrackingPage() {
|
|
|
250
285
|
Track Details
|
|
251
286
|
</Button>
|
|
252
287
|
</div>
|
|
253
|
-
</
|
|
254
|
-
</
|
|
288
|
+
</div>
|
|
289
|
+
</EmphasisPanel>
|
|
255
290
|
) : (
|
|
256
291
|
<Card>
|
|
257
|
-
<CardContent className="p-
|
|
258
|
-
<
|
|
259
|
-
|
|
292
|
+
<CardContent className="p-6">
|
|
293
|
+
<EmptyState
|
|
294
|
+
illustration="empty-generic"
|
|
295
|
+
title="No delivery selected"
|
|
296
|
+
description="Pick a delivery from the list to see its driver, vehicle, ETA and live location."
|
|
297
|
+
/>
|
|
260
298
|
</CardContent>
|
|
261
299
|
</Card>
|
|
262
300
|
)}
|
|
@@ -6,6 +6,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
6
6
|
import { Store, Star, MapPin, Clock, Filter, Search } from 'lucide-react';
|
|
7
7
|
import { PageLayout } from '../components/PageLayout';
|
|
8
8
|
import { Card, CardContent, CardHeader, CardTitle, Button, Badge, Input } from '../components/ui';
|
|
9
|
+
import { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
9
10
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
10
11
|
import { formatCurrency } from '../utils/formatters';
|
|
11
12
|
import { executeGraphQL } from '../utils/graphqlClient';
|
|
@@ -146,6 +147,12 @@ export default function MarketplacePage() {
|
|
|
146
147
|
</Button>
|
|
147
148
|
}
|
|
148
149
|
>
|
|
150
|
+
<PagePurpose icon={<Store size={18} />}>
|
|
151
|
+
Don't own every leg of the journey? Find vetted third-party logistics providers and
|
|
152
|
+
service offerings here — compare ratings, verification status and pricing, then engage a
|
|
153
|
+
partner to cover routes, warehousing or specialised handling you can't do in-house.
|
|
154
|
+
</PagePurpose>
|
|
155
|
+
|
|
149
156
|
<Card>
|
|
150
157
|
<CardContent className="p-4">
|
|
151
158
|
<div className="relative">
|
|
@@ -166,6 +173,21 @@ export default function MarketplacePage() {
|
|
|
166
173
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
167
174
|
<div className="lg:col-span-2 space-y-4">
|
|
168
175
|
<h3 className="font-semibold">Service Providers ({filteredProviders.length})</h3>
|
|
176
|
+
{filteredProviders.length === 0 && (
|
|
177
|
+
<Card>
|
|
178
|
+
<CardContent className="p-0">
|
|
179
|
+
<IllustratedEmptyState
|
|
180
|
+
illustration={search.trim() ? 'empty-search' : 'empty-people'}
|
|
181
|
+
title={search.trim() ? 'No providers match your search' : 'No providers yet'}
|
|
182
|
+
description={
|
|
183
|
+
search.trim()
|
|
184
|
+
? 'Try a different search term to find logistics partners.'
|
|
185
|
+
: 'Verified logistics service providers will appear here as they join the marketplace.'
|
|
186
|
+
}
|
|
187
|
+
/>
|
|
188
|
+
</CardContent>
|
|
189
|
+
</Card>
|
|
190
|
+
)}
|
|
169
191
|
{filteredProviders.map((provider) => (
|
|
170
192
|
<Card key={provider.id} className="hover:shadow-md transition-shadow cursor-pointer">
|
|
171
193
|
<CardContent className="p-6">
|
|
@@ -204,8 +226,20 @@ export default function MarketplacePage() {
|
|
|
204
226
|
))}
|
|
205
227
|
</div>
|
|
206
228
|
|
|
207
|
-
|
|
208
|
-
|
|
229
|
+
{/* Popular Services — the page's single emphasis zone */}
|
|
230
|
+
<EmphasisPanel className="space-y-4">
|
|
231
|
+
<h3 className="text-base font-semibold text-text-primary">Popular Services</h3>
|
|
232
|
+
{filteredOfferings.length === 0 && (
|
|
233
|
+
<IllustratedEmptyState
|
|
234
|
+
illustration={search.trim() ? 'empty-search' : 'empty-generic'}
|
|
235
|
+
title={search.trim() ? 'No matching services' : 'No services listed yet'}
|
|
236
|
+
description={
|
|
237
|
+
search.trim()
|
|
238
|
+
? 'Adjust your search to discover service offerings.'
|
|
239
|
+
: 'Service offerings from providers will be listed here.'
|
|
240
|
+
}
|
|
241
|
+
/>
|
|
242
|
+
)}
|
|
209
243
|
{filteredOfferings.map((service) => (
|
|
210
244
|
<Card key={service.id}>
|
|
211
245
|
<CardHeader>
|
|
@@ -228,7 +262,7 @@ export default function MarketplacePage() {
|
|
|
228
262
|
</CardContent>
|
|
229
263
|
</Card>
|
|
230
264
|
))}
|
|
231
|
-
</
|
|
265
|
+
</EmphasisPanel>
|
|
232
266
|
</div>
|
|
233
267
|
</PageLayout>
|
|
234
268
|
);
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
TableHeader,
|
|
43
43
|
TableRow,
|
|
44
44
|
} from '../components/ui';
|
|
45
|
+
import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
45
46
|
import { useOrder } from '../hooks/useOrders';
|
|
46
47
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
47
48
|
import { formatCurrency, formatDate, formatAddress } from '../utils/formatters';
|
|
@@ -108,6 +109,39 @@ export default function OrderDetailsPage() {
|
|
|
108
109
|
</div>
|
|
109
110
|
}
|
|
110
111
|
>
|
|
112
|
+
<PagePurpose icon={<Package size={18} />}>
|
|
113
|
+
Everything about this one delivery in a single place — who it's for, where it's
|
|
114
|
+
going, what's inside, the running cost and the full status history. Use this page to
|
|
115
|
+
review the order, hand it to a driver, print the docket, or jump into live tracking.
|
|
116
|
+
</PagePurpose>
|
|
117
|
+
|
|
118
|
+
<NextSteps
|
|
119
|
+
storageKey="movethewheels-order-details"
|
|
120
|
+
steps={[
|
|
121
|
+
{
|
|
122
|
+
id: 'track',
|
|
123
|
+
label: 'Track this order live',
|
|
124
|
+
description: 'See where the shipment is right now on the map.',
|
|
125
|
+
icon: <MapPin size={16} />,
|
|
126
|
+
onClick: () => goTo(`tracking?order=${order.id}`),
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'edit',
|
|
130
|
+
label: 'Edit order details',
|
|
131
|
+
description: 'Update items, addresses or instructions before dispatch.',
|
|
132
|
+
icon: <Edit size={16} />,
|
|
133
|
+
onClick: () => goTo(`orders/${orderId}/edit`),
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: 'print',
|
|
137
|
+
label: 'Print the delivery docket',
|
|
138
|
+
description: 'Generate a printable copy for the driver or warehouse.',
|
|
139
|
+
icon: <Printer size={16} />,
|
|
140
|
+
onClick: () => window.print(),
|
|
141
|
+
},
|
|
142
|
+
]}
|
|
143
|
+
/>
|
|
144
|
+
|
|
111
145
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
112
146
|
{/* Main Content */}
|
|
113
147
|
<div className="lg:col-span-2 space-y-6">
|
|
@@ -124,9 +158,7 @@ export default function OrderDetailsPage() {
|
|
|
124
158
|
<div>
|
|
125
159
|
<h4 className="font-medium mb-2">{order.customerInfo.name}</h4>
|
|
126
160
|
{order.customerInfo.company && (
|
|
127
|
-
<p className="text-sm text-text-secondary mb-2">
|
|
128
|
-
{order.customerInfo.company}
|
|
129
|
-
</p>
|
|
161
|
+
<p className="text-sm text-text-secondary mb-2">{order.customerInfo.company}</p>
|
|
130
162
|
)}
|
|
131
163
|
<div className="space-y-1 text-sm">
|
|
132
164
|
<p className="flex items-center gap-2">
|
|
@@ -285,52 +317,48 @@ export default function OrderDetailsPage() {
|
|
|
285
317
|
|
|
286
318
|
{/* Sidebar */}
|
|
287
319
|
<div className="space-y-6">
|
|
288
|
-
{/* Order Summary */}
|
|
289
|
-
<
|
|
290
|
-
<
|
|
291
|
-
<
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
</
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
</
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
<span>{formatCurrency(order.pricing.fees.handling)}</span>
|
|
309
|
-
</div>
|
|
310
|
-
{order.pricing.fees.insurance && (
|
|
311
|
-
<div className="flex justify-between text-sm">
|
|
312
|
-
<span className="text-text-secondary">Insurance</span>
|
|
313
|
-
<span>{formatCurrency(order.pricing.fees.insurance)}</span>
|
|
314
|
-
</div>
|
|
315
|
-
)}
|
|
320
|
+
{/* Order Summary — the page's single emphasis zone */}
|
|
321
|
+
<EmphasisPanel>
|
|
322
|
+
<h2 className="flex items-center gap-2 text-base font-semibold text-text-primary mb-4">
|
|
323
|
+
<DollarSign size={18} />
|
|
324
|
+
Order Summary
|
|
325
|
+
</h2>
|
|
326
|
+
<div className="space-y-3">
|
|
327
|
+
<div className="flex justify-between text-sm">
|
|
328
|
+
<span className="text-text-secondary">Subtotal</span>
|
|
329
|
+
<span>{formatCurrency(order.pricing.subtotal)}</span>
|
|
330
|
+
</div>
|
|
331
|
+
<div className="flex justify-between text-sm">
|
|
332
|
+
<span className="text-text-secondary">Delivery</span>
|
|
333
|
+
<span>{formatCurrency(order.pricing.fees.delivery)}</span>
|
|
334
|
+
</div>
|
|
335
|
+
<div className="flex justify-between text-sm">
|
|
336
|
+
<span className="text-text-secondary">Handling</span>
|
|
337
|
+
<span>{formatCurrency(order.pricing.fees.handling)}</span>
|
|
338
|
+
</div>
|
|
339
|
+
{order.pricing.fees.insurance && (
|
|
316
340
|
<div className="flex justify-between text-sm">
|
|
317
|
-
<span className="text-text-secondary">
|
|
318
|
-
<span>{formatCurrency(order.pricing.
|
|
319
|
-
</div>
|
|
320
|
-
<Separator />
|
|
321
|
-
<div className="flex justify-between font-medium">
|
|
322
|
-
<span>Total</span>
|
|
323
|
-
<span className="text-lg">{formatCurrency(order.pricing.total)}</span>
|
|
324
|
-
</div>
|
|
325
|
-
<div className="pt-2">
|
|
326
|
-
<p className="text-sm text-text-secondary">Payment Method</p>
|
|
327
|
-
<p className="text-sm capitalize">
|
|
328
|
-
{order.pricing.paymentMethod.replace('_', ' ')}
|
|
329
|
-
</p>
|
|
341
|
+
<span className="text-text-secondary">Insurance</span>
|
|
342
|
+
<span>{formatCurrency(order.pricing.fees.insurance)}</span>
|
|
330
343
|
</div>
|
|
344
|
+
)}
|
|
345
|
+
<div className="flex justify-between text-sm">
|
|
346
|
+
<span className="text-text-secondary">Taxes</span>
|
|
347
|
+
<span>{formatCurrency(order.pricing.taxes)}</span>
|
|
331
348
|
</div>
|
|
332
|
-
|
|
333
|
-
|
|
349
|
+
<Separator />
|
|
350
|
+
<div className="flex justify-between font-medium">
|
|
351
|
+
<span>Total</span>
|
|
352
|
+
<span className="text-lg">{formatCurrency(order.pricing.total)}</span>
|
|
353
|
+
</div>
|
|
354
|
+
<div className="pt-2">
|
|
355
|
+
<p className="text-sm text-text-secondary">Payment Method</p>
|
|
356
|
+
<p className="text-sm capitalize">
|
|
357
|
+
{order.pricing.paymentMethod.replace('_', ' ')}
|
|
358
|
+
</p>
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
</EmphasisPanel>
|
|
334
362
|
|
|
335
363
|
{/* Assignment Info */}
|
|
336
364
|
<Card>
|
|
@@ -9,6 +9,7 @@ import { Package, Plus, Filter, Download, RefreshCw } from 'lucide-react';
|
|
|
9
9
|
import { PageLayout } from '../components/PageLayout';
|
|
10
10
|
import { DataTable, type Column } from '../components/DataTable';
|
|
11
11
|
import { StatusBadge, PriorityBadge, TypeBadge } from '../components/StatusBadge';
|
|
12
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
12
13
|
import {
|
|
13
14
|
Button,
|
|
14
15
|
Card,
|
|
@@ -152,22 +153,76 @@ export default function OrdersListPage() {
|
|
|
152
153
|
subtitle={`${totalOrders} ${t('movethewheels.orders.subtitle', 'total orders')}`}
|
|
153
154
|
icon={<Package size={24} />}
|
|
154
155
|
headerActions={
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
{
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
{
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
156
|
+
<CTAOverflowMenu
|
|
157
|
+
primary={{
|
|
158
|
+
label: t('movethewheels.orders.actions.newOrder', 'New Order'),
|
|
159
|
+
icon: <Plus size={16} />,
|
|
160
|
+
onSelect: () => goTo('orders/new'),
|
|
161
|
+
}}
|
|
162
|
+
actions={[
|
|
163
|
+
{
|
|
164
|
+
label: t('movethewheels.orders.actions.refresh', 'Refresh'),
|
|
165
|
+
icon: <RefreshCw size={16} />,
|
|
166
|
+
onSelect: () => refetch(),
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
label: t('movethewheels.orders.actions.export', 'Export'),
|
|
170
|
+
icon: <Download size={16} />,
|
|
171
|
+
onSelect: () => {},
|
|
172
|
+
},
|
|
173
|
+
]}
|
|
174
|
+
/>
|
|
169
175
|
}
|
|
170
176
|
>
|
|
177
|
+
<PagePurpose icon={<Package size={18} />}>
|
|
178
|
+
{t(
|
|
179
|
+
'movethewheels.orders.purpose',
|
|
180
|
+
'Every delivery job lives here — B2B and B2C orders, their status, priority and value. Use this list to find an order, filter by status or type, and open one to assign a driver, track it, or update fulfilment.'
|
|
181
|
+
)}
|
|
182
|
+
</PagePurpose>
|
|
183
|
+
|
|
184
|
+
{/* Pipeline at a glance — the page's single emphasis zone */}
|
|
185
|
+
<EmphasisPanel>
|
|
186
|
+
<p className="text-sm font-medium text-text-secondary">
|
|
187
|
+
{t('movethewheels.orders.glance.title', 'Pipeline at a glance')}
|
|
188
|
+
</p>
|
|
189
|
+
<div className="mt-3 grid grid-cols-2 sm:grid-cols-4 gap-4">
|
|
190
|
+
<div>
|
|
191
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">{totalOrders}</p>
|
|
192
|
+
<p className="text-xs text-text-secondary">
|
|
193
|
+
{t('movethewheels.orders.glance.total', 'Total orders')}
|
|
194
|
+
</p>
|
|
195
|
+
</div>
|
|
196
|
+
<div>
|
|
197
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">
|
|
198
|
+
{orders.filter((o) => o.status === 'pending').length}
|
|
199
|
+
</p>
|
|
200
|
+
<p className="text-xs text-text-secondary">
|
|
201
|
+
{t('movethewheels.orders.glance.pending', 'Pending')}
|
|
202
|
+
</p>
|
|
203
|
+
</div>
|
|
204
|
+
<div>
|
|
205
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">
|
|
206
|
+
{
|
|
207
|
+
orders.filter((o) => o.status === 'in_transit' || o.status === 'out_for_delivery')
|
|
208
|
+
.length
|
|
209
|
+
}
|
|
210
|
+
</p>
|
|
211
|
+
<p className="text-xs text-text-secondary">
|
|
212
|
+
{t('movethewheels.orders.glance.inTransit', 'In transit')}
|
|
213
|
+
</p>
|
|
214
|
+
</div>
|
|
215
|
+
<div>
|
|
216
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">
|
|
217
|
+
{orders.filter((o) => o.status === 'delivered').length}
|
|
218
|
+
</p>
|
|
219
|
+
<p className="text-xs text-text-secondary">
|
|
220
|
+
{t('movethewheels.orders.glance.delivered', 'Delivered')}
|
|
221
|
+
</p>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
</EmphasisPanel>
|
|
225
|
+
|
|
171
226
|
{/* Filters */}
|
|
172
227
|
<Card>
|
|
173
228
|
<CardContent className="p-4">
|
|
@@ -235,6 +290,18 @@ export default function OrdersListPage() {
|
|
|
235
290
|
onRowClick={(row) => goTo(`orders/${row.id}`)}
|
|
236
291
|
getRowId={(row) => row.id}
|
|
237
292
|
emptyMessage={t('movethewheels.orders.empty', 'No orders found')}
|
|
293
|
+
emptyIllustration="empty-data"
|
|
294
|
+
emptyTitle={t('movethewheels.orders.emptyTitle', 'No orders yet')}
|
|
295
|
+
emptyDescription={t(
|
|
296
|
+
'movethewheels.orders.emptyDescription',
|
|
297
|
+
'Create your first order to start moving shipments through the pipeline.'
|
|
298
|
+
)}
|
|
299
|
+
emptyAction={
|
|
300
|
+
<Button onClick={() => goTo('orders/new')}>
|
|
301
|
+
<Plus size={16} className="mr-1.5" />
|
|
302
|
+
{t('movethewheels.orders.actions.newOrder', 'New Order')}
|
|
303
|
+
</Button>
|
|
304
|
+
}
|
|
238
305
|
/>
|
|
239
306
|
</PageLayout>
|
|
240
307
|
);
|
|
@@ -6,6 +6,7 @@ import { useParams } from 'react-router-dom';
|
|
|
6
6
|
import { Package, ArrowLeft, Edit, Box, DollarSign, Tag } from 'lucide-react';
|
|
7
7
|
import { PageLayout, LoadingState, ErrorState } from '../components/PageLayout';
|
|
8
8
|
import { Card, CardContent, CardHeader, CardTitle, Button, Badge } from '../components/ui';
|
|
9
|
+
import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
9
10
|
import { useProduct } from '../hooks/useInventory';
|
|
10
11
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
11
12
|
import { formatCurrency } from '../utils/formatters';
|
|
@@ -17,6 +18,7 @@ export default function ProductDetailsPage() {
|
|
|
17
18
|
const { product, isLoading, error, refetch } = useProduct(productId);
|
|
18
19
|
|
|
19
20
|
const goBack = () => navigate?.(joinPath(basePath, 'inventory/products'));
|
|
21
|
+
const goTo = (path: string) => navigate?.(joinPath(basePath, path));
|
|
20
22
|
|
|
21
23
|
if (isLoading) return <LoadingState message="Loading product details..." />;
|
|
22
24
|
if (error || !product) return <ErrorState title="Product not found" onRetry={refetch} />;
|
|
@@ -46,6 +48,32 @@ export default function ProductDetailsPage() {
|
|
|
46
48
|
</div>
|
|
47
49
|
}
|
|
48
50
|
>
|
|
51
|
+
<PagePurpose icon={<Package size={18} />}>
|
|
52
|
+
The catalog record for this item — its dimensions, weight, value and handling flags. These
|
|
53
|
+
drive how it's packed, priced and routed, so keep them accurate before adding the
|
|
54
|
+
product to an order or stocking it in a warehouse.
|
|
55
|
+
</PagePurpose>
|
|
56
|
+
|
|
57
|
+
<NextSteps
|
|
58
|
+
storageKey="movethewheels-product-details"
|
|
59
|
+
steps={[
|
|
60
|
+
{
|
|
61
|
+
id: 'order',
|
|
62
|
+
label: 'Add this product to an order',
|
|
63
|
+
description: 'Include it on a new delivery.',
|
|
64
|
+
icon: <Package size={16} />,
|
|
65
|
+
onClick: () => goTo('orders/new'),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'edit',
|
|
69
|
+
label: 'Edit product details',
|
|
70
|
+
description: 'Update dimensions, value or handling flags.',
|
|
71
|
+
icon: <Tag size={16} />,
|
|
72
|
+
onClick: () => {},
|
|
73
|
+
},
|
|
74
|
+
]}
|
|
75
|
+
/>
|
|
76
|
+
|
|
49
77
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
50
78
|
<div className="lg:col-span-2 space-y-6">
|
|
51
79
|
<Card>
|
|
@@ -119,17 +147,14 @@ export default function ProductDetailsPage() {
|
|
|
119
147
|
</div>
|
|
120
148
|
|
|
121
149
|
<div className="space-y-6">
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
</
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
<p className="text-sm text-text-secondary">Unit price</p>
|
|
131
|
-
</CardContent>
|
|
132
|
-
</Card>
|
|
150
|
+
{/* Pricing — the page's single emphasis zone */}
|
|
151
|
+
<EmphasisPanel>
|
|
152
|
+
<h2 className="flex items-center gap-2 text-base font-semibold text-text-primary mb-3">
|
|
153
|
+
<DollarSign size={18} /> Pricing
|
|
154
|
+
</h2>
|
|
155
|
+
<p className="text-3xl font-bold">{formatCurrency(product.value)}</p>
|
|
156
|
+
<p className="text-sm text-text-secondary">Unit price</p>
|
|
157
|
+
</EmphasisPanel>
|
|
133
158
|
|
|
134
159
|
<Card>
|
|
135
160
|
<CardHeader>
|