@burdenoff/microfe-movethewheels 2026.625.1 → 2026.626.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AIAssistantPage-CNyoKtID.js → AIAssistantPage-AvsMbunM.js} +35 -29
- package/dist/{AnalyticsPage-Ck615ox7.js → AnalyticsPage-CCLfHgQZ.js} +36 -25
- package/dist/CreateOrderPage-CjlnEtk3.js +510 -0
- package/dist/CustomerDetailsPage-I6cdDHUi.js +265 -0
- package/dist/CustomersPage-CGGfD4ki.js +163 -0
- package/dist/{DashboardPage-DTmyuC11.js → DashboardPage-D2HRqwg9.js} +93 -89
- 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-DCfOmBa2.js → FleetPage-DddXt8r8.js} +109 -71
- package/dist/ImportExportPage-BW6qO3kc.js +248 -0
- package/dist/{InventoryPage-BW0QXWuf.js → InventoryPage-rMGuaJN-.js} +74 -70
- package/dist/LiveTrackingPage-DQ1q4Id3.js +375 -0
- package/dist/MarketplacePage-DKrXAv62.js +219 -0
- package/dist/OrderDetailsPage-CFASgsYX.js +431 -0
- package/dist/OrdersListPage-BHeT8S5o.js +309 -0
- package/dist/ProductDetailsPage-CFGcbaHo.js +195 -0
- package/dist/ProductsPage--xEpbQ8A.js +171 -0
- package/dist/{ReportsPage-Df3hciPy.js → ReportsPage-Bb7_90rK.js} +53 -45
- package/dist/RouteDetailsPage-CVtDVb1N.js +282 -0
- package/dist/RoutesPage-s57PtgcQ.js +163 -0
- package/dist/SettingsPage-BYNCBOpN.js +298 -0
- package/dist/{TrackingPage-CU0Vk4mx.js → TrackingPage-CcRc_dva.js} +77 -72
- 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/index.js +2 -2
- package/package.json +2 -2
- package/src/movethewheels/components/DataTable.tsx +37 -2
- package/src/movethewheels/components/MetricCard.tsx +3 -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 +40 -2
- package/src/movethewheels/pages/DashboardPage.tsx +8 -1
- package/src/movethewheels/pages/DriverDetailsPage.tsx +57 -26
- package/src/movethewheels/pages/DriversPage.tsx +37 -1
- package/src/movethewheels/pages/FinancePage.tsx +45 -41
- package/src/movethewheels/pages/FleetPage.tsx +68 -25
- package/src/movethewheels/pages/ImportExportPage.tsx +46 -29
- package/src/movethewheels/pages/InventoryPage.tsx +20 -23
- 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 +62 -1
- package/src/movethewheels/pages/ProductDetailsPage.tsx +36 -11
- package/src/movethewheels/pages/ProductsPage.tsx +33 -1
- package/src/movethewheels/pages/ReportsPage.tsx +23 -19
- package/src/movethewheels/pages/RouteDetailsPage.tsx +71 -31
- package/src/movethewheels/pages/RoutesPage.tsx +41 -5
- package/src/movethewheels/pages/SettingsPage.tsx +68 -26
- package/src/movethewheels/pages/TrackingPage.tsx +50 -47
- package/src/movethewheels/pages/VehicleDetailsPage.tsx +49 -24
- package/src/movethewheels/pages/VehiclesPage.tsx +33 -1
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +55 -30
- package/src/movethewheels/pages/WarehousesPage.tsx +42 -4
- package/dist/CreateOrderPage-C9Vb57Yn.js +0 -471
- package/dist/CustomerDetailsPage-BVkHRZvi.js +0 -239
- package/dist/CustomersPage-DhQg-6h_.js +0 -118
- package/dist/DataTable-CfWvFw5B.js +0 -239
- package/dist/DriverDetailsPage-DaaCQ-2O.js +0 -297
- package/dist/DriversPage-D0RMBB_t.js +0 -129
- package/dist/FinancePage-BUXqW1IM.js +0 -154
- package/dist/ImportExportPage-BM1BoRcE.js +0 -232
- package/dist/LiveTrackingPage-DqcGXdkw.js +0 -332
- package/dist/MarketplacePage-DFqsxNYU.js +0 -192
- package/dist/OrderDetailsPage-Dx1m3cVA.js +0 -398
- package/dist/OrdersListPage-DHYBGrWf.js +0 -256
- package/dist/ProductDetailsPage-DOXO90kb.js +0 -168
- package/dist/ProductsPage-RLMTwWN0.js +0 -133
- package/dist/RouteDetailsPage-xzFDeR-X.js +0 -240
- package/dist/RoutesPage-DDFy1QVi.js +0 -118
- package/dist/SettingsPage-JH_-UPbf.js +0 -247
- package/dist/VehicleDetailsPage-P4xDttKw.js +0 -194
- package/dist/VehiclesPage-tJZ64QKV.js +0 -129
- package/dist/WarehouseDetailsPage-Bt_w2Yf7.js +0 -215
- package/dist/WarehousesPage-HsPpEFeG.js +0 -126
|
@@ -6,8 +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 { TypeBadge } from '../components/StatusBadge';
|
|
9
|
-
import { Badge, Avatar, AvatarFallback } from '../components/ui';
|
|
10
|
-
import { CTAOverflowMenu } from '@burdenoff/fe-libs/ui';
|
|
9
|
+
import { Badge, Avatar, AvatarFallback, Button } from '../components/ui';
|
|
10
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
11
11
|
import { useCustomers } from '../hooks/useCustomers';
|
|
12
12
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
13
13
|
import { formatDate } from '../utils/formatters';
|
|
@@ -101,6 +101,35 @@ export default function CustomersPage() {
|
|
|
101
101
|
/>
|
|
102
102
|
}
|
|
103
103
|
>
|
|
104
|
+
<PagePurpose icon={<Users size={18} />}>
|
|
105
|
+
The businesses and individuals you deliver for. Keep contact details, locations and
|
|
106
|
+
satisfaction ratings here, and open a customer to see their order history before booking the
|
|
107
|
+
next shipment.
|
|
108
|
+
</PagePurpose>
|
|
109
|
+
|
|
110
|
+
{/* Customer base at a glance — the page's single emphasis zone */}
|
|
111
|
+
<EmphasisPanel>
|
|
112
|
+
<p className="text-sm font-medium text-text-secondary">Customer base at a glance</p>
|
|
113
|
+
<div className="mt-3 grid grid-cols-3 gap-4">
|
|
114
|
+
<div>
|
|
115
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">{totalCustomers}</p>
|
|
116
|
+
<p className="text-xs text-text-secondary">Total customers</p>
|
|
117
|
+
</div>
|
|
118
|
+
<div>
|
|
119
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">
|
|
120
|
+
{customers.filter((c) => c.isActive).length}
|
|
121
|
+
</p>
|
|
122
|
+
<p className="text-xs text-text-secondary">Active</p>
|
|
123
|
+
</div>
|
|
124
|
+
<div>
|
|
125
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">
|
|
126
|
+
{customers.filter((c) => c.type === 'b2b').length}
|
|
127
|
+
</p>
|
|
128
|
+
<p className="text-xs text-text-secondary">Business accounts</p>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</EmphasisPanel>
|
|
132
|
+
|
|
104
133
|
<DataTable
|
|
105
134
|
columns={columns}
|
|
106
135
|
data={customers}
|
|
@@ -110,6 +139,15 @@ export default function CustomersPage() {
|
|
|
110
139
|
searchFields={['name', 'email', 'company'] as (keyof Customer)[]}
|
|
111
140
|
onRowClick={(row) => goTo(`customers/${row.id}`)}
|
|
112
141
|
getRowId={(row) => row.id}
|
|
142
|
+
emptyIllustration="empty-people"
|
|
143
|
+
emptyTitle="No customers yet"
|
|
144
|
+
emptyDescription="Add your first customer to start booking deliveries for them."
|
|
145
|
+
emptyAction={
|
|
146
|
+
<Button onClick={() => goTo('customers/new')}>
|
|
147
|
+
<Plus size={16} className="mr-1.5" />
|
|
148
|
+
Add Customer
|
|
149
|
+
</Button>
|
|
150
|
+
}
|
|
113
151
|
/>
|
|
114
152
|
</PageLayout>
|
|
115
153
|
);
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
Badge,
|
|
28
28
|
Progress,
|
|
29
29
|
} from '../components/ui';
|
|
30
|
-
import { CTAOverflowMenu, EmphasisPanel } from '@burdenoff/fe-libs/ui';
|
|
30
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
31
31
|
import { useDashboardMetrics } from '../hooks/useAnalytics';
|
|
32
32
|
import { useOrders, useOrderStats } from '../hooks/useOrders';
|
|
33
33
|
import { useFleetStats } from '../hooks/useFleet';
|
|
@@ -80,6 +80,13 @@ export default function DashboardPage() {
|
|
|
80
80
|
/>
|
|
81
81
|
}
|
|
82
82
|
>
|
|
83
|
+
<PagePurpose icon={<TrendingUp size={18} />}>
|
|
84
|
+
{t(
|
|
85
|
+
'movethewheels.dashboard.purpose',
|
|
86
|
+
'Your logistics command center — a live snapshot of orders, active deliveries, fleet health and revenue. Start here each day to spot what needs attention, then jump straight into an order, the live map or fleet status.'
|
|
87
|
+
)}
|
|
88
|
+
</PagePurpose>
|
|
89
|
+
|
|
83
90
|
{/* Key Metrics */}
|
|
84
91
|
<MetricGrid columns={4}>
|
|
85
92
|
<MetricCard
|
|
@@ -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>
|
|
@@ -7,7 +7,7 @@ import { PageLayout } from '../components/PageLayout';
|
|
|
7
7
|
import { DataTable, type Column } from '../components/DataTable';
|
|
8
8
|
import { StatusBadge } from '../components/StatusBadge';
|
|
9
9
|
import { Avatar, AvatarFallback } from '../components/ui';
|
|
10
|
-
import { CTAOverflowMenu } from '@burdenoff/fe-libs/ui';
|
|
10
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
11
11
|
import { useDrivers } from '../hooks/useFleet';
|
|
12
12
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
13
13
|
import { formatDate } from '../utils/formatters';
|
|
@@ -100,6 +100,39 @@ export default function DriversPage() {
|
|
|
100
100
|
/>
|
|
101
101
|
}
|
|
102
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
|
+
|
|
103
136
|
<DataTable
|
|
104
137
|
columns={columns}
|
|
105
138
|
data={drivers}
|
|
@@ -109,6 +142,9 @@ export default function DriversPage() {
|
|
|
109
142
|
searchFields={['fullName', 'employeeId'] as (keyof Driver)[]}
|
|
110
143
|
onRowClick={(row) => goTo(`fleet/drivers/${row.id}`)}
|
|
111
144
|
getRowId={(row) => row.id}
|
|
145
|
+
emptyIllustration="empty-people"
|
|
146
|
+
emptyTitle="No drivers yet"
|
|
147
|
+
emptyDescription="Add drivers to start assigning deliveries and routes."
|
|
112
148
|
/>
|
|
113
149
|
</PageLayout>
|
|
114
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,7 +18,7 @@ import {
|
|
|
18
18
|
Avatar,
|
|
19
19
|
AvatarFallback,
|
|
20
20
|
} from '../components/ui';
|
|
21
|
-
import { CTAOverflowMenu } from '@burdenoff/fe-libs/ui';
|
|
21
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';
|
|
22
22
|
import { useFleetStats, useVehicles, useDrivers } from '../hooks/useFleet';
|
|
23
23
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
24
24
|
import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';
|
|
@@ -63,6 +63,49 @@ export default function FleetPage() {
|
|
|
63
63
|
/>
|
|
64
64
|
}
|
|
65
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
|
+
|
|
66
109
|
{/* Key Metrics */}
|
|
67
110
|
<MetricGrid columns={4}>
|
|
68
111
|
<MetricCard
|
|
@@ -80,6 +123,8 @@ export default function FleetPage() {
|
|
|
80
123
|
onClick={() => goTo('fleet/drivers')}
|
|
81
124
|
/>
|
|
82
125
|
<MetricCard
|
|
126
|
+
treatment="glass"
|
|
127
|
+
glow
|
|
83
128
|
label={t('movethewheels.fleet.metrics.utilization', 'Fleet Utilization')}
|
|
84
129
|
value={`${stats.utilization.toFixed(0)}%`}
|
|
85
130
|
icon={<Truck size={20} />}
|
|
@@ -202,29 +247,27 @@ export default function FleetPage() {
|
|
|
202
247
|
</PageSection>
|
|
203
248
|
</div>
|
|
204
249
|
|
|
205
|
-
{/* Status Summary */}
|
|
250
|
+
{/* Status Summary — Vehicle Status is the page's single emphasis zone */}
|
|
206
251
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
207
252
|
{/* Vehicle Status */}
|
|
208
|
-
<
|
|
209
|
-
<
|
|
210
|
-
|
|
211
|
-
</
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
{
|
|
215
|
-
<div
|
|
216
|
-
<
|
|
217
|
-
<StatusBadge status={status} type="vehicle" />
|
|
218
|
-
</div>
|
|
219
|
-
<div className="flex items-center gap-3">
|
|
220
|
-
<span className="font-medium">{count}</span>
|
|
221
|
-
<Progress value={(count / stats.totalVehicles) * 100} className="w-24 h-2" />
|
|
222
|
-
</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" />
|
|
223
262
|
</div>
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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>
|
|
228
271
|
|
|
229
272
|
{/* Driver Status */}
|
|
230
273
|
<Card>
|
|
@@ -251,9 +294,9 @@ export default function FleetPage() {
|
|
|
251
294
|
|
|
252
295
|
{/* Maintenance Alerts */}
|
|
253
296
|
{stats.vehiclesByStatus.maintenance > 0 && (
|
|
254
|
-
<Card className="border-
|
|
297
|
+
<Card className="border-status-warning-border">
|
|
255
298
|
<CardHeader>
|
|
256
|
-
<CardTitle className="flex items-center gap-2 text-
|
|
299
|
+
<CardTitle className="flex items-center gap-2 text-status-warning-text">
|
|
257
300
|
<Wrench size={18} />
|
|
258
301
|
{t('movethewheels.fleet.maintenance.title', 'Maintenance Required')}
|
|
259
302
|
</CardTitle>
|
|
@@ -265,10 +308,10 @@ export default function FleetPage() {
|
|
|
265
308
|
.map((vehicle) => (
|
|
266
309
|
<div
|
|
267
310
|
key={vehicle.id}
|
|
268
|
-
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"
|
|
269
312
|
>
|
|
270
313
|
<div className="flex items-center gap-3">
|
|
271
|
-
<Truck size={20} className="text-
|
|
314
|
+
<Truck size={20} className="text-status-warning-text" />
|
|
272
315
|
<div>
|
|
273
316
|
<p className="font-medium">{vehicle.vehicleNumber}</p>
|
|
274
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>
|