@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
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
SelectTrigger,
|
|
18
18
|
SelectValue,
|
|
19
19
|
} from '../components/ui';
|
|
20
|
+
import { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
20
21
|
|
|
21
22
|
export default function ReportsPage() {
|
|
22
23
|
const reports = [
|
|
@@ -87,6 +88,12 @@ export default function ReportsPage() {
|
|
|
87
88
|
</div>
|
|
88
89
|
}
|
|
89
90
|
>
|
|
91
|
+
<PagePurpose icon={<FileBarChart size={18} />}>
|
|
92
|
+
Turn your operational data into shareable summaries. Generate one-off reports on orders,
|
|
93
|
+
finance, fleet, customers or inventory, or schedule them to land in inboxes automatically —
|
|
94
|
+
so stakeholders always have the latest numbers without logging in.
|
|
95
|
+
</PagePurpose>
|
|
96
|
+
|
|
90
97
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
91
98
|
<div className="lg:col-span-2 space-y-4">
|
|
92
99
|
<h3 className="font-semibold">Available Reports</h3>
|
|
@@ -121,25 +128,22 @@ export default function ReportsPage() {
|
|
|
121
128
|
</div>
|
|
122
129
|
|
|
123
130
|
<div className="space-y-6">
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
</Button>
|
|
141
|
-
</CardContent>
|
|
142
|
-
</Card>
|
|
131
|
+
{/* Scheduled Reports — the page's single emphasis zone */}
|
|
132
|
+
<EmphasisPanel className="space-y-3">
|
|
133
|
+
<h2 className="flex items-center gap-2 text-base font-semibold text-text-primary">
|
|
134
|
+
<Clock size={18} /> Scheduled Reports
|
|
135
|
+
</h2>
|
|
136
|
+
{scheduledReports.map((report) => (
|
|
137
|
+
<div key={report.name} className="p-3 border border-border-seam rounded-lg">
|
|
138
|
+
<p className="font-medium">{report.name}</p>
|
|
139
|
+
<p className="text-sm text-text-secondary">Next: {report.nextRun}</p>
|
|
140
|
+
<p className="text-xs text-text-secondary">{report.recipients} recipients</p>
|
|
141
|
+
</div>
|
|
142
|
+
))}
|
|
143
|
+
<Button variant="outline" className="w-full">
|
|
144
|
+
Schedule New Report
|
|
145
|
+
</Button>
|
|
146
|
+
</EmphasisPanel>
|
|
143
147
|
|
|
144
148
|
<Card>
|
|
145
149
|
<CardHeader>
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import { useParams } from 'react-router-dom';
|
|
6
6
|
import { Navigation, ArrowLeft, Edit, MapPin, Clock, Zap, CheckCircle } from 'lucide-react';
|
|
7
|
-
import { PageLayout, LoadingState, ErrorState } from '../components/PageLayout';
|
|
7
|
+
import { PageLayout, LoadingState, ErrorState, EmptyState } from '../components/PageLayout';
|
|
8
8
|
import { StatusBadge } from '../components/StatusBadge';
|
|
9
9
|
import { Card, CardContent, CardHeader, CardTitle, Button, Badge } from '../components/ui';
|
|
10
|
+
import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
10
11
|
import { useRoute, useRoutes } from '../hooks/useRoutes';
|
|
11
12
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
12
13
|
import { formatDate, formatAddress } from '../utils/formatters';
|
|
@@ -19,6 +20,7 @@ export default function RouteDetailsPage() {
|
|
|
19
20
|
const { optimizeRoute } = useRoutes();
|
|
20
21
|
|
|
21
22
|
const goBack = () => navigate?.(joinPath(basePath, 'routes'));
|
|
23
|
+
const goTo = (path: string) => navigate?.(joinPath(basePath, path));
|
|
22
24
|
|
|
23
25
|
// Handle new route form
|
|
24
26
|
if (!routeId) {
|
|
@@ -33,11 +35,15 @@ export default function RouteDetailsPage() {
|
|
|
33
35
|
</Button>
|
|
34
36
|
}
|
|
35
37
|
>
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
<PagePurpose icon={<Navigation size={18} />}>
|
|
39
|
+
Plan a new delivery route — pick the stops to visit, and the optimizer will sequence them
|
|
40
|
+
into the shortest driving plan so one driver can cover them in a single run.
|
|
41
|
+
</PagePurpose>
|
|
42
|
+
<EmptyState
|
|
43
|
+
illustration="onboarding"
|
|
44
|
+
title="Build your route"
|
|
45
|
+
description="The route planner lets you add stops, set the running order, and optimize for distance and time."
|
|
46
|
+
/>
|
|
41
47
|
</PageLayout>
|
|
42
48
|
);
|
|
43
49
|
}
|
|
@@ -90,6 +96,43 @@ export default function RouteDetailsPage() {
|
|
|
90
96
|
</div>
|
|
91
97
|
}
|
|
92
98
|
>
|
|
99
|
+
<PagePurpose icon={<Navigation size={18} />}>
|
|
100
|
+
A single optimized driving plan that chains together this route's stops. Review the
|
|
101
|
+
sequence, distance and timing here, re-optimize if traffic or stops change, then hand the
|
|
102
|
+
itinerary to a driver.
|
|
103
|
+
</PagePurpose>
|
|
104
|
+
|
|
105
|
+
<NextSteps
|
|
106
|
+
storageKey="movethewheels-route-details"
|
|
107
|
+
steps={[
|
|
108
|
+
...(!route.optimized
|
|
109
|
+
? [
|
|
110
|
+
{
|
|
111
|
+
id: 'optimize',
|
|
112
|
+
label: 'Optimize this route',
|
|
113
|
+
description: 'Reorder stops to cut distance and driving time.',
|
|
114
|
+
icon: <Zap size={16} />,
|
|
115
|
+
onClick: handleOptimize,
|
|
116
|
+
},
|
|
117
|
+
]
|
|
118
|
+
: []),
|
|
119
|
+
{
|
|
120
|
+
id: 'edit',
|
|
121
|
+
label: 'Adjust stops',
|
|
122
|
+
description: 'Add, remove or reorder the delivery stops.',
|
|
123
|
+
icon: <Edit size={16} />,
|
|
124
|
+
onClick: () => {},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 'orders',
|
|
128
|
+
label: 'Review the orders on this route',
|
|
129
|
+
description: 'Check the deliveries bundled into this run.',
|
|
130
|
+
icon: <MapPin size={16} />,
|
|
131
|
+
onClick: () => goTo('orders'),
|
|
132
|
+
},
|
|
133
|
+
]}
|
|
134
|
+
/>
|
|
135
|
+
|
|
93
136
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
94
137
|
<div className="lg:col-span-2 space-y-6">
|
|
95
138
|
<Card>
|
|
@@ -161,36 +204,33 @@ export default function RouteDetailsPage() {
|
|
|
161
204
|
</div>
|
|
162
205
|
|
|
163
206
|
<div className="space-y-6">
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
<
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
</
|
|
207
|
+
{/* Route Info — the page's single emphasis zone */}
|
|
208
|
+
<EmphasisPanel className="space-y-4">
|
|
209
|
+
<h2 className="text-base font-semibold text-text-primary">Route Info</h2>
|
|
210
|
+
<div>
|
|
211
|
+
<p className="text-sm text-text-secondary">Total Distance</p>
|
|
212
|
+
<p className="text-2xl font-bold">
|
|
213
|
+
{route.distance.toFixed(1)}
|
|
214
|
+
<span className="text-sm font-normal"> km</span>
|
|
215
|
+
</p>
|
|
216
|
+
</div>
|
|
217
|
+
<div>
|
|
218
|
+
<p className="text-sm text-text-secondary">Est. Duration</p>
|
|
219
|
+
<p className="text-2xl font-bold">
|
|
220
|
+
{route.estimatedDuration}
|
|
221
|
+
<span className="text-sm font-normal"> min</span>
|
|
222
|
+
</p>
|
|
223
|
+
</div>
|
|
224
|
+
{route.actualDuration && (
|
|
176
225
|
<div>
|
|
177
|
-
<p className="text-sm text-text-secondary">
|
|
226
|
+
<p className="text-sm text-text-secondary">Actual Duration</p>
|
|
178
227
|
<p className="text-2xl font-bold">
|
|
179
|
-
{route.
|
|
228
|
+
{route.actualDuration}
|
|
180
229
|
<span className="text-sm font-normal"> min</span>
|
|
181
230
|
</p>
|
|
182
231
|
</div>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
<p className="text-sm text-text-secondary">Actual Duration</p>
|
|
186
|
-
<p className="text-2xl font-bold">
|
|
187
|
-
{route.actualDuration}
|
|
188
|
-
<span className="text-sm font-normal"> min</span>
|
|
189
|
-
</p>
|
|
190
|
-
</div>
|
|
191
|
-
)}
|
|
192
|
-
</CardContent>
|
|
193
|
-
</Card>
|
|
232
|
+
)}
|
|
233
|
+
</EmphasisPanel>
|
|
194
234
|
|
|
195
235
|
<Card>
|
|
196
236
|
<CardHeader>
|
|
@@ -6,8 +6,8 @@ import { Plus, RefreshCw, Navigation } 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 { Badge } from '../components/ui';
|
|
10
|
-
import { CTAOverflowMenu } from '@burdenoff/fe-libs/ui';
|
|
9
|
+
import { Badge, Button } from '../components/ui';
|
|
10
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
11
11
|
import { useRoutes } from '../hooks/useRoutes';
|
|
12
12
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
13
13
|
import { formatDate } from '../utils/formatters';
|
|
@@ -86,12 +86,39 @@ export default function RoutesPage() {
|
|
|
86
86
|
icon: <Plus size={16} />,
|
|
87
87
|
onSelect: () => goTo('routes/new'),
|
|
88
88
|
}}
|
|
89
|
-
actions={[
|
|
90
|
-
{ label: 'Refresh', icon: <RefreshCw size={16} />, onSelect: () => refetch() },
|
|
91
|
-
]}
|
|
89
|
+
actions={[{ label: 'Refresh', icon: <RefreshCw size={16} />, onSelect: () => refetch() }]}
|
|
92
90
|
/>
|
|
93
91
|
}
|
|
94
92
|
>
|
|
93
|
+
<PagePurpose icon={<Navigation size={18} />}>
|
|
94
|
+
Routes group multiple delivery stops into an optimized driving plan. Build a route to batch
|
|
95
|
+
nearby drop-offs, cut distance and fuel, and give drivers a single ordered itinerary — then
|
|
96
|
+
open one to review stops, distance and optimization status.
|
|
97
|
+
</PagePurpose>
|
|
98
|
+
|
|
99
|
+
{/* Routes at a glance — the page's single emphasis zone */}
|
|
100
|
+
<EmphasisPanel>
|
|
101
|
+
<p className="text-sm font-medium text-text-secondary">Routes at a glance</p>
|
|
102
|
+
<div className="mt-3 grid grid-cols-3 gap-4">
|
|
103
|
+
<div>
|
|
104
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">{totalRoutes}</p>
|
|
105
|
+
<p className="text-xs text-text-secondary">Total routes</p>
|
|
106
|
+
</div>
|
|
107
|
+
<div>
|
|
108
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">
|
|
109
|
+
{routes.filter((r) => r.status === 'in_progress').length}
|
|
110
|
+
</p>
|
|
111
|
+
<p className="text-xs text-text-secondary">In progress</p>
|
|
112
|
+
</div>
|
|
113
|
+
<div>
|
|
114
|
+
<p className="text-2xl font-bold text-text-primary tabular-nums">
|
|
115
|
+
{routes.filter((r) => r.optimized).length}
|
|
116
|
+
</p>
|
|
117
|
+
<p className="text-xs text-text-secondary">Optimized</p>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</EmphasisPanel>
|
|
121
|
+
|
|
95
122
|
<DataTable
|
|
96
123
|
columns={columns}
|
|
97
124
|
data={routes}
|
|
@@ -101,6 +128,15 @@ export default function RoutesPage() {
|
|
|
101
128
|
searchFields={['name'] as (keyof Route)[]}
|
|
102
129
|
onRowClick={(row) => goTo(`routes/${row.id}`)}
|
|
103
130
|
getRowId={(row) => row.id}
|
|
131
|
+
emptyIllustration="empty-data"
|
|
132
|
+
emptyTitle="No routes yet"
|
|
133
|
+
emptyDescription="Create a route to batch deliveries into an optimized driving plan."
|
|
134
|
+
emptyAction={
|
|
135
|
+
<Button onClick={() => goTo('routes/new')}>
|
|
136
|
+
<Plus size={16} className="mr-1.5" />
|
|
137
|
+
Create Route
|
|
138
|
+
</Button>
|
|
139
|
+
}
|
|
104
140
|
/>
|
|
105
141
|
</PageLayout>
|
|
106
142
|
);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Settings Page
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { useState } from 'react';
|
|
5
6
|
import { Settings, User, Bell, Shield, Globe, Webhook, Save } from 'lucide-react';
|
|
6
7
|
import { PageLayout } from '../components/PageLayout';
|
|
7
8
|
import {
|
|
@@ -24,15 +25,59 @@ import {
|
|
|
24
25
|
TabsTrigger,
|
|
25
26
|
TabsContent,
|
|
26
27
|
} from '../components/ui';
|
|
28
|
+
import { EmphasisPanel, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';
|
|
27
29
|
|
|
28
30
|
export default function SettingsPage() {
|
|
31
|
+
const [tab, setTab] = useState('general');
|
|
32
|
+
|
|
29
33
|
return (
|
|
30
34
|
<PageLayout
|
|
31
35
|
title="Settings"
|
|
32
36
|
subtitle="Manage your account and preferences"
|
|
33
37
|
icon={<Settings size={24} />}
|
|
34
38
|
>
|
|
35
|
-
<
|
|
39
|
+
<PagePurpose icon={<Settings size={18} />}>
|
|
40
|
+
Configure how MoveTheWheels works for your business — company details and localization,
|
|
41
|
+
which notifications you receive, account security, and the integrations (maps, payments,
|
|
42
|
+
messaging) that power deliveries. Set these once so the rest of the app behaves the way you
|
|
43
|
+
expect.
|
|
44
|
+
</PagePurpose>
|
|
45
|
+
|
|
46
|
+
<NextSteps
|
|
47
|
+
storageKey="movethewheels-settings"
|
|
48
|
+
steps={[
|
|
49
|
+
{
|
|
50
|
+
id: 'company',
|
|
51
|
+
label: 'Confirm company details & localization',
|
|
52
|
+
description: 'Set your name, contact info, timezone, currency and language.',
|
|
53
|
+
icon: <User size={16} />,
|
|
54
|
+
onClick: () => setTab('general'),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'notifications',
|
|
58
|
+
label: 'Choose your notifications',
|
|
59
|
+
description: 'Decide which order, delivery and report emails you receive.',
|
|
60
|
+
icon: <Bell size={16} />,
|
|
61
|
+
onClick: () => setTab('notifications'),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'security',
|
|
65
|
+
label: 'Secure your account',
|
|
66
|
+
description: 'Update your password and enable two-factor authentication.',
|
|
67
|
+
icon: <Shield size={16} />,
|
|
68
|
+
onClick: () => setTab('security'),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'integrations',
|
|
72
|
+
label: 'Connect your tools',
|
|
73
|
+
description: 'Link maps, payments and messaging providers.',
|
|
74
|
+
icon: <Webhook size={16} />,
|
|
75
|
+
onClick: () => setTab('integrations'),
|
|
76
|
+
},
|
|
77
|
+
]}
|
|
78
|
+
/>
|
|
79
|
+
|
|
80
|
+
<Tabs value={tab} onValueChange={setTab}>
|
|
36
81
|
<TabsList className="mb-6">
|
|
37
82
|
<TabsTrigger value="general">
|
|
38
83
|
<User size={16} /> General
|
|
@@ -49,32 +94,29 @@ export default function SettingsPage() {
|
|
|
49
94
|
</TabsList>
|
|
50
95
|
|
|
51
96
|
<TabsContent value="general" className="space-y-6">
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<
|
|
60
|
-
<Label>Company Name</Label>
|
|
61
|
-
<Input defaultValue="QuickLogistics Pro" />
|
|
62
|
-
</div>
|
|
63
|
-
<div>
|
|
64
|
-
<Label>Contact Email</Label>
|
|
65
|
-
<Input defaultValue="contact@quicklogistics.com" />
|
|
66
|
-
</div>
|
|
67
|
-
<div>
|
|
68
|
-
<Label>Phone</Label>
|
|
69
|
-
<Input defaultValue="+1-555-QUICK" />
|
|
70
|
-
</div>
|
|
71
|
-
<div>
|
|
72
|
-
<Label>Website</Label>
|
|
73
|
-
<Input defaultValue="https://quicklogistics.com" />
|
|
74
|
-
</div>
|
|
97
|
+
{/* Company Information — the page's single emphasis zone */}
|
|
98
|
+
<EmphasisPanel>
|
|
99
|
+
<h2 className="text-base font-semibold text-text-primary">Company Information</h2>
|
|
100
|
+
<p className="text-sm text-text-secondary mb-4">Basic company details</p>
|
|
101
|
+
<div className="grid grid-cols-2 gap-4">
|
|
102
|
+
<div>
|
|
103
|
+
<Label>Company Name</Label>
|
|
104
|
+
<Input defaultValue="QuickLogistics Pro" />
|
|
75
105
|
</div>
|
|
76
|
-
|
|
77
|
-
|
|
106
|
+
<div>
|
|
107
|
+
<Label>Contact Email</Label>
|
|
108
|
+
<Input defaultValue="contact@quicklogistics.com" />
|
|
109
|
+
</div>
|
|
110
|
+
<div>
|
|
111
|
+
<Label>Phone</Label>
|
|
112
|
+
<Input defaultValue="+1-555-QUICK" />
|
|
113
|
+
</div>
|
|
114
|
+
<div>
|
|
115
|
+
<Label>Website</Label>
|
|
116
|
+
<Input defaultValue="https://quicklogistics.com" />
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</EmphasisPanel>
|
|
78
120
|
|
|
79
121
|
<Card>
|
|
80
122
|
<CardHeader>
|
|
@@ -10,6 +10,7 @@ import { MapPin, Search, Package, Truck, Clock, CheckCircle, AlertCircle } from
|
|
|
10
10
|
import { PageLayout, EmptyState } from '../components/PageLayout';
|
|
11
11
|
import { StatusBadge } from '../components/StatusBadge';
|
|
12
12
|
import { Card, CardContent, CardHeader, CardTitle, Button, Input } from '../components/ui';
|
|
13
|
+
import { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
13
14
|
import { useOrderTracking } from '../hooks/useTracking';
|
|
14
15
|
import { formatDate, formatAddress } from '../utils/formatters';
|
|
15
16
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
@@ -56,6 +57,13 @@ export default function TrackingPage() {
|
|
|
56
57
|
</Button>
|
|
57
58
|
}
|
|
58
59
|
>
|
|
60
|
+
<PagePurpose icon={<MapPin size={18} />}>
|
|
61
|
+
{t(
|
|
62
|
+
'movethewheels.tracking.purpose',
|
|
63
|
+
'Look up any single shipment by its tracking number or order ID to see exactly where it is, its progress through pickup and delivery, the current location and any alerts — the same view you can share with a customer.'
|
|
64
|
+
)}
|
|
65
|
+
</PagePurpose>
|
|
66
|
+
|
|
59
67
|
{/* Search Form */}
|
|
60
68
|
<Card>
|
|
61
69
|
<CardContent className="p-6">
|
|
@@ -120,55 +128,50 @@ export default function TrackingPage() {
|
|
|
120
128
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
121
129
|
{/* Main Tracking Info */}
|
|
122
130
|
<div className="lg:col-span-2 space-y-6">
|
|
123
|
-
{/* Status Card */}
|
|
124
|
-
<
|
|
125
|
-
<
|
|
126
|
-
<
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
{/* Status Card — the page's single emphasis zone */}
|
|
132
|
+
<EmphasisPanel>
|
|
133
|
+
<div className="flex items-center justify-between mb-4">
|
|
134
|
+
<h2 className="flex items-center gap-2 text-base font-semibold text-text-primary">
|
|
135
|
+
<Package size={20} />
|
|
136
|
+
{t('movethewheels.tracking.orderLabel', 'Order')} {order.orderNumber}
|
|
137
|
+
</h2>
|
|
138
|
+
<StatusBadge status={order.status} type="order" />
|
|
139
|
+
</div>
|
|
140
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
141
|
+
<div>
|
|
142
|
+
<p className="text-sm text-text-secondary">
|
|
143
|
+
{t('movethewheels.tracking.details.trackingNumber', 'Tracking Number')}
|
|
144
|
+
</p>
|
|
145
|
+
<p className="font-mono font-medium">{order.trackingNumber}</p>
|
|
132
146
|
</div>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
{t('movethewheels.tracking.details.estimatedDelivery', 'Estimated Delivery')}
|
|
145
|
-
</p>
|
|
146
|
-
<p className="font-medium">
|
|
147
|
-
{tracking.estimatedArrival
|
|
148
|
-
? formatDate(tracking.estimatedArrival, 'full')
|
|
149
|
-
: order.scheduledDelivery
|
|
150
|
-
? formatDate(order.scheduledDelivery, 'full')
|
|
151
|
-
: t('movethewheels.tracking.details.tbd', 'TBD')}
|
|
152
|
-
</p>
|
|
153
|
-
</div>
|
|
154
|
-
<div>
|
|
155
|
-
<p className="text-sm text-text-secondary">
|
|
156
|
-
{t('movethewheels.tracking.details.items', 'Items')}
|
|
157
|
-
</p>
|
|
158
|
-
<p className="font-medium">
|
|
159
|
-
{order.items.length}{' '}
|
|
160
|
-
{t('movethewheels.tracking.details.itemsSuffix', 'items')}
|
|
161
|
-
</p>
|
|
162
|
-
</div>
|
|
163
|
-
<div>
|
|
164
|
-
<p className="text-sm text-text-secondary">
|
|
165
|
-
{t('movethewheels.tracking.details.lastUpdated', 'Last Updated')}
|
|
166
|
-
</p>
|
|
167
|
-
<p className="font-medium">{formatDate(tracking.lastUpdated, 'relative')}</p>
|
|
168
|
-
</div>
|
|
147
|
+
<div>
|
|
148
|
+
<p className="text-sm text-text-secondary">
|
|
149
|
+
{t('movethewheels.tracking.details.estimatedDelivery', 'Estimated Delivery')}
|
|
150
|
+
</p>
|
|
151
|
+
<p className="font-medium">
|
|
152
|
+
{tracking.estimatedArrival
|
|
153
|
+
? formatDate(tracking.estimatedArrival, 'full')
|
|
154
|
+
: order.scheduledDelivery
|
|
155
|
+
? formatDate(order.scheduledDelivery, 'full')
|
|
156
|
+
: t('movethewheels.tracking.details.tbd', 'TBD')}
|
|
157
|
+
</p>
|
|
169
158
|
</div>
|
|
170
|
-
|
|
171
|
-
|
|
159
|
+
<div>
|
|
160
|
+
<p className="text-sm text-text-secondary">
|
|
161
|
+
{t('movethewheels.tracking.details.items', 'Items')}
|
|
162
|
+
</p>
|
|
163
|
+
<p className="font-medium">
|
|
164
|
+
{order.items.length} {t('movethewheels.tracking.details.itemsSuffix', 'items')}
|
|
165
|
+
</p>
|
|
166
|
+
</div>
|
|
167
|
+
<div>
|
|
168
|
+
<p className="text-sm text-text-secondary">
|
|
169
|
+
{t('movethewheels.tracking.details.lastUpdated', 'Last Updated')}
|
|
170
|
+
</p>
|
|
171
|
+
<p className="font-medium">{formatDate(tracking.lastUpdated, 'relative')}</p>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</EmphasisPanel>
|
|
172
175
|
|
|
173
176
|
{/* Progress Timeline */}
|
|
174
177
|
<Card>
|
|
@@ -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 { useVehicle } from '../hooks/useFleet';
|
|
19
20
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
20
21
|
import { formatDate } from '../utils/formatters';
|
|
@@ -26,6 +27,7 @@ export default function VehicleDetailsPage() {
|
|
|
26
27
|
const { vehicle, isLoading, error, refetch } = useVehicle(vehicleId);
|
|
27
28
|
|
|
28
29
|
const goBack = () => navigate?.(joinPath(basePath, 'fleet/vehicles'));
|
|
30
|
+
const goTo = (path: string) => navigate?.(joinPath(basePath, path));
|
|
29
31
|
|
|
30
32
|
if (isLoading) return <LoadingState message="Loading vehicle details..." />;
|
|
31
33
|
if (error || !vehicle) return <ErrorState title="Vehicle not found" onRetry={refetch} />;
|
|
@@ -52,33 +54,56 @@ export default function VehicleDetailsPage() {
|
|
|
52
54
|
</div>
|
|
53
55
|
}
|
|
54
56
|
>
|
|
57
|
+
<PagePurpose icon={<Truck size={18} />}>
|
|
58
|
+
The full record for this vehicle — specs, capacity, fuel and mileage, maintenance schedule
|
|
59
|
+
and current location. Use it to confirm a vehicle is fuelled, serviced and the right size
|
|
60
|
+
before dispatching it on a delivery.
|
|
61
|
+
</PagePurpose>
|
|
62
|
+
|
|
63
|
+
<NextSteps
|
|
64
|
+
storageKey="movethewheels-vehicle-details"
|
|
65
|
+
steps={[
|
|
66
|
+
{
|
|
67
|
+
id: 'assign',
|
|
68
|
+
label: 'Assign to a delivery',
|
|
69
|
+
description: 'Put this vehicle on an order that fits its capacity.',
|
|
70
|
+
icon: <MapPin size={16} />,
|
|
71
|
+
onClick: () => goTo('orders'),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'maintenance',
|
|
75
|
+
label: 'Schedule maintenance',
|
|
76
|
+
description: 'Keep it road-ready and compliant.',
|
|
77
|
+
icon: <Wrench size={16} />,
|
|
78
|
+
onClick: () => {},
|
|
79
|
+
},
|
|
80
|
+
]}
|
|
81
|
+
/>
|
|
82
|
+
|
|
55
83
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
56
84
|
<div className="lg:col-span-2 space-y-6">
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
<p className="text-sm text-text-secondary">Type</p>
|
|
65
|
-
<p className="font-medium capitalize">{vehicle.type}</p>
|
|
66
|
-
</div>
|
|
67
|
-
<div>
|
|
68
|
-
<p className="text-sm text-text-secondary">Make</p>
|
|
69
|
-
<p className="font-medium">{vehicle.make}</p>
|
|
70
|
-
</div>
|
|
71
|
-
<div>
|
|
72
|
-
<p className="text-sm text-text-secondary">Model</p>
|
|
73
|
-
<p className="font-medium">{vehicle.model}</p>
|
|
74
|
-
</div>
|
|
75
|
-
<div>
|
|
76
|
-
<p className="text-sm text-text-secondary">Year</p>
|
|
77
|
-
<p className="font-medium">{vehicle.year}</p>
|
|
78
|
-
</div>
|
|
85
|
+
{/* Vehicle Information — the page's single emphasis zone */}
|
|
86
|
+
<EmphasisPanel>
|
|
87
|
+
<h2 className="text-base font-semibold text-text-primary mb-4">Vehicle Information</h2>
|
|
88
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
|
89
|
+
<div>
|
|
90
|
+
<p className="text-sm text-text-secondary">Type</p>
|
|
91
|
+
<p className="font-medium capitalize">{vehicle.type}</p>
|
|
79
92
|
</div>
|
|
80
|
-
|
|
81
|
-
|
|
93
|
+
<div>
|
|
94
|
+
<p className="text-sm text-text-secondary">Make</p>
|
|
95
|
+
<p className="font-medium">{vehicle.make}</p>
|
|
96
|
+
</div>
|
|
97
|
+
<div>
|
|
98
|
+
<p className="text-sm text-text-secondary">Model</p>
|
|
99
|
+
<p className="font-medium">{vehicle.model}</p>
|
|
100
|
+
</div>
|
|
101
|
+
<div>
|
|
102
|
+
<p className="text-sm text-text-secondary">Year</p>
|
|
103
|
+
<p className="font-medium">{vehicle.year}</p>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</EmphasisPanel>
|
|
82
107
|
|
|
83
108
|
<Card>
|
|
84
109
|
<CardHeader>
|