@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
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
Badge,
|
|
16
16
|
Progress,
|
|
17
17
|
} from '../components/ui';
|
|
18
|
+
import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
18
19
|
import { useDriver } from '../hooks/useFleet';
|
|
19
20
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
20
21
|
import { formatCurrency, formatDate } from '../utils/formatters';
|
|
@@ -26,6 +27,7 @@ export default function DriverDetailsPage() {
|
|
|
26
27
|
const { driver, isLoading, error, refetch } = useDriver(driverId);
|
|
27
28
|
|
|
28
29
|
const goBack = () => navigate?.(joinPath(basePath, 'fleet/drivers'));
|
|
30
|
+
const goTo = (path: string) => navigate?.(joinPath(basePath, path));
|
|
29
31
|
|
|
30
32
|
if (isLoading) return <LoadingState message="Loading driver details..." />;
|
|
31
33
|
if (error || !driver) return <ErrorState title="Driver not found" onRetry={refetch} />;
|
|
@@ -56,35 +58,64 @@ export default function DriverDetailsPage() {
|
|
|
56
58
|
</div>
|
|
57
59
|
}
|
|
58
60
|
>
|
|
61
|
+
<PagePurpose icon={<Users size={18} />}>
|
|
62
|
+
A driver's full profile — performance ratings, working hours, documents, earnings and
|
|
63
|
+
live location. Use it to confirm someone is road-ready and compliant before assigning them
|
|
64
|
+
an order or route.
|
|
65
|
+
</PagePurpose>
|
|
66
|
+
|
|
67
|
+
<NextSteps
|
|
68
|
+
storageKey="movethewheels-driver-details"
|
|
69
|
+
steps={[
|
|
70
|
+
{
|
|
71
|
+
id: 'assign',
|
|
72
|
+
label: 'Assign to an order',
|
|
73
|
+
description: 'Hand a pending delivery to this driver.',
|
|
74
|
+
icon: <MapPin size={16} />,
|
|
75
|
+
onClick: () => goTo('orders'),
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'routes',
|
|
79
|
+
label: 'Plan their route',
|
|
80
|
+
description: 'Batch nearby drop-offs into an optimized itinerary.',
|
|
81
|
+
icon: <Clock size={16} />,
|
|
82
|
+
onClick: () => goTo('routes'),
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'edit',
|
|
86
|
+
label: 'Update driver record',
|
|
87
|
+
description: 'Refresh documents, shift or compliance details.',
|
|
88
|
+
icon: <Edit size={16} />,
|
|
89
|
+
onClick: () => {},
|
|
90
|
+
},
|
|
91
|
+
]}
|
|
92
|
+
/>
|
|
93
|
+
|
|
59
94
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
60
95
|
<div className="lg:col-span-2 space-y-6">
|
|
61
|
-
{/* Ratings */}
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
{
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
<
|
|
79
|
-
<div className="flex items-center gap-2 mt-1">
|
|
80
|
-
<Progress value={rating.value * 20} className="flex-1 h-2" />
|
|
81
|
-
<span className="text-sm font-medium">{rating.value.toFixed(1)}</span>
|
|
82
|
-
</div>
|
|
96
|
+
{/* Ratings — the page's single emphasis zone */}
|
|
97
|
+
<EmphasisPanel>
|
|
98
|
+
<h2 className="flex items-center gap-2 text-base font-semibold text-text-primary mb-4">
|
|
99
|
+
<Star size={18} /> Ratings
|
|
100
|
+
</h2>
|
|
101
|
+
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
|
102
|
+
{[
|
|
103
|
+
{ label: 'Punctuality', value: driver.ratings.punctuality },
|
|
104
|
+
{ label: 'Communication', value: driver.ratings.communication },
|
|
105
|
+
{ label: 'Handling', value: driver.ratings.carefulHandling },
|
|
106
|
+
{ label: 'Professionalism', value: driver.ratings.professionalism },
|
|
107
|
+
{ label: 'On-Time Delivery', value: driver.ratings.onTimeDelivery },
|
|
108
|
+
].map((rating) => (
|
|
109
|
+
<div key={rating.label}>
|
|
110
|
+
<p className="text-sm text-text-secondary">{rating.label}</p>
|
|
111
|
+
<div className="flex items-center gap-2 mt-1">
|
|
112
|
+
<Progress value={rating.value * 20} className="flex-1 h-2" />
|
|
113
|
+
<span className="text-sm font-medium">{rating.value.toFixed(1)}</span>
|
|
83
114
|
</div>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
</
|
|
87
|
-
</
|
|
115
|
+
</div>
|
|
116
|
+
))}
|
|
117
|
+
</div>
|
|
118
|
+
</EmphasisPanel>
|
|
88
119
|
|
|
89
120
|
{/* Working Hours */}
|
|
90
121
|
<Card>
|
|
@@ -6,7 +6,8 @@ import { Users, Plus, RefreshCw, Download, Star } from 'lucide-react';
|
|
|
6
6
|
import { PageLayout } from '../components/PageLayout';
|
|
7
7
|
import { DataTable, type Column } from '../components/DataTable';
|
|
8
8
|
import { StatusBadge } from '../components/StatusBadge';
|
|
9
|
-
import {
|
|
9
|
+
import { Avatar, AvatarFallback } from '../components/ui';
|
|
10
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
10
11
|
import { useDrivers } from '../hooks/useFleet';
|
|
11
12
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
12
13
|
import { formatDate } from '../utils/formatters';
|
|
@@ -90,19 +91,48 @@ export default function DriversPage() {
|
|
|
90
91
|
subtitle={`${totalDrivers} drivers`}
|
|
91
92
|
icon={<Users size={24} />}
|
|
92
93
|
headerActions={
|
|
93
|
-
<
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<Button>
|
|
101
|
-
<Plus size={16} /> Add Driver
|
|
102
|
-
</Button>
|
|
103
|
-
</div>
|
|
94
|
+
<CTAOverflowMenu
|
|
95
|
+
primary={{ label: 'Add Driver', icon: <Plus size={16} />, onSelect: () => {} }}
|
|
96
|
+
actions={[
|
|
97
|
+
{ label: 'Refresh', icon: <RefreshCw size={16} />, onSelect: () => refetch() },
|
|
98
|
+
{ label: 'Export', icon: <Download size={16} />, onSelect: () => {} },
|
|
99
|
+
]}
|
|
100
|
+
/>
|
|
104
101
|
}
|
|
105
102
|
>
|
|
103
|
+
<PagePurpose icon={<Users size={18} />}>
|
|
104
|
+
Your delivery workforce. Track each driver's availability, performance rating,
|
|
105
|
+
completed deliveries and license status here — open a driver to review their history before
|
|
106
|
+
assigning them to an order or route.
|
|
107
|
+
</PagePurpose>
|
|
108
|
+
|
|
109
|
+
{/* Workforce at a glance — the page's single emphasis zone */}
|
|
110
|
+
<EmphasisPanel>
|
|
111
|
+
<p className="text-sm font-medium text-text-secondary">Workforce at a glance</p>
|
|
112
|
+
<div className="mt-3 grid grid-cols-3 gap-4">
|
|
113
|
+
<div>
|
|
114
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">{totalDrivers}</p>
|
|
115
|
+
<p className="text-xs text-text-secondary">Total drivers</p>
|
|
116
|
+
</div>
|
|
117
|
+
<div>
|
|
118
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">
|
|
119
|
+
{drivers.filter((d) => d.status === 'available' || d.status === 'busy').length}
|
|
120
|
+
</p>
|
|
121
|
+
<p className="text-xs text-text-secondary">On duty / available</p>
|
|
122
|
+
</div>
|
|
123
|
+
<div>
|
|
124
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">
|
|
125
|
+
{drivers.length > 0
|
|
126
|
+
? (drivers.reduce((sum, d) => sum + d.ratings.overall, 0) / drivers.length).toFixed(
|
|
127
|
+
1
|
|
128
|
+
)
|
|
129
|
+
: '—'}
|
|
130
|
+
</p>
|
|
131
|
+
<p className="text-xs text-text-secondary">Avg rating</p>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</EmphasisPanel>
|
|
135
|
+
|
|
106
136
|
<DataTable
|
|
107
137
|
columns={columns}
|
|
108
138
|
data={drivers}
|
|
@@ -112,6 +142,9 @@ export default function DriversPage() {
|
|
|
112
142
|
searchFields={['fullName', 'employeeId'] as (keyof Driver)[]}
|
|
113
143
|
onRowClick={(row) => goTo(`fleet/drivers/${row.id}`)}
|
|
114
144
|
getRowId={(row) => row.id}
|
|
145
|
+
emptyIllustration="empty-people"
|
|
146
|
+
emptyTitle="No drivers yet"
|
|
147
|
+
emptyDescription="Add drivers to start assigning deliveries and routes."
|
|
115
148
|
/>
|
|
116
149
|
</PageLayout>
|
|
117
150
|
);
|
|
@@ -6,6 +6,7 @@ import { DollarSign, TrendingUp, CreditCard, FileText, Download } from 'lucide-r
|
|
|
6
6
|
import { PageLayout } from '../components/PageLayout';
|
|
7
7
|
import { MetricCard, MetricGrid } from '../components/MetricCard';
|
|
8
8
|
import { Card, CardContent, CardHeader, CardTitle, Button } from '../components/ui';
|
|
9
|
+
import { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
9
10
|
import { useAnalytics } from '../hooks/useAnalytics';
|
|
10
11
|
import { useOrderStats } from '../hooks/useOrders';
|
|
11
12
|
import { formatCurrency } from '../utils/formatters';
|
|
@@ -33,8 +34,16 @@ export default function FinancePage() {
|
|
|
33
34
|
</div>
|
|
34
35
|
}
|
|
35
36
|
>
|
|
37
|
+
<PagePurpose icon={<DollarSign size={18} />}>
|
|
38
|
+
Follow the money behind your deliveries. See revenue earned, what'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.
|
|
41
|
+
</PagePurpose>
|
|
42
|
+
|
|
36
43
|
<MetricGrid columns={4}>
|
|
37
44
|
<MetricCard
|
|
45
|
+
treatment="glass"
|
|
46
|
+
glow
|
|
38
47
|
label="Total Revenue"
|
|
39
48
|
value={formatCurrency(analytics.overview.totalRevenue)}
|
|
40
49
|
icon={<DollarSign size={20} />}
|
|
@@ -81,47 +90,42 @@ export default function FinancePage() {
|
|
|
81
90
|
</CardContent>
|
|
82
91
|
</Card>
|
|
83
92
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
</
|
|
95
|
-
<
|
|
96
|
-
<p className="font-medium">Paid</p>
|
|
97
|
-
<p className="text-sm text-text-secondary">
|
|
98
|
-
{stats.byStatus.delivered} orders
|
|
99
|
-
</p>
|
|
100
|
-
</div>
|
|
93
|
+
{/* Payment Status — the page's single emphasis zone */}
|
|
94
|
+
<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>
|
|
101
105
|
</div>
|
|
102
|
-
<span className="text-xl font-bold text-green-600">
|
|
103
|
-
{formatCurrency(completedPayments)}
|
|
104
|
-
</span>
|
|
105
106
|
</div>
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
</
|
|
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>
|
|
117
121
|
</div>
|
|
118
|
-
<span className="text-xl font-bold text-yellow-600">
|
|
119
|
-
{formatCurrency(pendingPayments)}
|
|
120
|
-
</span>
|
|
121
122
|
</div>
|
|
123
|
+
<span className="text-xl font-bold text-status-warning-text">
|
|
124
|
+
{formatCurrency(pendingPayments)}
|
|
125
|
+
</span>
|
|
122
126
|
</div>
|
|
123
|
-
</
|
|
124
|
-
</
|
|
127
|
+
</div>
|
|
128
|
+
</EmphasisPanel>
|
|
125
129
|
</div>
|
|
126
130
|
|
|
127
131
|
<Card>
|
|
@@ -129,11 +133,11 @@ export default function FinancePage() {
|
|
|
129
133
|
<CardTitle>Recent Transactions</CardTitle>
|
|
130
134
|
</CardHeader>
|
|
131
135
|
<CardContent>
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
/>
|
|
137
141
|
</CardContent>
|
|
138
142
|
</Card>
|
|
139
143
|
</PageLayout>
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
Avatar,
|
|
19
19
|
AvatarFallback,
|
|
20
20
|
} from '../components/ui';
|
|
21
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';
|
|
21
22
|
import { useFleetStats, useVehicles, useDrivers } from '../hooks/useFleet';
|
|
22
23
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
23
24
|
import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';
|
|
@@ -46,18 +47,65 @@ export default function FleetPage() {
|
|
|
46
47
|
subtitle={t('movethewheels.fleet.subtitle', 'Manage your vehicles and drivers')}
|
|
47
48
|
icon={<Truck size={24} />}
|
|
48
49
|
headerActions={
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
<CTAOverflowMenu
|
|
51
|
+
primary={{
|
|
52
|
+
label: t('movethewheels.fleet.actions.allVehicles', 'All Vehicles'),
|
|
53
|
+
icon: <Truck size={16} />,
|
|
54
|
+
onSelect: () => goTo('fleet/vehicles'),
|
|
55
|
+
}}
|
|
56
|
+
actions={[
|
|
57
|
+
{
|
|
58
|
+
label: t('movethewheels.fleet.actions.allDrivers', 'All Drivers'),
|
|
59
|
+
icon: <Users size={16} />,
|
|
60
|
+
onSelect: () => goTo('fleet/drivers'),
|
|
61
|
+
},
|
|
62
|
+
]}
|
|
63
|
+
/>
|
|
59
64
|
}
|
|
60
65
|
>
|
|
66
|
+
<PagePurpose icon={<Truck size={18} />}>
|
|
67
|
+
{t(
|
|
68
|
+
'movethewheels.fleet.purpose',
|
|
69
|
+
'Your command center for vehicles and drivers. See utilization at a glance, keep an eye on availability and ratings, and act on maintenance alerts so you always have road-ready capacity to fulfil orders.'
|
|
70
|
+
)}
|
|
71
|
+
</PagePurpose>
|
|
72
|
+
|
|
73
|
+
<NextSteps
|
|
74
|
+
storageKey="movethewheels-fleet"
|
|
75
|
+
steps={[
|
|
76
|
+
{
|
|
77
|
+
id: 'vehicles',
|
|
78
|
+
label: t('movethewheels.fleet.nextSteps.vehicles', 'Review your vehicles'),
|
|
79
|
+
description: t(
|
|
80
|
+
'movethewheels.fleet.nextSteps.vehiclesDesc',
|
|
81
|
+
'Check capacity, fuel and maintenance status across the fleet.'
|
|
82
|
+
),
|
|
83
|
+
icon: <Truck size={16} />,
|
|
84
|
+
onClick: () => goTo('fleet/vehicles'),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'drivers',
|
|
88
|
+
label: t('movethewheels.fleet.nextSteps.drivers', 'Review your drivers'),
|
|
89
|
+
description: t(
|
|
90
|
+
'movethewheels.fleet.nextSteps.driversDesc',
|
|
91
|
+
'See availability, ratings and delivery history before assigning work.'
|
|
92
|
+
),
|
|
93
|
+
icon: <Users size={16} />,
|
|
94
|
+
onClick: () => goTo('fleet/drivers'),
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'assign',
|
|
98
|
+
label: t('movethewheels.fleet.nextSteps.assign', 'Assign capacity to orders'),
|
|
99
|
+
description: t(
|
|
100
|
+
'movethewheels.fleet.nextSteps.assignDesc',
|
|
101
|
+
'Open an order to assign an available vehicle and driver.'
|
|
102
|
+
),
|
|
103
|
+
icon: <ChevronRight size={16} />,
|
|
104
|
+
onClick: () => goTo('orders'),
|
|
105
|
+
},
|
|
106
|
+
]}
|
|
107
|
+
/>
|
|
108
|
+
|
|
61
109
|
{/* Key Metrics */}
|
|
62
110
|
<MetricGrid columns={4}>
|
|
63
111
|
<MetricCard
|
|
@@ -75,6 +123,8 @@ export default function FleetPage() {
|
|
|
75
123
|
onClick={() => goTo('fleet/drivers')}
|
|
76
124
|
/>
|
|
77
125
|
<MetricCard
|
|
126
|
+
treatment="glass"
|
|
127
|
+
glow
|
|
78
128
|
label={t('movethewheels.fleet.metrics.utilization', 'Fleet Utilization')}
|
|
79
129
|
value={`${stats.utilization.toFixed(0)}%`}
|
|
80
130
|
icon={<Truck size={20} />}
|
|
@@ -197,29 +247,27 @@ export default function FleetPage() {
|
|
|
197
247
|
</PageSection>
|
|
198
248
|
</div>
|
|
199
249
|
|
|
200
|
-
{/* Status Summary */}
|
|
250
|
+
{/* Status Summary — Vehicle Status is the page's single emphasis zone */}
|
|
201
251
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
202
252
|
{/* Vehicle Status */}
|
|
203
|
-
<
|
|
204
|
-
<
|
|
205
|
-
|
|
206
|
-
</
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
{
|
|
210
|
-
<div
|
|
211
|
-
<
|
|
212
|
-
<StatusBadge status={status} type="vehicle" />
|
|
213
|
-
</div>
|
|
214
|
-
<div className="flex items-center gap-3">
|
|
215
|
-
<span className="font-medium">{count}</span>
|
|
216
|
-
<Progress value={(count / stats.totalVehicles) * 100} className="w-24 h-2" />
|
|
217
|
-
</div>
|
|
253
|
+
<EmphasisPanel className="space-y-4">
|
|
254
|
+
<h2 className="text-base font-semibold text-text-primary">
|
|
255
|
+
{t('movethewheels.fleet.status.vehicleTitle', 'Vehicle Status')}
|
|
256
|
+
</h2>
|
|
257
|
+
<div className="space-y-4">
|
|
258
|
+
{Object.entries(stats.vehiclesByStatus).map(([status, count]) => (
|
|
259
|
+
<div key={status} className="flex items-center justify-between">
|
|
260
|
+
<div className="flex items-center gap-3">
|
|
261
|
+
<StatusBadge status={status} type="vehicle" />
|
|
218
262
|
</div>
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
263
|
+
<div className="flex items-center gap-3">
|
|
264
|
+
<span className="font-medium">{count}</span>
|
|
265
|
+
<Progress value={(count / stats.totalVehicles) * 100} className="w-24 h-2" />
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
))}
|
|
269
|
+
</div>
|
|
270
|
+
</EmphasisPanel>
|
|
223
271
|
|
|
224
272
|
{/* Driver Status */}
|
|
225
273
|
<Card>
|
|
@@ -246,9 +294,9 @@ export default function FleetPage() {
|
|
|
246
294
|
|
|
247
295
|
{/* Maintenance Alerts */}
|
|
248
296
|
{stats.vehiclesByStatus.maintenance > 0 && (
|
|
249
|
-
<Card className="border-
|
|
297
|
+
<Card className="border-status-warning-border">
|
|
250
298
|
<CardHeader>
|
|
251
|
-
<CardTitle className="flex items-center gap-2 text-
|
|
299
|
+
<CardTitle className="flex items-center gap-2 text-status-warning-text">
|
|
252
300
|
<Wrench size={18} />
|
|
253
301
|
{t('movethewheels.fleet.maintenance.title', 'Maintenance Required')}
|
|
254
302
|
</CardTitle>
|
|
@@ -260,10 +308,10 @@ export default function FleetPage() {
|
|
|
260
308
|
.map((vehicle) => (
|
|
261
309
|
<div
|
|
262
310
|
key={vehicle.id}
|
|
263
|
-
className="flex items-center justify-between p-3 bg-
|
|
311
|
+
className="flex items-center justify-between p-3 bg-status-warning-bg/10 rounded-lg"
|
|
264
312
|
>
|
|
265
313
|
<div className="flex items-center gap-3">
|
|
266
|
-
<Truck size={20} className="text-
|
|
314
|
+
<Truck size={20} className="text-status-warning-text" />
|
|
267
315
|
<div>
|
|
268
316
|
<p className="font-medium">{vehicle.vehicleNumber}</p>
|
|
269
317
|
<p className="text-sm text-text-secondary">
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { Upload, Download, FileText, FileSpreadsheet, Check } from 'lucide-react';
|
|
6
6
|
import { PageLayout } from '../components/PageLayout';
|
|
7
7
|
import { Card, CardContent, CardHeader, CardTitle, Button, Badge } from '../components/ui';
|
|
8
|
+
import { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
8
9
|
|
|
9
10
|
export default function ImportExportPage() {
|
|
10
11
|
const importOptions = [
|
|
@@ -69,14 +70,19 @@ export default function ImportExportPage() {
|
|
|
69
70
|
subtitle="Import and export your data"
|
|
70
71
|
icon={<FileText size={24} />}
|
|
71
72
|
>
|
|
73
|
+
<PagePurpose icon={<FileText size={18} />}>
|
|
74
|
+
Move data in and out in bulk. Import orders, customers, products or inventory from CSV/Excel
|
|
75
|
+
to onboard or migrate without re-keying, and export any dataset for accounting, BI tools or
|
|
76
|
+
backups — no manual copy-paste required.
|
|
77
|
+
</PagePurpose>
|
|
78
|
+
|
|
72
79
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<CardContent className="space-y-4">
|
|
80
|
+
{/* Import Data — the page's single emphasis zone */}
|
|
81
|
+
<EmphasisPanel className="space-y-4">
|
|
82
|
+
<h2 className="flex items-center gap-2 text-base font-semibold text-text-primary">
|
|
83
|
+
<Upload size={18} /> Import Data
|
|
84
|
+
</h2>
|
|
85
|
+
<div className="space-y-4">
|
|
80
86
|
{importOptions.map((opt) => (
|
|
81
87
|
<div
|
|
82
88
|
key={opt.name}
|
|
@@ -99,8 +105,8 @@ export default function ImportExportPage() {
|
|
|
99
105
|
</div>
|
|
100
106
|
</div>
|
|
101
107
|
))}
|
|
102
|
-
</
|
|
103
|
-
</
|
|
108
|
+
</div>
|
|
109
|
+
</EmphasisPanel>
|
|
104
110
|
|
|
105
111
|
<Card>
|
|
106
112
|
<CardHeader>
|
|
@@ -136,28 +142,39 @@ export default function ImportExportPage() {
|
|
|
136
142
|
<CardTitle>Recent Activity</CardTitle>
|
|
137
143
|
</CardHeader>
|
|
138
144
|
<CardContent>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
145
|
+
{recentActivity.length === 0 ? (
|
|
146
|
+
<IllustratedEmptyState
|
|
147
|
+
illustration="empty-data"
|
|
148
|
+
title="No import or export activity yet"
|
|
149
|
+
description="Your recent imports and exports will show up here once you run one."
|
|
150
|
+
/>
|
|
151
|
+
) : (
|
|
152
|
+
<div className="space-y-3">
|
|
153
|
+
{recentActivity.map((activity, i) => (
|
|
154
|
+
<div
|
|
155
|
+
key={i}
|
|
156
|
+
className="flex items-center justify-between p-3 bg-bg-sunken/50 rounded-lg"
|
|
157
|
+
>
|
|
158
|
+
<div className="flex items-center gap-3">
|
|
159
|
+
{activity.type === 'import' ? (
|
|
160
|
+
<Upload size={16} className="text-primary" />
|
|
161
|
+
) : (
|
|
162
|
+
<Download size={16} className="text-primary" />
|
|
163
|
+
)}
|
|
164
|
+
<div>
|
|
165
|
+
<p className="font-medium">{activity.name}</p>
|
|
166
|
+
<p className="text-sm text-text-secondary">
|
|
167
|
+
{activity.type === 'import' ? 'Imported' : 'Exported'} on {activity.date}
|
|
168
|
+
</p>
|
|
169
|
+
</div>
|
|
153
170
|
</div>
|
|
171
|
+
<Badge variant="default" className="gap-1">
|
|
172
|
+
<Check size={12} /> {activity.status}
|
|
173
|
+
</Badge>
|
|
154
174
|
</div>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
</div>
|
|
159
|
-
))}
|
|
160
|
-
</div>
|
|
175
|
+
))}
|
|
176
|
+
</div>
|
|
177
|
+
)}
|
|
161
178
|
</CardContent>
|
|
162
179
|
</Card>
|
|
163
180
|
</PageLayout>
|