@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.
Files changed (208) hide show
  1. package/README.md +82 -0
  2. package/dist/AIAssistantPage-hD0VYJdH.js +210 -0
  3. package/dist/AnalyticsPage-DHTHCUtr.js +201 -0
  4. package/dist/CreateOrderPage-Cprg4Y9V.js +471 -0
  5. package/dist/CustomerDetailsPage-DNDEw7IW.js +239 -0
  6. package/dist/CustomersPage-CDjjeCEL.js +119 -0
  7. package/dist/DashboardPage-8iTPXRAG.js +374 -0
  8. package/dist/DataTable-CRIKfdIN.js +239 -0
  9. package/dist/DriverDetailsPage-CRyRCno7.js +297 -0
  10. package/dist/DriversPage-16O8fVmf.js +127 -0
  11. package/dist/FinancePage-BYUxK5dR.js +154 -0
  12. package/dist/FleetPage-CHYETCWT.js +293 -0
  13. package/dist/ImportExportPage-C3MKKxfc.js +232 -0
  14. package/dist/InventoryPage--822AxZM.js +223 -0
  15. package/dist/LiveTrackingPage-Dp3rTJDr.js +332 -0
  16. package/dist/MarketplacePage-DjEqudfM.js +192 -0
  17. package/dist/MetricCard-GTbxAk1a.js +135 -0
  18. package/dist/OrderDetailsPage-BIuYG0ub.js +398 -0
  19. package/dist/OrdersListPage-CW5V0Uvh.js +257 -0
  20. package/dist/PageLayout-B7b0vl0R.js +1894 -0
  21. package/dist/ProductDetailsPage-Q3X7AT-7.js +168 -0
  22. package/dist/ProductsPage-CUj9JpnW.js +131 -0
  23. package/dist/ReportsPage-DblO5CdJ.js +227 -0
  24. package/dist/RouteDetailsPage-CLctgk6A.js +240 -0
  25. package/dist/RoutesPage-8hrv6RWT.js +116 -0
  26. package/dist/SettingsPage-BJ5BQeqn.js +247 -0
  27. package/dist/StatusBadge-BrrwraIA.js +206 -0
  28. package/dist/TrackingPage-BGqHDh-w.js +322 -0
  29. package/dist/VehicleDetailsPage-XnDH4iQR.js +194 -0
  30. package/dist/VehiclesPage-Cs4XxHkA.js +127 -0
  31. package/dist/WarehouseDetailsPage-GemdMvr_.js +215 -0
  32. package/dist/WarehousesPage-QTiuDuXy.js +121 -0
  33. package/dist/arrow-left-6CiLhqVp.js +11 -0
  34. package/dist/box-BunB_4UH.js +18 -0
  35. package/dist/chart-column-DWwVEVQ-.js +22 -0
  36. package/dist/chevron-right-DhZVf20o.js +8 -0
  37. package/dist/circle-alert-D5f6RZxt.js +26 -0
  38. package/dist/circle-check-big-D-JMHcTe.js +11 -0
  39. package/dist/clock-CvwBKbQP.js +13 -0
  40. package/dist/dev/main.d.ts +1 -0
  41. package/dist/dollar-sign-CP9qeU5d.js +14 -0
  42. package/dist/download-CIuG04pJ.js +21 -0
  43. package/dist/file-text-Dd_thxkn.js +26 -0
  44. package/dist/filter-DyRMX9CU.js +8 -0
  45. package/dist/formatters-_vJlC-47.js +50 -0
  46. package/dist/generated/global-operations.d.ts +1 -0
  47. package/dist/generated/global-types.d.ts +20715 -0
  48. package/dist/generated/wspace-operations.d.ts +3704 -0
  49. package/dist/generated/wspace-types.d.ts +53362 -0
  50. package/dist/graphqlClient-CdJyR_ed.js +55 -0
  51. package/dist/index.d.ts +4 -0
  52. package/dist/index.js +772 -0
  53. package/dist/map-BqH1cBJi.js +18 -0
  54. package/dist/map-pin-CFBOmh-A.js +13 -0
  55. package/dist/movethewheels/MoveTheWheelsRoot.d.ts +25 -0
  56. package/dist/movethewheels/MoveTheWheelsRoutes.d.ts +7 -0
  57. package/dist/movethewheels/components/DataTable.d.ts +32 -0
  58. package/dist/movethewheels/components/MetricCard.d.ts +43 -0
  59. package/dist/movethewheels/components/PageLayout.d.ts +68 -0
  60. package/dist/movethewheels/components/StatusBadge.d.ts +49 -0
  61. package/dist/movethewheels/components/index.d.ts +10 -0
  62. package/dist/movethewheels/components/ui.d.ts +22 -0
  63. package/dist/movethewheels/constants/index.d.ts +24 -0
  64. package/dist/movethewheels/constants/mockData.d.ts +33 -0
  65. package/dist/movethewheels/hooks/index.d.ts +12 -0
  66. package/dist/movethewheels/hooks/useAnalytics.d.ts +118 -0
  67. package/dist/movethewheels/hooks/useCustomers.d.ts +37 -0
  68. package/dist/movethewheels/hooks/useFleet.d.ts +71 -0
  69. package/dist/movethewheels/hooks/useInventory.d.ts +60 -0
  70. package/dist/movethewheels/hooks/useOrders.d.ts +47 -0
  71. package/dist/movethewheels/hooks/useRoutes.d.ts +41 -0
  72. package/dist/movethewheels/hooks/useTracking.d.ts +69 -0
  73. package/dist/movethewheels/index.d.ts +30 -0
  74. package/dist/movethewheels/pages/AIAssistantPage.d.ts +4 -0
  75. package/dist/movethewheels/pages/AnalyticsPage.d.ts +4 -0
  76. package/dist/movethewheels/pages/CreateOrderPage.d.ts +6 -0
  77. package/dist/movethewheels/pages/CustomerDetailsPage.d.ts +4 -0
  78. package/dist/movethewheels/pages/CustomersPage.d.ts +4 -0
  79. package/dist/movethewheels/pages/DashboardPage.d.ts +6 -0
  80. package/dist/movethewheels/pages/DriverDetailsPage.d.ts +4 -0
  81. package/dist/movethewheels/pages/DriversPage.d.ts +4 -0
  82. package/dist/movethewheels/pages/FinancePage.d.ts +4 -0
  83. package/dist/movethewheels/pages/FleetPage.d.ts +6 -0
  84. package/dist/movethewheels/pages/ImportExportPage.d.ts +4 -0
  85. package/dist/movethewheels/pages/InventoryPage.d.ts +4 -0
  86. package/dist/movethewheels/pages/LiveTrackingPage.d.ts +6 -0
  87. package/dist/movethewheels/pages/MarketplacePage.d.ts +4 -0
  88. package/dist/movethewheels/pages/OrderDetailsPage.d.ts +6 -0
  89. package/dist/movethewheels/pages/OrdersListPage.d.ts +6 -0
  90. package/dist/movethewheels/pages/ProductDetailsPage.d.ts +4 -0
  91. package/dist/movethewheels/pages/ProductsPage.d.ts +4 -0
  92. package/dist/movethewheels/pages/ReportsPage.d.ts +4 -0
  93. package/dist/movethewheels/pages/RouteDetailsPage.d.ts +4 -0
  94. package/dist/movethewheels/pages/RoutesPage.d.ts +4 -0
  95. package/dist/movethewheels/pages/SettingsPage.d.ts +4 -0
  96. package/dist/movethewheels/pages/TrackingPage.d.ts +6 -0
  97. package/dist/movethewheels/pages/VehicleDetailsPage.d.ts +4 -0
  98. package/dist/movethewheels/pages/VehiclesPage.d.ts +4 -0
  99. package/dist/movethewheels/pages/WarehouseDetailsPage.d.ts +4 -0
  100. package/dist/movethewheels/pages/WarehousesPage.d.ts +4 -0
  101. package/dist/movethewheels/providers/MoveTheWheelsProvider.d.ts +16 -0
  102. package/dist/movethewheels/store/movethewheelsStore.d.ts +73 -0
  103. package/dist/movethewheels/types/index.d.ts +655 -0
  104. package/dist/movethewheels/utils/cn.d.ts +6 -0
  105. package/dist/movethewheels/utils/formatters.d.ts +60 -0
  106. package/dist/movethewheels/utils/graphqlClient.d.ts +11 -0
  107. package/dist/movethewheels/utils/index.d.ts +7 -0
  108. package/dist/movethewheels/utils/navigation.d.ts +23 -0
  109. package/dist/navigation-BgnOfsVd.js +6 -0
  110. package/dist/navigation-C2fY_aS9.js +8 -0
  111. package/dist/package-DVZbDRcV.js +22 -0
  112. package/dist/phone-KdwpVmC4.js +18 -0
  113. package/dist/plus-Bl7uX6Ji.js +11 -0
  114. package/dist/refresh-cw-BYjl3K-8.js +22 -0
  115. package/dist/route-Ce_poKFi.js +51 -0
  116. package/dist/save-C-qDVat-.js +18 -0
  117. package/dist/search-5pdn5eOO.js +13 -0
  118. package/dist/settings-C4kIDsYg.js +28 -0
  119. package/dist/square-pen-BwQ67vLE.js +11 -0
  120. package/dist/star-BlVsC3Ad.js +8 -0
  121. package/dist/store-DTmQT5M0.js +26 -0
  122. package/dist/trending-up-C1faflCI.js +11 -0
  123. package/dist/triangle-alert-CUoVAA4L.js +18 -0
  124. package/dist/truck-BmDAzu05.js +30 -0
  125. package/dist/useAnalytics-ph7eTIK6.js +297 -0
  126. package/dist/useCustomers-bS3a4ytk.js +186 -0
  127. package/dist/useFleet-BdETplNE.js +398 -0
  128. package/dist/useInventory-Dwn18FPz.js +323 -0
  129. package/dist/useOrders-D_3_hGMp.js +324 -0
  130. package/dist/useRoutes-v4aBaS-E.js +224 -0
  131. package/dist/useTracking-De2KIUNu.js +261 -0
  132. package/dist/user-BplzDrLP.js +13 -0
  133. package/dist/users-i-igmsP4.js +24 -0
  134. package/dist/warehouse-DewG0PXh.js +25 -0
  135. package/dist/wrench-CoSDEIC7.js +31 -0
  136. package/package.json +107 -0
  137. package/src/dev/main.tsx +110 -0
  138. package/src/dev/styles.css +139 -0
  139. package/src/generated/global-operations.ts +2 -0
  140. package/src/generated/global-types.ts +24048 -0
  141. package/src/generated/wspace-operations.ts +3734 -0
  142. package/src/generated/wspace-types.ts +60715 -0
  143. package/src/index.ts +4 -0
  144. package/src/movethewheels/MoveTheWheelsRoot.tsx +258 -0
  145. package/src/movethewheels/MoveTheWheelsRoutes.tsx +119 -0
  146. package/src/movethewheels/components/DataTable.tsx +367 -0
  147. package/src/movethewheels/components/MetricCard.tsx +180 -0
  148. package/src/movethewheels/components/PageLayout.tsx +234 -0
  149. package/src/movethewheels/components/StatusBadge.tsx +243 -0
  150. package/src/movethewheels/components/index.ts +26 -0
  151. package/src/movethewheels/components/ui.tsx +124 -0
  152. package/src/movethewheels/constants/index.ts +65 -0
  153. package/src/movethewheels/constants/mockData.ts +1342 -0
  154. package/src/movethewheels/hooks/index.ts +55 -0
  155. package/src/movethewheels/hooks/useAnalytics.ts +476 -0
  156. package/src/movethewheels/hooks/useCustomers.ts +359 -0
  157. package/src/movethewheels/hooks/useFleet.ts +778 -0
  158. package/src/movethewheels/hooks/useInventory.ts +632 -0
  159. package/src/movethewheels/hooks/useOrders.ts +703 -0
  160. package/src/movethewheels/hooks/useRoutes.ts +453 -0
  161. package/src/movethewheels/hooks/useTracking.ts +505 -0
  162. package/src/movethewheels/index.ts +68 -0
  163. package/src/movethewheels/pages/AIAssistantPage.tsx +160 -0
  164. package/src/movethewheels/pages/AnalyticsPage.tsx +190 -0
  165. package/src/movethewheels/pages/CreateOrderPage.tsx +454 -0
  166. package/src/movethewheels/pages/CustomerDetailsPage.tsx +207 -0
  167. package/src/movethewheels/pages/CustomersPage.tsx +115 -0
  168. package/src/movethewheels/pages/DashboardPage.tsx +414 -0
  169. package/src/movethewheels/pages/DriverDetailsPage.tsx +261 -0
  170. package/src/movethewheels/pages/DriversPage.tsx +118 -0
  171. package/src/movethewheels/pages/FinancePage.tsx +141 -0
  172. package/src/movethewheels/pages/FleetPage.tsx +289 -0
  173. package/src/movethewheels/pages/ImportExportPage.tsx +165 -0
  174. package/src/movethewheels/pages/InventoryPage.tsx +212 -0
  175. package/src/movethewheels/pages/LiveTrackingPage.tsx +325 -0
  176. package/src/movethewheels/pages/MarketplacePage.tsx +235 -0
  177. package/src/movethewheels/pages/OrderDetailsPage.tsx +387 -0
  178. package/src/movethewheels/pages/OrdersListPage.tsx +241 -0
  179. package/src/movethewheels/pages/ProductDetailsPage.tsx +155 -0
  180. package/src/movethewheels/pages/ProductsPage.tsx +124 -0
  181. package/src/movethewheels/pages/ReportsPage.tsx +164 -0
  182. package/src/movethewheels/pages/RouteDetailsPage.tsx +245 -0
  183. package/src/movethewheels/pages/RoutesPage.tsx +104 -0
  184. package/src/movethewheels/pages/SettingsPage.tsx +242 -0
  185. package/src/movethewheels/pages/TrackingPage.tsx +419 -0
  186. package/src/movethewheels/pages/VehicleDetailsPage.tsx +218 -0
  187. package/src/movethewheels/pages/VehiclesPage.tsx +124 -0
  188. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +216 -0
  189. package/src/movethewheels/pages/WarehousesPage.tsx +122 -0
  190. package/src/movethewheels/providers/MoveTheWheelsProvider.tsx +66 -0
  191. package/src/movethewheels/store/movethewheelsStore.ts +136 -0
  192. package/src/movethewheels/types/index.ts +744 -0
  193. package/src/movethewheels/utils/cn.ts +9 -0
  194. package/src/movethewheels/utils/formatters.ts +215 -0
  195. package/src/movethewheels/utils/graphqlClient.ts +63 -0
  196. package/src/movethewheels/utils/index.ts +8 -0
  197. package/src/movethewheels/utils/navigation.ts +70 -0
  198. package/src/operations/global/.gitkeep +0 -0
  199. package/src/operations/wspace/movethewheels/fragments/core.graphql +191 -0
  200. package/src/operations/wspace/movethewheels/mutations/entities.graphql +87 -0
  201. package/src/operations/wspace/movethewheels/mutations/logistics.graphql +86 -0
  202. package/src/operations/wspace/movethewheels/mutations/marketplace-reports.graphql +81 -0
  203. package/src/operations/wspace/movethewheels/mutations/orders.graphql +21 -0
  204. package/src/operations/wspace/movethewheels/queries/dashboard.graphql +61 -0
  205. package/src/operations/wspace/movethewheels/queries/entities.graphql +83 -0
  206. package/src/operations/wspace/movethewheels/queries/logistics.graphql +84 -0
  207. package/src/operations/wspace/movethewheels/queries/marketplace-reports.graphql +40 -0
  208. 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
+ }