@burdenoff/microfe-movethewheels 2026.510.105
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/README.md +82 -0
- package/dist/AIAssistantPage-hD0VYJdH.js +210 -0
- package/dist/AnalyticsPage-DHTHCUtr.js +201 -0
- package/dist/CreateOrderPage-Cprg4Y9V.js +471 -0
- package/dist/CustomerDetailsPage-DNDEw7IW.js +239 -0
- package/dist/CustomersPage-CDjjeCEL.js +119 -0
- package/dist/DashboardPage-8iTPXRAG.js +374 -0
- package/dist/DataTable-CRIKfdIN.js +239 -0
- package/dist/DriverDetailsPage-CRyRCno7.js +297 -0
- package/dist/DriversPage-16O8fVmf.js +127 -0
- package/dist/FinancePage-BYUxK5dR.js +154 -0
- package/dist/FleetPage-CHYETCWT.js +293 -0
- package/dist/ImportExportPage-C3MKKxfc.js +232 -0
- package/dist/InventoryPage--822AxZM.js +223 -0
- package/dist/LiveTrackingPage-Dp3rTJDr.js +332 -0
- package/dist/MarketplacePage-DjEqudfM.js +192 -0
- package/dist/MetricCard-GTbxAk1a.js +135 -0
- package/dist/OrderDetailsPage-BIuYG0ub.js +398 -0
- package/dist/OrdersListPage-CW5V0Uvh.js +257 -0
- package/dist/PageLayout-B7b0vl0R.js +1894 -0
- package/dist/ProductDetailsPage-Q3X7AT-7.js +168 -0
- package/dist/ProductsPage-CUj9JpnW.js +131 -0
- package/dist/ReportsPage-DblO5CdJ.js +227 -0
- package/dist/RouteDetailsPage-CLctgk6A.js +240 -0
- package/dist/RoutesPage-8hrv6RWT.js +116 -0
- package/dist/SettingsPage-BJ5BQeqn.js +247 -0
- package/dist/StatusBadge-BrrwraIA.js +206 -0
- package/dist/TrackingPage-BGqHDh-w.js +322 -0
- package/dist/VehicleDetailsPage-XnDH4iQR.js +194 -0
- package/dist/VehiclesPage-Cs4XxHkA.js +127 -0
- package/dist/WarehouseDetailsPage-GemdMvr_.js +215 -0
- package/dist/WarehousesPage-QTiuDuXy.js +121 -0
- package/dist/arrow-left-6CiLhqVp.js +11 -0
- package/dist/box-BunB_4UH.js +18 -0
- package/dist/chart-column-DWwVEVQ-.js +22 -0
- package/dist/chevron-right-DhZVf20o.js +8 -0
- package/dist/circle-alert-D5f6RZxt.js +26 -0
- package/dist/circle-check-big-D-JMHcTe.js +11 -0
- package/dist/clock-CvwBKbQP.js +13 -0
- package/dist/dev/main.d.ts +1 -0
- package/dist/dollar-sign-CP9qeU5d.js +14 -0
- package/dist/download-CIuG04pJ.js +21 -0
- package/dist/file-text-Dd_thxkn.js +26 -0
- package/dist/filter-DyRMX9CU.js +8 -0
- package/dist/formatters-_vJlC-47.js +50 -0
- package/dist/generated/global-operations.d.ts +1 -0
- package/dist/generated/global-types.d.ts +20715 -0
- package/dist/generated/wspace-operations.d.ts +3704 -0
- package/dist/generated/wspace-types.d.ts +53362 -0
- package/dist/graphqlClient-CdJyR_ed.js +55 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +772 -0
- package/dist/map-BqH1cBJi.js +18 -0
- package/dist/map-pin-CFBOmh-A.js +13 -0
- package/dist/movethewheels/MoveTheWheelsRoot.d.ts +25 -0
- package/dist/movethewheels/MoveTheWheelsRoutes.d.ts +7 -0
- package/dist/movethewheels/components/DataTable.d.ts +32 -0
- package/dist/movethewheels/components/MetricCard.d.ts +43 -0
- package/dist/movethewheels/components/PageLayout.d.ts +68 -0
- package/dist/movethewheels/components/StatusBadge.d.ts +49 -0
- package/dist/movethewheels/components/index.d.ts +10 -0
- package/dist/movethewheels/components/ui.d.ts +22 -0
- package/dist/movethewheels/constants/index.d.ts +24 -0
- package/dist/movethewheels/constants/mockData.d.ts +33 -0
- package/dist/movethewheels/hooks/index.d.ts +12 -0
- package/dist/movethewheels/hooks/useAnalytics.d.ts +118 -0
- package/dist/movethewheels/hooks/useCustomers.d.ts +37 -0
- package/dist/movethewheels/hooks/useFleet.d.ts +71 -0
- package/dist/movethewheels/hooks/useInventory.d.ts +60 -0
- package/dist/movethewheels/hooks/useOrders.d.ts +47 -0
- package/dist/movethewheels/hooks/useRoutes.d.ts +41 -0
- package/dist/movethewheels/hooks/useTracking.d.ts +69 -0
- package/dist/movethewheels/index.d.ts +30 -0
- package/dist/movethewheels/pages/AIAssistantPage.d.ts +4 -0
- package/dist/movethewheels/pages/AnalyticsPage.d.ts +4 -0
- package/dist/movethewheels/pages/CreateOrderPage.d.ts +6 -0
- package/dist/movethewheels/pages/CustomerDetailsPage.d.ts +4 -0
- package/dist/movethewheels/pages/CustomersPage.d.ts +4 -0
- package/dist/movethewheels/pages/DashboardPage.d.ts +6 -0
- package/dist/movethewheels/pages/DriverDetailsPage.d.ts +4 -0
- package/dist/movethewheels/pages/DriversPage.d.ts +4 -0
- package/dist/movethewheels/pages/FinancePage.d.ts +4 -0
- package/dist/movethewheels/pages/FleetPage.d.ts +6 -0
- package/dist/movethewheels/pages/ImportExportPage.d.ts +4 -0
- package/dist/movethewheels/pages/InventoryPage.d.ts +4 -0
- package/dist/movethewheels/pages/LiveTrackingPage.d.ts +6 -0
- package/dist/movethewheels/pages/MarketplacePage.d.ts +4 -0
- package/dist/movethewheels/pages/OrderDetailsPage.d.ts +6 -0
- package/dist/movethewheels/pages/OrdersListPage.d.ts +6 -0
- package/dist/movethewheels/pages/ProductDetailsPage.d.ts +4 -0
- package/dist/movethewheels/pages/ProductsPage.d.ts +4 -0
- package/dist/movethewheels/pages/ReportsPage.d.ts +4 -0
- package/dist/movethewheels/pages/RouteDetailsPage.d.ts +4 -0
- package/dist/movethewheels/pages/RoutesPage.d.ts +4 -0
- package/dist/movethewheels/pages/SettingsPage.d.ts +4 -0
- package/dist/movethewheels/pages/TrackingPage.d.ts +6 -0
- package/dist/movethewheels/pages/VehicleDetailsPage.d.ts +4 -0
- package/dist/movethewheels/pages/VehiclesPage.d.ts +4 -0
- package/dist/movethewheels/pages/WarehouseDetailsPage.d.ts +4 -0
- package/dist/movethewheels/pages/WarehousesPage.d.ts +4 -0
- package/dist/movethewheels/providers/MoveTheWheelsProvider.d.ts +16 -0
- package/dist/movethewheels/store/movethewheelsStore.d.ts +73 -0
- package/dist/movethewheels/types/index.d.ts +655 -0
- package/dist/movethewheels/utils/cn.d.ts +6 -0
- package/dist/movethewheels/utils/formatters.d.ts +60 -0
- package/dist/movethewheels/utils/graphqlClient.d.ts +11 -0
- package/dist/movethewheels/utils/index.d.ts +7 -0
- package/dist/movethewheels/utils/navigation.d.ts +23 -0
- package/dist/navigation-BgnOfsVd.js +6 -0
- package/dist/navigation-C2fY_aS9.js +8 -0
- package/dist/package-DVZbDRcV.js +22 -0
- package/dist/phone-KdwpVmC4.js +18 -0
- package/dist/plus-Bl7uX6Ji.js +11 -0
- package/dist/refresh-cw-BYjl3K-8.js +22 -0
- package/dist/route-Ce_poKFi.js +51 -0
- package/dist/save-C-qDVat-.js +18 -0
- package/dist/search-5pdn5eOO.js +13 -0
- package/dist/settings-C4kIDsYg.js +28 -0
- package/dist/square-pen-BwQ67vLE.js +11 -0
- package/dist/star-BlVsC3Ad.js +8 -0
- package/dist/store-DTmQT5M0.js +26 -0
- package/dist/trending-up-C1faflCI.js +11 -0
- package/dist/triangle-alert-CUoVAA4L.js +18 -0
- package/dist/truck-BmDAzu05.js +30 -0
- package/dist/useAnalytics-ph7eTIK6.js +297 -0
- package/dist/useCustomers-bS3a4ytk.js +186 -0
- package/dist/useFleet-BdETplNE.js +398 -0
- package/dist/useInventory-Dwn18FPz.js +323 -0
- package/dist/useOrders-D_3_hGMp.js +324 -0
- package/dist/useRoutes-v4aBaS-E.js +224 -0
- package/dist/useTracking-De2KIUNu.js +261 -0
- package/dist/user-BplzDrLP.js +13 -0
- package/dist/users-i-igmsP4.js +24 -0
- package/dist/warehouse-DewG0PXh.js +25 -0
- package/dist/wrench-CoSDEIC7.js +31 -0
- package/package.json +107 -0
- package/src/dev/main.tsx +110 -0
- package/src/dev/styles.css +139 -0
- package/src/generated/global-operations.ts +2 -0
- package/src/generated/global-types.ts +24048 -0
- package/src/generated/wspace-operations.ts +3734 -0
- package/src/generated/wspace-types.ts +60715 -0
- package/src/index.ts +4 -0
- package/src/movethewheels/MoveTheWheelsRoot.tsx +258 -0
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +119 -0
- package/src/movethewheels/components/DataTable.tsx +367 -0
- package/src/movethewheels/components/MetricCard.tsx +180 -0
- package/src/movethewheels/components/PageLayout.tsx +234 -0
- package/src/movethewheels/components/StatusBadge.tsx +243 -0
- package/src/movethewheels/components/index.ts +26 -0
- package/src/movethewheels/components/ui.tsx +124 -0
- package/src/movethewheels/constants/index.ts +65 -0
- package/src/movethewheels/constants/mockData.ts +1342 -0
- package/src/movethewheels/hooks/index.ts +55 -0
- package/src/movethewheels/hooks/useAnalytics.ts +476 -0
- package/src/movethewheels/hooks/useCustomers.ts +359 -0
- package/src/movethewheels/hooks/useFleet.ts +778 -0
- package/src/movethewheels/hooks/useInventory.ts +632 -0
- package/src/movethewheels/hooks/useOrders.ts +703 -0
- package/src/movethewheels/hooks/useRoutes.ts +453 -0
- package/src/movethewheels/hooks/useTracking.ts +505 -0
- package/src/movethewheels/index.ts +68 -0
- package/src/movethewheels/pages/AIAssistantPage.tsx +160 -0
- package/src/movethewheels/pages/AnalyticsPage.tsx +190 -0
- package/src/movethewheels/pages/CreateOrderPage.tsx +454 -0
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +207 -0
- package/src/movethewheels/pages/CustomersPage.tsx +115 -0
- package/src/movethewheels/pages/DashboardPage.tsx +414 -0
- package/src/movethewheels/pages/DriverDetailsPage.tsx +261 -0
- package/src/movethewheels/pages/DriversPage.tsx +118 -0
- package/src/movethewheels/pages/FinancePage.tsx +141 -0
- package/src/movethewheels/pages/FleetPage.tsx +289 -0
- package/src/movethewheels/pages/ImportExportPage.tsx +165 -0
- package/src/movethewheels/pages/InventoryPage.tsx +212 -0
- package/src/movethewheels/pages/LiveTrackingPage.tsx +325 -0
- package/src/movethewheels/pages/MarketplacePage.tsx +235 -0
- package/src/movethewheels/pages/OrderDetailsPage.tsx +387 -0
- package/src/movethewheels/pages/OrdersListPage.tsx +241 -0
- package/src/movethewheels/pages/ProductDetailsPage.tsx +155 -0
- package/src/movethewheels/pages/ProductsPage.tsx +124 -0
- package/src/movethewheels/pages/ReportsPage.tsx +164 -0
- package/src/movethewheels/pages/RouteDetailsPage.tsx +245 -0
- package/src/movethewheels/pages/RoutesPage.tsx +104 -0
- package/src/movethewheels/pages/SettingsPage.tsx +242 -0
- package/src/movethewheels/pages/TrackingPage.tsx +419 -0
- package/src/movethewheels/pages/VehicleDetailsPage.tsx +218 -0
- package/src/movethewheels/pages/VehiclesPage.tsx +124 -0
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +216 -0
- package/src/movethewheels/pages/WarehousesPage.tsx +122 -0
- package/src/movethewheels/providers/MoveTheWheelsProvider.tsx +66 -0
- package/src/movethewheels/store/movethewheelsStore.ts +136 -0
- package/src/movethewheels/types/index.ts +744 -0
- package/src/movethewheels/utils/cn.ts +9 -0
- package/src/movethewheels/utils/formatters.ts +215 -0
- package/src/movethewheels/utils/graphqlClient.ts +63 -0
- package/src/movethewheels/utils/index.ts +8 -0
- package/src/movethewheels/utils/navigation.ts +70 -0
- package/src/operations/global/.gitkeep +0 -0
- package/src/operations/wspace/movethewheels/fragments/core.graphql +191 -0
- package/src/operations/wspace/movethewheels/mutations/entities.graphql +87 -0
- package/src/operations/wspace/movethewheels/mutations/logistics.graphql +86 -0
- package/src/operations/wspace/movethewheels/mutations/marketplace-reports.graphql +81 -0
- package/src/operations/wspace/movethewheels/mutations/orders.graphql +21 -0
- package/src/operations/wspace/movethewheels/queries/dashboard.graphql +61 -0
- package/src/operations/wspace/movethewheels/queries/entities.graphql +83 -0
- package/src/operations/wspace/movethewheels/queries/logistics.graphql +84 -0
- package/src/operations/wspace/movethewheels/queries/marketplace-reports.graphql +40 -0
- package/src/operations/wspace/movethewheels/queries/orders.graphql +43 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracking Page
|
|
3
|
+
*
|
|
4
|
+
* Track orders by tracking number or order ID.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useState } from 'react';
|
|
8
|
+
import { useSearchParams } from 'react-router-dom';
|
|
9
|
+
import { MapPin, Search, Package, Truck, Clock, CheckCircle, AlertCircle } from 'lucide-react';
|
|
10
|
+
import { PageLayout, EmptyState } from '../components/PageLayout';
|
|
11
|
+
import { StatusBadge } from '../components/StatusBadge';
|
|
12
|
+
import { Card, CardContent, CardHeader, CardTitle, Button, Input } from '../components/ui';
|
|
13
|
+
import { useOrderTracking } from '../hooks/useTracking';
|
|
14
|
+
import { formatDate, formatAddress } from '../utils/formatters';
|
|
15
|
+
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
16
|
+
import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';
|
|
17
|
+
import { joinPath } from '../utils/navigation';
|
|
18
|
+
|
|
19
|
+
export default function TrackingPage() {
|
|
20
|
+
const { t: translate } = useI18n();
|
|
21
|
+
const t = (key: string, fallback: string): string => {
|
|
22
|
+
const value = translate(key);
|
|
23
|
+
return value === key ? fallback : value;
|
|
24
|
+
};
|
|
25
|
+
const { basePath, navigate } = useMoveTheWheels();
|
|
26
|
+
const [searchParams] = useSearchParams();
|
|
27
|
+
const initialOrderId = searchParams.get('order') || '';
|
|
28
|
+
const [trackingInput, setTrackingInput] = useState(initialOrderId);
|
|
29
|
+
const [searchedOrderId, setSearchedOrderId] = useState(initialOrderId);
|
|
30
|
+
|
|
31
|
+
const { tracking, order, isLoading, error } = useOrderTracking(searchedOrderId || undefined);
|
|
32
|
+
|
|
33
|
+
const handleSearch = (e: React.FormEvent) => {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
setSearchedOrderId(trackingInput);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const goTo = (path: string) => {
|
|
39
|
+
if (navigate) {
|
|
40
|
+
navigate(joinPath(basePath, path));
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<PageLayout
|
|
46
|
+
title={t('movethewheels.tracking.title', 'Track Shipment')}
|
|
47
|
+
subtitle={t(
|
|
48
|
+
'movethewheels.tracking.subtitle',
|
|
49
|
+
'Enter a tracking number or order ID to track your shipment'
|
|
50
|
+
)}
|
|
51
|
+
icon={<MapPin size={24} />}
|
|
52
|
+
headerActions={
|
|
53
|
+
<Button variant="outline" onClick={() => goTo('tracking/live')}>
|
|
54
|
+
<MapPin size={16} />
|
|
55
|
+
{t('movethewheels.tracking.actions.liveMapView', 'Live Map View')}
|
|
56
|
+
</Button>
|
|
57
|
+
}
|
|
58
|
+
>
|
|
59
|
+
{/* Search Form */}
|
|
60
|
+
<Card>
|
|
61
|
+
<CardContent className="p-6">
|
|
62
|
+
<form onSubmit={handleSearch} className="flex gap-4">
|
|
63
|
+
<div className="flex-1 relative">
|
|
64
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-muted-foreground" />
|
|
65
|
+
<Input
|
|
66
|
+
value={trackingInput}
|
|
67
|
+
onChange={(e) => setTrackingInput(e.target.value)}
|
|
68
|
+
placeholder={t(
|
|
69
|
+
'movethewheels.tracking.search.placeholder',
|
|
70
|
+
'Enter tracking number or order ID (e.g., order-1 or TRK123456789)'
|
|
71
|
+
)}
|
|
72
|
+
className="pl-10 h-12 text-lg"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
<Button type="submit" size="lg" disabled={!trackingInput || isLoading}>
|
|
76
|
+
{isLoading
|
|
77
|
+
? t('movethewheels.tracking.search.searching', 'Searching...')
|
|
78
|
+
: t('movethewheels.tracking.search.track', 'Track')}
|
|
79
|
+
</Button>
|
|
80
|
+
</form>
|
|
81
|
+
</CardContent>
|
|
82
|
+
</Card>
|
|
83
|
+
|
|
84
|
+
{/* Error State */}
|
|
85
|
+
{error && (
|
|
86
|
+
<Card className="border-destructive">
|
|
87
|
+
<CardContent className="p-6">
|
|
88
|
+
<div className="flex items-center gap-4">
|
|
89
|
+
<AlertCircle className="w-8 h-8 text-destructive" />
|
|
90
|
+
<div>
|
|
91
|
+
<p className="font-medium text-destructive">
|
|
92
|
+
{t('movethewheels.tracking.error.title', 'Unable to find shipment')}
|
|
93
|
+
</p>
|
|
94
|
+
<p className="text-sm text-muted-foreground">
|
|
95
|
+
{t(
|
|
96
|
+
'movethewheels.tracking.error.description',
|
|
97
|
+
'Please check the tracking number and try again'
|
|
98
|
+
)}
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</CardContent>
|
|
103
|
+
</Card>
|
|
104
|
+
)}
|
|
105
|
+
|
|
106
|
+
{/* No Search Yet */}
|
|
107
|
+
{!searchedOrderId && !error && (
|
|
108
|
+
<EmptyState
|
|
109
|
+
icon={<Package size={48} />}
|
|
110
|
+
title={t('movethewheels.tracking.empty.title', 'Enter a tracking number')}
|
|
111
|
+
description={t(
|
|
112
|
+
'movethewheels.tracking.empty.description',
|
|
113
|
+
'Use your tracking number or order ID to see the current status of your shipment'
|
|
114
|
+
)}
|
|
115
|
+
/>
|
|
116
|
+
)}
|
|
117
|
+
|
|
118
|
+
{/* Tracking Results */}
|
|
119
|
+
{order && tracking && (
|
|
120
|
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
121
|
+
{/* Main Tracking Info */}
|
|
122
|
+
<div className="lg:col-span-2 space-y-6">
|
|
123
|
+
{/* Status Card */}
|
|
124
|
+
<Card>
|
|
125
|
+
<CardHeader>
|
|
126
|
+
<div className="flex items-center justify-between">
|
|
127
|
+
<CardTitle className="flex items-center gap-2">
|
|
128
|
+
<Package size={20} />
|
|
129
|
+
{t('movethewheels.tracking.orderLabel', 'Order')} {order.orderNumber}
|
|
130
|
+
</CardTitle>
|
|
131
|
+
<StatusBadge status={order.status} type="order" />
|
|
132
|
+
</div>
|
|
133
|
+
</CardHeader>
|
|
134
|
+
<CardContent>
|
|
135
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
136
|
+
<div>
|
|
137
|
+
<p className="text-sm text-muted-foreground">
|
|
138
|
+
{t('movethewheels.tracking.details.trackingNumber', 'Tracking Number')}
|
|
139
|
+
</p>
|
|
140
|
+
<p className="font-mono font-medium">{order.trackingNumber}</p>
|
|
141
|
+
</div>
|
|
142
|
+
<div>
|
|
143
|
+
<p className="text-sm text-muted-foreground">
|
|
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-muted-foreground">
|
|
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-muted-foreground">
|
|
165
|
+
{t('movethewheels.tracking.details.lastUpdated', 'Last Updated')}
|
|
166
|
+
</p>
|
|
167
|
+
<p className="font-medium">{formatDate(tracking.lastUpdated, 'relative')}</p>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</CardContent>
|
|
171
|
+
</Card>
|
|
172
|
+
|
|
173
|
+
{/* Progress Timeline */}
|
|
174
|
+
<Card>
|
|
175
|
+
<CardHeader>
|
|
176
|
+
<CardTitle className="flex items-center gap-2">
|
|
177
|
+
<Clock size={18} />
|
|
178
|
+
{t('movethewheels.tracking.progress.title', 'Shipment Progress')}
|
|
179
|
+
</CardTitle>
|
|
180
|
+
</CardHeader>
|
|
181
|
+
<CardContent>
|
|
182
|
+
<div className="relative">
|
|
183
|
+
{/* Progress Steps */}
|
|
184
|
+
<div className="flex justify-between mb-4">
|
|
185
|
+
{[
|
|
186
|
+
{
|
|
187
|
+
status: 'confirmed',
|
|
188
|
+
label: t('movethewheels.tracking.progress.confirmed', 'Confirmed'),
|
|
189
|
+
icon: CheckCircle,
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
status: 'picked_up',
|
|
193
|
+
label: t('movethewheels.tracking.progress.pickedUp', 'Picked Up'),
|
|
194
|
+
icon: Package,
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
status: 'in_transit',
|
|
198
|
+
label: t('movethewheels.tracking.progress.inTransit', 'In Transit'),
|
|
199
|
+
icon: Truck,
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
status: 'delivered',
|
|
203
|
+
label: t('movethewheels.tracking.progress.delivered', 'Delivered'),
|
|
204
|
+
icon: MapPin,
|
|
205
|
+
},
|
|
206
|
+
].map((step, index) => {
|
|
207
|
+
const statusOrder = [
|
|
208
|
+
'pending',
|
|
209
|
+
'confirmed',
|
|
210
|
+
'assigned',
|
|
211
|
+
'picked_up',
|
|
212
|
+
'in_transit',
|
|
213
|
+
'out_for_delivery',
|
|
214
|
+
'delivered',
|
|
215
|
+
];
|
|
216
|
+
const currentIndex = statusOrder.indexOf(order.status);
|
|
217
|
+
const stepIndex = statusOrder.indexOf(step.status);
|
|
218
|
+
const isComplete = stepIndex <= currentIndex;
|
|
219
|
+
const isCurrent =
|
|
220
|
+
step.status === order.status ||
|
|
221
|
+
(order.status === 'out_for_delivery' && step.status === 'in_transit');
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<div key={step.status} className="flex flex-col items-center flex-1">
|
|
225
|
+
<div
|
|
226
|
+
className={`w-10 h-10 rounded-full flex items-center justify-center ${
|
|
227
|
+
isComplete
|
|
228
|
+
? 'bg-primary text-primary-foreground'
|
|
229
|
+
: 'bg-muted text-muted-foreground'
|
|
230
|
+
} ${isCurrent ? 'ring-4 ring-primary/20' : ''}`}
|
|
231
|
+
>
|
|
232
|
+
<step.icon size={20} />
|
|
233
|
+
</div>
|
|
234
|
+
<p
|
|
235
|
+
className={`text-xs mt-2 text-center ${isComplete ? 'font-medium' : 'text-muted-foreground'}`}
|
|
236
|
+
>
|
|
237
|
+
{step.label}
|
|
238
|
+
</p>
|
|
239
|
+
{index < 3 && (
|
|
240
|
+
<div
|
|
241
|
+
className={`absolute top-5 h-0.5 ${
|
|
242
|
+
isComplete ? 'bg-primary' : 'bg-muted'
|
|
243
|
+
}`}
|
|
244
|
+
style={{
|
|
245
|
+
left: `calc(${(index + 0.5) * 25}% + 20px)`,
|
|
246
|
+
width: 'calc(25% - 40px)',
|
|
247
|
+
}}
|
|
248
|
+
/>
|
|
249
|
+
)}
|
|
250
|
+
</div>
|
|
251
|
+
);
|
|
252
|
+
})}
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
{/* Timeline Events */}
|
|
257
|
+
<div className="mt-8 space-y-4">
|
|
258
|
+
{order.timeline
|
|
259
|
+
.slice()
|
|
260
|
+
.reverse()
|
|
261
|
+
.map((event, index) => (
|
|
262
|
+
<div key={index} className="flex gap-4">
|
|
263
|
+
<div className="relative">
|
|
264
|
+
<div
|
|
265
|
+
className={`w-3 h-3 rounded-full ${index === 0 ? 'bg-primary' : 'bg-muted'}`}
|
|
266
|
+
/>
|
|
267
|
+
{index < order.timeline.length - 1 && (
|
|
268
|
+
<div className="absolute top-3 left-1.5 w-px h-8 -ml-px bg-border" />
|
|
269
|
+
)}
|
|
270
|
+
</div>
|
|
271
|
+
<div className="flex-1 pb-4">
|
|
272
|
+
<div className="flex items-center gap-2">
|
|
273
|
+
<StatusBadge status={event.status} type="order" size="sm" />
|
|
274
|
+
<span className="text-sm text-muted-foreground">
|
|
275
|
+
{formatDate(event.timestamp, 'full')}
|
|
276
|
+
</span>
|
|
277
|
+
</div>
|
|
278
|
+
{event.notes && <p className="text-sm mt-1">{event.notes}</p>}
|
|
279
|
+
{event.location && (
|
|
280
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
281
|
+
<MapPin size={12} className="inline mr-1" />
|
|
282
|
+
{formatAddress(event.location)}
|
|
283
|
+
</p>
|
|
284
|
+
)}
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
))}
|
|
288
|
+
</div>
|
|
289
|
+
</CardContent>
|
|
290
|
+
</Card>
|
|
291
|
+
|
|
292
|
+
{/* Alerts */}
|
|
293
|
+
{tracking.alerts && tracking.alerts.length > 0 && (
|
|
294
|
+
<Card>
|
|
295
|
+
<CardHeader>
|
|
296
|
+
<CardTitle className="flex items-center gap-2">
|
|
297
|
+
<AlertCircle size={18} />
|
|
298
|
+
{t('movethewheels.tracking.alerts.title', 'Alerts')}
|
|
299
|
+
</CardTitle>
|
|
300
|
+
</CardHeader>
|
|
301
|
+
<CardContent>
|
|
302
|
+
<div className="space-y-3">
|
|
303
|
+
{tracking.alerts.map((alert) => (
|
|
304
|
+
<div
|
|
305
|
+
key={alert.id}
|
|
306
|
+
className={`flex items-start gap-3 p-3 rounded-lg ${
|
|
307
|
+
alert.severity === 'critical'
|
|
308
|
+
? 'bg-red-50 dark:bg-red-900/20'
|
|
309
|
+
: alert.severity === 'high'
|
|
310
|
+
? 'bg-orange-50 dark:bg-orange-900/20'
|
|
311
|
+
: 'bg-yellow-50 dark:bg-yellow-900/20'
|
|
312
|
+
}`}
|
|
313
|
+
>
|
|
314
|
+
<AlertCircle
|
|
315
|
+
size={16}
|
|
316
|
+
className={
|
|
317
|
+
alert.severity === 'critical'
|
|
318
|
+
? 'text-red-600'
|
|
319
|
+
: alert.severity === 'high'
|
|
320
|
+
? 'text-orange-600'
|
|
321
|
+
: 'text-yellow-600'
|
|
322
|
+
}
|
|
323
|
+
/>
|
|
324
|
+
<div>
|
|
325
|
+
<p className="text-sm font-medium">{alert.message}</p>
|
|
326
|
+
<p className="text-xs text-muted-foreground">
|
|
327
|
+
{formatDate(alert.timestamp, 'relative')}
|
|
328
|
+
{alert.resolvedAt &&
|
|
329
|
+
` - ${t('movethewheels.tracking.alerts.resolved', 'Resolved')}`}
|
|
330
|
+
</p>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
))}
|
|
334
|
+
</div>
|
|
335
|
+
</CardContent>
|
|
336
|
+
</Card>
|
|
337
|
+
)}
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
{/* Sidebar */}
|
|
341
|
+
<div className="space-y-6">
|
|
342
|
+
{/* Current Location */}
|
|
343
|
+
<Card>
|
|
344
|
+
<CardHeader>
|
|
345
|
+
<CardTitle className="flex items-center gap-2">
|
|
346
|
+
<MapPin size={18} />
|
|
347
|
+
{t('movethewheels.tracking.location.title', 'Current Location')}
|
|
348
|
+
</CardTitle>
|
|
349
|
+
</CardHeader>
|
|
350
|
+
<CardContent>
|
|
351
|
+
<div className="aspect-video bg-muted rounded-lg flex items-center justify-center mb-4">
|
|
352
|
+
<div className="text-center text-muted-foreground">
|
|
353
|
+
<MapPin size={32} className="mx-auto mb-2" />
|
|
354
|
+
<p className="text-sm">
|
|
355
|
+
{t('movethewheels.tracking.location.mapView', 'Map View')}
|
|
356
|
+
</p>
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
<p className="text-sm">
|
|
360
|
+
{tracking.currentLocation.address ||
|
|
361
|
+
t('movethewheels.tracking.location.updating', 'Location updating...')}
|
|
362
|
+
</p>
|
|
363
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
364
|
+
{t('movethewheels.tracking.location.updated', 'Updated')}{' '}
|
|
365
|
+
{formatDate(tracking.currentLocation.timestamp || new Date(), 'relative')}
|
|
366
|
+
</p>
|
|
367
|
+
</CardContent>
|
|
368
|
+
</Card>
|
|
369
|
+
|
|
370
|
+
{/* Delivery Address */}
|
|
371
|
+
<Card>
|
|
372
|
+
<CardHeader>
|
|
373
|
+
<CardTitle>
|
|
374
|
+
{t('movethewheels.tracking.deliveryAddress.title', 'Delivery Address')}
|
|
375
|
+
</CardTitle>
|
|
376
|
+
</CardHeader>
|
|
377
|
+
<CardContent>
|
|
378
|
+
<p className="text-sm">{order.customerInfo.name}</p>
|
|
379
|
+
<p className="text-sm text-muted-foreground mt-2">
|
|
380
|
+
{formatAddress(order.deliveryAddress)}
|
|
381
|
+
</p>
|
|
382
|
+
</CardContent>
|
|
383
|
+
</Card>
|
|
384
|
+
|
|
385
|
+
{/* Actions */}
|
|
386
|
+
<Card>
|
|
387
|
+
<CardContent className="p-4 space-y-2">
|
|
388
|
+
<Button
|
|
389
|
+
variant="outline"
|
|
390
|
+
className="w-full"
|
|
391
|
+
onClick={() => goTo(`orders/${order.id}`)}
|
|
392
|
+
>
|
|
393
|
+
{t('movethewheels.tracking.actions.viewOrderDetails', 'View Order Details')}
|
|
394
|
+
</Button>
|
|
395
|
+
<Button variant="outline" className="w-full">
|
|
396
|
+
{t('movethewheels.tracking.actions.contactDriver', 'Contact Driver')}
|
|
397
|
+
</Button>
|
|
398
|
+
</CardContent>
|
|
399
|
+
</Card>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
)}
|
|
403
|
+
|
|
404
|
+
{/* No Results */}
|
|
405
|
+
{searchedOrderId && !order && !isLoading && !error && (
|
|
406
|
+
<EmptyState
|
|
407
|
+
icon={<Search size={48} />}
|
|
408
|
+
title={t('movethewheels.tracking.notFound.title', 'Shipment not found')}
|
|
409
|
+
description={`${t('movethewheels.tracking.notFound.descriptionPrefix', 'No shipment found with tracking number')} "${searchedOrderId}"`}
|
|
410
|
+
action={
|
|
411
|
+
<Button variant="outline" onClick={() => setTrackingInput('')}>
|
|
412
|
+
{t('movethewheels.tracking.notFound.tryAnother', 'Try another number')}
|
|
413
|
+
</Button>
|
|
414
|
+
}
|
|
415
|
+
/>
|
|
416
|
+
)}
|
|
417
|
+
</PageLayout>
|
|
418
|
+
);
|
|
419
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vehicle Details Page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useParams } from 'react-router-dom';
|
|
6
|
+
import { Truck, ArrowLeft, MapPin, Fuel, Wrench, FileText, Edit } from 'lucide-react';
|
|
7
|
+
import { PageLayout, LoadingState, ErrorState } from '../components/PageLayout';
|
|
8
|
+
import { StatusBadge, VehicleTypeBadge } from '../components/StatusBadge';
|
|
9
|
+
import {
|
|
10
|
+
Card,
|
|
11
|
+
CardContent,
|
|
12
|
+
CardHeader,
|
|
13
|
+
CardTitle,
|
|
14
|
+
Button,
|
|
15
|
+
Badge,
|
|
16
|
+
Progress,
|
|
17
|
+
} from '../components/ui';
|
|
18
|
+
import { useVehicle } from '../hooks/useFleet';
|
|
19
|
+
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
20
|
+
import { formatDate } from '../utils/formatters';
|
|
21
|
+
import { joinPath } from '../utils/navigation';
|
|
22
|
+
|
|
23
|
+
export default function VehicleDetailsPage() {
|
|
24
|
+
const { vehicleId } = useParams<{ vehicleId: string }>();
|
|
25
|
+
const { basePath, navigate } = useMoveTheWheels();
|
|
26
|
+
const { vehicle, isLoading, error, refetch } = useVehicle(vehicleId);
|
|
27
|
+
|
|
28
|
+
const goBack = () => navigate?.(joinPath(basePath, 'fleet/vehicles'));
|
|
29
|
+
|
|
30
|
+
if (isLoading) return <LoadingState message="Loading vehicle details..." />;
|
|
31
|
+
if (error || !vehicle) return <ErrorState title="Vehicle not found" onRetry={refetch} />;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<PageLayout
|
|
35
|
+
title={vehicle.vehicleNumber}
|
|
36
|
+
subtitle={`${vehicle.make} ${vehicle.model} (${vehicle.year})`}
|
|
37
|
+
icon={<Truck size={24} />}
|
|
38
|
+
headerActions={
|
|
39
|
+
<div className="flex gap-2">
|
|
40
|
+
<Button variant="outline" onClick={goBack}>
|
|
41
|
+
<ArrowLeft size={16} /> Back
|
|
42
|
+
</Button>
|
|
43
|
+
<Button>
|
|
44
|
+
<Edit size={16} /> Edit
|
|
45
|
+
</Button>
|
|
46
|
+
</div>
|
|
47
|
+
}
|
|
48
|
+
headerContent={
|
|
49
|
+
<div className="flex gap-2">
|
|
50
|
+
<StatusBadge status={vehicle.status} type="vehicle" />
|
|
51
|
+
<VehicleTypeBadge type={vehicle.type} />
|
|
52
|
+
</div>
|
|
53
|
+
}
|
|
54
|
+
>
|
|
55
|
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
56
|
+
<div className="lg:col-span-2 space-y-6">
|
|
57
|
+
<Card>
|
|
58
|
+
<CardHeader>
|
|
59
|
+
<CardTitle>Vehicle Information</CardTitle>
|
|
60
|
+
</CardHeader>
|
|
61
|
+
<CardContent>
|
|
62
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
|
63
|
+
<div>
|
|
64
|
+
<p className="text-sm text-muted-foreground">Type</p>
|
|
65
|
+
<p className="font-medium capitalize">{vehicle.type}</p>
|
|
66
|
+
</div>
|
|
67
|
+
<div>
|
|
68
|
+
<p className="text-sm text-muted-foreground">Make</p>
|
|
69
|
+
<p className="font-medium">{vehicle.make}</p>
|
|
70
|
+
</div>
|
|
71
|
+
<div>
|
|
72
|
+
<p className="text-sm text-muted-foreground">Model</p>
|
|
73
|
+
<p className="font-medium">{vehicle.model}</p>
|
|
74
|
+
</div>
|
|
75
|
+
<div>
|
|
76
|
+
<p className="text-sm text-muted-foreground">Year</p>
|
|
77
|
+
<p className="font-medium">{vehicle.year}</p>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</CardContent>
|
|
81
|
+
</Card>
|
|
82
|
+
|
|
83
|
+
<Card>
|
|
84
|
+
<CardHeader>
|
|
85
|
+
<CardTitle>Capacity</CardTitle>
|
|
86
|
+
</CardHeader>
|
|
87
|
+
<CardContent>
|
|
88
|
+
<div className="grid grid-cols-3 gap-6">
|
|
89
|
+
<div>
|
|
90
|
+
<p className="text-sm text-muted-foreground">Weight</p>
|
|
91
|
+
<p className="text-2xl font-bold">
|
|
92
|
+
{vehicle.capacity.weight}
|
|
93
|
+
<span className="text-sm font-normal"> kg</span>
|
|
94
|
+
</p>
|
|
95
|
+
</div>
|
|
96
|
+
<div>
|
|
97
|
+
<p className="text-sm text-muted-foreground">Volume</p>
|
|
98
|
+
<p className="text-2xl font-bold">
|
|
99
|
+
{vehicle.capacity.volume}
|
|
100
|
+
<span className="text-sm font-normal"> m³</span>
|
|
101
|
+
</p>
|
|
102
|
+
</div>
|
|
103
|
+
{vehicle.capacity.pallets && (
|
|
104
|
+
<div>
|
|
105
|
+
<p className="text-sm text-muted-foreground">Pallets</p>
|
|
106
|
+
<p className="text-2xl font-bold">{vehicle.capacity.pallets}</p>
|
|
107
|
+
</div>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
110
|
+
</CardContent>
|
|
111
|
+
</Card>
|
|
112
|
+
|
|
113
|
+
<Card>
|
|
114
|
+
<CardHeader>
|
|
115
|
+
<CardTitle className="flex items-center gap-2">
|
|
116
|
+
<FileText size={18} /> Documents
|
|
117
|
+
</CardTitle>
|
|
118
|
+
</CardHeader>
|
|
119
|
+
<CardContent>
|
|
120
|
+
{vehicle.documents.length > 0 ? (
|
|
121
|
+
<div className="space-y-3">
|
|
122
|
+
{vehicle.documents.map((doc) => (
|
|
123
|
+
<div
|
|
124
|
+
key={doc.id}
|
|
125
|
+
className="flex items-center justify-between p-3 border rounded-lg"
|
|
126
|
+
>
|
|
127
|
+
<div>
|
|
128
|
+
<p className="font-medium capitalize">{doc.type.replace('_', ' ')}</p>
|
|
129
|
+
<p className="text-sm text-muted-foreground">
|
|
130
|
+
Expires: {formatDate(doc.expiryDate)}
|
|
131
|
+
</p>
|
|
132
|
+
</div>
|
|
133
|
+
<Badge variant={doc.verified ? 'default' : 'secondary'}>
|
|
134
|
+
{doc.verified ? 'Verified' : 'Pending'}
|
|
135
|
+
</Badge>
|
|
136
|
+
</div>
|
|
137
|
+
))}
|
|
138
|
+
</div>
|
|
139
|
+
) : (
|
|
140
|
+
<p className="text-muted-foreground text-center py-4">No documents uploaded</p>
|
|
141
|
+
)}
|
|
142
|
+
</CardContent>
|
|
143
|
+
</Card>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div className="space-y-6">
|
|
147
|
+
<Card>
|
|
148
|
+
<CardHeader>
|
|
149
|
+
<CardTitle className="flex items-center gap-2">
|
|
150
|
+
<Fuel size={18} /> Fuel & Mileage
|
|
151
|
+
</CardTitle>
|
|
152
|
+
</CardHeader>
|
|
153
|
+
<CardContent className="space-y-4">
|
|
154
|
+
{vehicle.fuelLevel !== undefined && (
|
|
155
|
+
<div>
|
|
156
|
+
<div className="flex justify-between text-sm mb-2">
|
|
157
|
+
<span>Fuel Level</span>
|
|
158
|
+
<span>{vehicle.fuelLevel}%</span>
|
|
159
|
+
</div>
|
|
160
|
+
<Progress value={vehicle.fuelLevel} />
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
{vehicle.mileage !== undefined && (
|
|
164
|
+
<div>
|
|
165
|
+
<p className="text-sm text-muted-foreground">Total Mileage</p>
|
|
166
|
+
<p className="text-2xl font-bold">
|
|
167
|
+
{vehicle.mileage.toLocaleString()}{' '}
|
|
168
|
+
<span className="text-sm font-normal">miles</span>
|
|
169
|
+
</p>
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
172
|
+
</CardContent>
|
|
173
|
+
</Card>
|
|
174
|
+
|
|
175
|
+
<Card>
|
|
176
|
+
<CardHeader>
|
|
177
|
+
<CardTitle className="flex items-center gap-2">
|
|
178
|
+
<Wrench size={18} /> Maintenance
|
|
179
|
+
</CardTitle>
|
|
180
|
+
</CardHeader>
|
|
181
|
+
<CardContent className="space-y-3">
|
|
182
|
+
<div>
|
|
183
|
+
<p className="text-sm text-muted-foreground">Last Maintenance</p>
|
|
184
|
+
<p className="font-medium">
|
|
185
|
+
{vehicle.lastMaintenance ? formatDate(vehicle.lastMaintenance) : 'N/A'}
|
|
186
|
+
</p>
|
|
187
|
+
</div>
|
|
188
|
+
<div>
|
|
189
|
+
<p className="text-sm text-muted-foreground">Next Scheduled</p>
|
|
190
|
+
<p className="font-medium">
|
|
191
|
+
{vehicle.nextMaintenance ? formatDate(vehicle.nextMaintenance) : 'N/A'}
|
|
192
|
+
</p>
|
|
193
|
+
</div>
|
|
194
|
+
</CardContent>
|
|
195
|
+
</Card>
|
|
196
|
+
|
|
197
|
+
{vehicle.currentLocation && (
|
|
198
|
+
<Card>
|
|
199
|
+
<CardHeader>
|
|
200
|
+
<CardTitle className="flex items-center gap-2">
|
|
201
|
+
<MapPin size={18} /> Current Location
|
|
202
|
+
</CardTitle>
|
|
203
|
+
</CardHeader>
|
|
204
|
+
<CardContent>
|
|
205
|
+
<p className="text-sm">
|
|
206
|
+
{vehicle.currentLocation.address || 'Location updating...'}
|
|
207
|
+
</p>
|
|
208
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
209
|
+
Updated {formatDate(vehicle.currentLocation.timestamp || new Date(), 'relative')}
|
|
210
|
+
</p>
|
|
211
|
+
</CardContent>
|
|
212
|
+
</Card>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</PageLayout>
|
|
217
|
+
);
|
|
218
|
+
}
|