@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
|
@@ -6,6 +6,7 @@ import { BarChart3, TrendingUp, Package, Truck, DollarSign, Users } from 'lucide
|
|
|
6
6
|
import { PageLayout } from '../components/PageLayout';
|
|
7
7
|
import { MetricCard, MetricGrid } from '../components/MetricCard';
|
|
8
8
|
import { Card, CardContent, CardHeader, CardTitle, Progress } from '../components/ui';
|
|
9
|
+
import { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
9
10
|
import { useAnalytics, useOrderAnalytics, useFleetAnalytics } from '../hooks/useAnalytics';
|
|
10
11
|
import { formatCurrency } from '../utils/formatters';
|
|
11
12
|
|
|
@@ -20,6 +21,12 @@ export default function AnalyticsPage() {
|
|
|
20
21
|
subtitle="Business performance and insights"
|
|
21
22
|
icon={<BarChart3 size={24} />}
|
|
22
23
|
>
|
|
24
|
+
<PagePurpose icon={<BarChart3 size={18} />}>
|
|
25
|
+
See how the operation is really performing. Track order volume, revenue, on-time rate and
|
|
26
|
+
customer satisfaction over time so you can spot trends, find bottlenecks, and decide where
|
|
27
|
+
to invest — whether that's more vehicles, better routes, or new customers.
|
|
28
|
+
</PagePurpose>
|
|
29
|
+
|
|
23
30
|
<MetricGrid columns={5}>
|
|
24
31
|
<MetricCard
|
|
25
32
|
label="Total Orders"
|
|
@@ -30,6 +37,8 @@ export default function AnalyticsPage() {
|
|
|
30
37
|
trend="up"
|
|
31
38
|
/>
|
|
32
39
|
<MetricCard
|
|
40
|
+
treatment="glass"
|
|
41
|
+
glow
|
|
33
42
|
label="Revenue"
|
|
34
43
|
value={formatCurrency(analytics.overview.totalRevenue)}
|
|
35
44
|
icon={<DollarSign size={20} />}
|
|
@@ -159,31 +168,28 @@ export default function AnalyticsPage() {
|
|
|
159
168
|
</CardContent>
|
|
160
169
|
</Card>
|
|
161
170
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
<
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
</
|
|
171
|
-
<
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
</
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
</
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
</div>
|
|
185
|
-
</CardContent>
|
|
186
|
-
</Card>
|
|
171
|
+
{/* Performance Metrics — the page's single emphasis zone */}
|
|
172
|
+
<EmphasisPanel className="space-y-4">
|
|
173
|
+
<h2 className="text-base font-semibold text-text-primary">Performance Metrics</h2>
|
|
174
|
+
<div>
|
|
175
|
+
<p className="text-sm text-text-secondary">On-Time Pickup</p>
|
|
176
|
+
<p className="text-2xl font-bold">{analytics.performance.onTimePickup}%</p>
|
|
177
|
+
</div>
|
|
178
|
+
<div>
|
|
179
|
+
<p className="text-sm text-text-secondary">On-Time Delivery</p>
|
|
180
|
+
<p className="text-2xl font-bold">{analytics.performance.onTimeDelivery}%</p>
|
|
181
|
+
</div>
|
|
182
|
+
<div>
|
|
183
|
+
<p className="text-sm text-text-secondary">Damaged Items</p>
|
|
184
|
+
<p className="text-2xl font-bold text-status-success-text">
|
|
185
|
+
{analytics.performance.damagedItems}%
|
|
186
|
+
</p>
|
|
187
|
+
</div>
|
|
188
|
+
<div>
|
|
189
|
+
<p className="text-sm text-text-secondary">Driver Ratings</p>
|
|
190
|
+
<p className="text-2xl font-bold">{analytics.performance.driverRatings}/5</p>
|
|
191
|
+
</div>
|
|
192
|
+
</EmphasisPanel>
|
|
187
193
|
</div>
|
|
188
194
|
</PageLayout>
|
|
189
195
|
);
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
import { useState } from 'react';
|
|
8
8
|
import { useParams } from 'react-router-dom';
|
|
9
|
-
import { Package, ArrowLeft, Save, Plus, Trash2 } from 'lucide-react';
|
|
10
|
-
import { PageLayout } from '../components/PageLayout';
|
|
9
|
+
import { Package, ArrowLeft, Save, Plus, Trash2, Users, MapPin } from 'lucide-react';
|
|
10
|
+
import { PageLayout, EmptyState } from '../components/PageLayout';
|
|
11
11
|
import {
|
|
12
12
|
Card,
|
|
13
13
|
CardContent,
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
SelectValue,
|
|
25
25
|
Separator,
|
|
26
26
|
} from '../components/ui';
|
|
27
|
+
import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
27
28
|
import { useOrder, useOrders } from '../hooks/useOrders';
|
|
28
29
|
import { useCustomers } from '../hooks/useCustomers';
|
|
29
30
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
@@ -171,13 +172,47 @@ export default function CreateOrderPage() {
|
|
|
171
172
|
</div>
|
|
172
173
|
}
|
|
173
174
|
>
|
|
175
|
+
<PagePurpose icon={<Package size={18} />}>
|
|
176
|
+
{isEditing
|
|
177
|
+
? 'Update this delivery before it goes out — change the customer, addresses, items or handling notes, then save.'
|
|
178
|
+
: 'Book a new delivery in a few steps: choose who it’s for, set pickup and drop-off addresses, list what’s being shipped, and add any handling notes. Once created it joins the orders pipeline ready to assign and track.'}
|
|
179
|
+
</PagePurpose>
|
|
180
|
+
|
|
181
|
+
<NextSteps
|
|
182
|
+
storageKey="movethewheels-create-order"
|
|
183
|
+
steps={[
|
|
184
|
+
{
|
|
185
|
+
id: 'customer',
|
|
186
|
+
label: 'Pick the customer first',
|
|
187
|
+
description: 'Their saved contact details auto-fill the order.',
|
|
188
|
+
icon: <Users size={16} />,
|
|
189
|
+
done: !!formData.customerId,
|
|
190
|
+
onClick: () => document.getElementById('customer')?.focus(),
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: 'addresses',
|
|
194
|
+
label: 'Set pickup & delivery addresses',
|
|
195
|
+
description: 'Where the shipment is collected and dropped off.',
|
|
196
|
+
icon: <MapPin size={16} />,
|
|
197
|
+
done: !!formData.pickupAddress.street && !!formData.deliveryAddress.street,
|
|
198
|
+
onClick: () => document.getElementById('pickupStreet')?.focus(),
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'items',
|
|
202
|
+
label: 'Add the items to ship',
|
|
203
|
+
description: 'List each product with its quantity and value.',
|
|
204
|
+
icon: <Package size={16} />,
|
|
205
|
+
done: formData.items.length > 0,
|
|
206
|
+
onClick: addItem,
|
|
207
|
+
},
|
|
208
|
+
]}
|
|
209
|
+
/>
|
|
210
|
+
|
|
174
211
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
175
|
-
{/* Order Details */}
|
|
176
|
-
<
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
</CardHeader>
|
|
180
|
-
<CardContent className="space-y-4">
|
|
212
|
+
{/* Order Details — the page's single emphasis zone (the form's starting point) */}
|
|
213
|
+
<EmphasisPanel>
|
|
214
|
+
<h2 className="text-base font-semibold text-text-primary mb-4">Order Details</h2>
|
|
215
|
+
<div className="space-y-4">
|
|
181
216
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
182
217
|
<div className="space-y-2">
|
|
183
218
|
<Label htmlFor="customer">Customer</Label>
|
|
@@ -230,8 +265,8 @@ export default function CreateOrderPage() {
|
|
|
230
265
|
</Select>
|
|
231
266
|
</div>
|
|
232
267
|
</div>
|
|
233
|
-
</
|
|
234
|
-
</
|
|
268
|
+
</div>
|
|
269
|
+
</EmphasisPanel>
|
|
235
270
|
|
|
236
271
|
{/* Addresses */}
|
|
237
272
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
@@ -363,13 +398,17 @@ export default function CreateOrderPage() {
|
|
|
363
398
|
</CardHeader>
|
|
364
399
|
<CardContent>
|
|
365
400
|
{formData.items.length === 0 ? (
|
|
366
|
-
<
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
401
|
+
<EmptyState
|
|
402
|
+
illustration="empty-files"
|
|
403
|
+
title="No items added yet"
|
|
404
|
+
description="Add the products this order will ship — each with a name, SKU, quantity and value."
|
|
405
|
+
action={
|
|
406
|
+
<Button type="button" variant="outline" onClick={addItem}>
|
|
407
|
+
<Plus size={16} className="mr-1.5" />
|
|
408
|
+
Add your first item
|
|
409
|
+
</Button>
|
|
410
|
+
}
|
|
411
|
+
/>
|
|
373
412
|
) : (
|
|
374
413
|
<div className="space-y-4">
|
|
375
414
|
{formData.items.map((item, index) => (
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { PageLayout, LoadingState, ErrorState } from '../components/PageLayout';
|
|
18
18
|
import { TypeBadge } from '../components/StatusBadge';
|
|
19
19
|
import { Card, CardContent, CardHeader, CardTitle, Button, Badge } from '../components/ui';
|
|
20
|
+
import { EmphasisPanel, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
20
21
|
import { useCustomer } from '../hooks/useCustomers';
|
|
21
22
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
22
23
|
import { formatAddress, formatDate } from '../utils/formatters';
|
|
@@ -28,6 +29,7 @@ export default function CustomerDetailsPage() {
|
|
|
28
29
|
const { customer, isLoading, error, refetch } = useCustomer(customerId);
|
|
29
30
|
|
|
30
31
|
const goBack = () => navigate?.(joinPath(basePath, 'customers'));
|
|
32
|
+
const goTo = (path: string) => navigate?.(joinPath(basePath, path));
|
|
31
33
|
|
|
32
34
|
// Handle new customer form
|
|
33
35
|
if (!customerId) {
|
|
@@ -82,52 +84,75 @@ export default function CustomerDetailsPage() {
|
|
|
82
84
|
</div>
|
|
83
85
|
}
|
|
84
86
|
>
|
|
87
|
+
<PagePurpose icon={<Users size={18} />}>
|
|
88
|
+
Everything you know about this customer — how to reach them, where they are, and their
|
|
89
|
+
account standing. Use it to confirm contact details before a delivery, check their rating,
|
|
90
|
+
or book their next order.
|
|
91
|
+
</PagePurpose>
|
|
92
|
+
|
|
93
|
+
<NextSteps
|
|
94
|
+
storageKey="movethewheels-customer-details"
|
|
95
|
+
steps={[
|
|
96
|
+
{
|
|
97
|
+
id: 'new-order',
|
|
98
|
+
label: 'Book a delivery for this customer',
|
|
99
|
+
description: 'Start a new order pre-filled for this account.',
|
|
100
|
+
icon: <FileText size={16} />,
|
|
101
|
+
onClick: () => goTo('orders/new'),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 'edit',
|
|
105
|
+
label: 'Update contact details',
|
|
106
|
+
description: 'Keep email, phone and address current.',
|
|
107
|
+
icon: <Edit size={16} />,
|
|
108
|
+
onClick: () => {},
|
|
109
|
+
},
|
|
110
|
+
]}
|
|
111
|
+
/>
|
|
112
|
+
|
|
85
113
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
86
114
|
<div className="lg:col-span-2 space-y-6">
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
<div
|
|
95
|
-
<
|
|
96
|
-
<
|
|
97
|
-
<p className="text-sm text-text-secondary">Email</p>
|
|
98
|
-
<p className="font-medium">{customer.email}</p>
|
|
99
|
-
</div>
|
|
115
|
+
{/* Contact Information — the page's single emphasis zone */}
|
|
116
|
+
<EmphasisPanel>
|
|
117
|
+
<h2 className="text-base font-semibold text-text-primary mb-4">Contact Information</h2>
|
|
118
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
119
|
+
<div className="space-y-4">
|
|
120
|
+
<div className="flex items-center gap-3">
|
|
121
|
+
<Mail size={18} className="text-text-secondary" />
|
|
122
|
+
<div>
|
|
123
|
+
<p className="text-sm text-text-secondary">Email</p>
|
|
124
|
+
<p className="font-medium">{customer.email}</p>
|
|
100
125
|
</div>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
</
|
|
126
|
+
</div>
|
|
127
|
+
<div className="flex items-center gap-3">
|
|
128
|
+
<Phone size={18} className="text-text-secondary" />
|
|
129
|
+
<div>
|
|
130
|
+
<p className="text-sm text-text-secondary">Phone</p>
|
|
131
|
+
<p className="font-medium">{customer.phone}</p>
|
|
107
132
|
</div>
|
|
108
133
|
</div>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
<p className="text-sm text-text-secondary">Company</p>
|
|
115
|
-
<p className="font-medium">{customer.company}</p>
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
|
-
)}
|
|
119
|
-
{customer.website && (
|
|
134
|
+
</div>
|
|
135
|
+
<div className="space-y-4">
|
|
136
|
+
{customer.company && (
|
|
137
|
+
<div className="flex items-center gap-3">
|
|
138
|
+
<Building size={18} className="text-text-secondary" />
|
|
120
139
|
<div>
|
|
121
|
-
<p className="text-sm text-text-secondary">
|
|
122
|
-
<
|
|
123
|
-
{customer.website}
|
|
124
|
-
</a>
|
|
140
|
+
<p className="text-sm text-text-secondary">Company</p>
|
|
141
|
+
<p className="font-medium">{customer.company}</p>
|
|
125
142
|
</div>
|
|
126
|
-
|
|
127
|
-
|
|
143
|
+
</div>
|
|
144
|
+
)}
|
|
145
|
+
{customer.website && (
|
|
146
|
+
<div>
|
|
147
|
+
<p className="text-sm text-text-secondary">Website</p>
|
|
148
|
+
<a href={customer.website} className="text-primary hover:underline">
|
|
149
|
+
{customer.website}
|
|
150
|
+
</a>
|
|
151
|
+
</div>
|
|
152
|
+
)}
|
|
128
153
|
</div>
|
|
129
|
-
</
|
|
130
|
-
</
|
|
154
|
+
</div>
|
|
155
|
+
</EmphasisPanel>
|
|
131
156
|
|
|
132
157
|
<Card>
|
|
133
158
|
<CardHeader>
|
|
@@ -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 { TypeBadge } from '../components/StatusBadge';
|
|
9
|
-
import {
|
|
9
|
+
import { Badge, Avatar, AvatarFallback, Button } from '../components/ui';
|
|
10
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
10
11
|
import { useCustomers } from '../hooks/useCustomers';
|
|
11
12
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
12
13
|
import { formatDate } from '../utils/formatters';
|
|
@@ -87,19 +88,48 @@ export default function CustomersPage() {
|
|
|
87
88
|
subtitle={`${totalCustomers} customers`}
|
|
88
89
|
icon={<Users size={24} />}
|
|
89
90
|
headerActions={
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
91
|
+
<CTAOverflowMenu
|
|
92
|
+
primary={{
|
|
93
|
+
label: 'Add Customer',
|
|
94
|
+
icon: <Plus size={16} />,
|
|
95
|
+
onSelect: () => goTo('customers/new'),
|
|
96
|
+
}}
|
|
97
|
+
actions={[
|
|
98
|
+
{ label: 'Refresh', icon: <RefreshCw size={16} />, onSelect: () => refetch() },
|
|
99
|
+
{ label: 'Export', icon: <Download size={16} />, onSelect: () => {} },
|
|
100
|
+
]}
|
|
101
|
+
/>
|
|
101
102
|
}
|
|
102
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
|
+
|
|
103
133
|
<DataTable
|
|
104
134
|
columns={columns}
|
|
105
135
|
data={customers}
|
|
@@ -109,6 +139,15 @@ export default function CustomersPage() {
|
|
|
109
139
|
searchFields={['name', 'email', 'company'] as (keyof Customer)[]}
|
|
110
140
|
onRowClick={(row) => goTo(`customers/${row.id}`)}
|
|
111
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
|
+
}
|
|
112
151
|
/>
|
|
113
152
|
</PageLayout>
|
|
114
153
|
);
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
Badge,
|
|
28
28
|
Progress,
|
|
29
29
|
} from '../components/ui';
|
|
30
|
+
import { CTAOverflowMenu, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
30
31
|
import { useDashboardMetrics } from '../hooks/useAnalytics';
|
|
31
32
|
import { useOrders, useOrderStats } from '../hooks/useOrders';
|
|
32
33
|
import { useFleetStats } from '../hooks/useFleet';
|
|
@@ -57,22 +58,35 @@ export default function DashboardPage() {
|
|
|
57
58
|
|
|
58
59
|
return (
|
|
59
60
|
<PageLayout
|
|
61
|
+
hero
|
|
62
|
+
titleAccent={t('movethewheels.dashboard.title', 'Dashboard')}
|
|
60
63
|
title={t('movethewheels.dashboard.title', 'Dashboard')}
|
|
61
64
|
subtitle={t('movethewheels.dashboard.subtitle', 'Overview of your logistics operations')}
|
|
62
65
|
icon={<TrendingUp size={24} />}
|
|
63
66
|
headerActions={
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
<CTAOverflowMenu
|
|
68
|
+
primary={{
|
|
69
|
+
label: t('movethewheels.dashboard.actions.newOrder', 'New Order'),
|
|
70
|
+
icon: <Package size={16} />,
|
|
71
|
+
onSelect: () => goTo('orders/new'),
|
|
72
|
+
}}
|
|
73
|
+
actions={[
|
|
74
|
+
{
|
|
75
|
+
label: t('movethewheels.dashboard.actions.liveMap', 'Live Map'),
|
|
76
|
+
icon: <MapPin size={16} />,
|
|
77
|
+
onSelect: () => goTo('tracking/live'),
|
|
78
|
+
},
|
|
79
|
+
]}
|
|
80
|
+
/>
|
|
74
81
|
}
|
|
75
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
|
+
|
|
76
90
|
{/* Key Metrics */}
|
|
77
91
|
<MetricGrid columns={4}>
|
|
78
92
|
<MetricCard
|
|
@@ -99,6 +113,8 @@ export default function DashboardPage() {
|
|
|
99
113
|
onClick={() => goTo('fleet')}
|
|
100
114
|
/>
|
|
101
115
|
<MetricCard
|
|
116
|
+
treatment="glass"
|
|
117
|
+
glow
|
|
102
118
|
label={t('movethewheels.dashboard.metrics.revenue', 'Revenue')}
|
|
103
119
|
value={formatCurrency(metrics.revenue.total)}
|
|
104
120
|
icon={<DollarSign size={20} />}
|
|
@@ -234,45 +250,43 @@ export default function DashboardPage() {
|
|
|
234
250
|
</div>
|
|
235
251
|
|
|
236
252
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
237
|
-
{/* Order Status Summary */}
|
|
238
|
-
<
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
status.replace('_', ' ')
|
|
267
|
-
)}
|
|
268
|
-
</span>
|
|
269
|
-
</div>
|
|
270
|
-
<Badge variant="secondary">{count}</Badge>
|
|
253
|
+
{/* Order Status Summary — the page's single emphasis zone */}
|
|
254
|
+
<EmphasisPanel className="space-y-4">
|
|
255
|
+
<h2 className="text-base font-semibold text-text-primary">
|
|
256
|
+
{t('movethewheels.dashboard.orderStatus.title', 'Order Status')}
|
|
257
|
+
</h2>
|
|
258
|
+
<div className="space-y-3">
|
|
259
|
+
{[
|
|
260
|
+
{ status: 'pending', count: orderStats.byStatus.pending, color: 'bg-yellow-500' },
|
|
261
|
+
{
|
|
262
|
+
status: 'in_transit',
|
|
263
|
+
count: orderStats.byStatus.in_transit,
|
|
264
|
+
color: 'bg-blue-500',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
status: 'delivered',
|
|
268
|
+
count: orderStats.byStatus.delivered,
|
|
269
|
+
color: 'bg-green-500',
|
|
270
|
+
},
|
|
271
|
+
{ status: 'cancelled', count: orderStats.byStatus.cancelled, color: 'bg-red-500' },
|
|
272
|
+
].map(({ status, count, color }) => (
|
|
273
|
+
<div key={status} className="flex items-center justify-between">
|
|
274
|
+
<div className="flex items-center gap-2">
|
|
275
|
+
<div className={`w-2 h-2 rounded-full ${color}`} />
|
|
276
|
+
<span className="text-sm capitalize">
|
|
277
|
+
{t(
|
|
278
|
+
`movethewheels.dashboard.orderStatus.status.${status}`,
|
|
279
|
+
status.replace('_', ' ')
|
|
280
|
+
)}
|
|
281
|
+
</span>
|
|
271
282
|
</div>
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
283
|
+
<Badge variant="secondary" className="tabular-nums">
|
|
284
|
+
{count}
|
|
285
|
+
</Badge>
|
|
286
|
+
</div>
|
|
287
|
+
))}
|
|
288
|
+
</div>
|
|
289
|
+
</EmphasisPanel>
|
|
276
290
|
|
|
277
291
|
{/* Fleet Status */}
|
|
278
292
|
<Card>
|