@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,453 @@
1
+ import { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
3
+ import type { Route, RouteStop } from '../types';
4
+ import { executeGraphQL } from '../utils/graphqlClient';
5
+
6
+ interface UseRoutesOptions {
7
+ driverId?: string;
8
+ vehicleId?: string;
9
+ status?: 'planned' | 'in_progress' | 'completed' | 'cancelled';
10
+ }
11
+
12
+ interface UseRoutesResult {
13
+ routes: Route[];
14
+ totalRoutes: number;
15
+ isLoading: boolean;
16
+ error: Error | null;
17
+ refetch: () => void;
18
+ createRoute: (route: Partial<Route>) => Promise<Route>;
19
+ updateRoute: (id: string, updates: Partial<Route>) => Promise<Route>;
20
+ deleteRoute: (id: string) => Promise<void>;
21
+ optimizeRoute: (routeId: string) => Promise<Route>;
22
+ updateStopStatus: (
23
+ routeId: string,
24
+ stopId: string,
25
+ status: RouteStop['status']
26
+ ) => Promise<Route>;
27
+ }
28
+
29
+ interface ApiRoute {
30
+ id: string;
31
+ name: string;
32
+ status: string;
33
+ shipmentId?: string | null;
34
+ vehicleId?: string | null;
35
+ driverId?: string | null;
36
+ waypoints?: unknown;
37
+ estimatedDistance?: number | null;
38
+ estimatedDuration?: number | null;
39
+ actualDistance?: number | null;
40
+ actualDuration?: number | null;
41
+ createdAt: string;
42
+ }
43
+
44
+ const LIST_ROUTES_QUERY = `
45
+ query ListRoutes($filter: RouteFilterInput, $pagination: PaginationInput) {
46
+ routes(filter: $filter, pagination: $pagination) {
47
+ id
48
+ name
49
+ status
50
+ shipmentId
51
+ vehicleId
52
+ driverId
53
+ waypoints
54
+ estimatedDistance
55
+ estimatedDuration
56
+ actualDistance
57
+ actualDuration
58
+ createdAt
59
+ }
60
+ }
61
+ `;
62
+
63
+ const GET_ROUTE_QUERY = `
64
+ query GetRoute($id: ID!) {
65
+ route(id: $id) {
66
+ id
67
+ name
68
+ status
69
+ shipmentId
70
+ vehicleId
71
+ driverId
72
+ waypoints
73
+ estimatedDistance
74
+ estimatedDuration
75
+ actualDistance
76
+ actualDuration
77
+ createdAt
78
+ }
79
+ }
80
+ `;
81
+
82
+ const CREATE_ROUTE_MUTATION = `
83
+ mutation CreateRoute($input: CreateRouteInput!) {
84
+ createRoute(input: $input) {
85
+ id
86
+ name
87
+ status
88
+ shipmentId
89
+ vehicleId
90
+ driverId
91
+ waypoints
92
+ estimatedDistance
93
+ estimatedDuration
94
+ actualDistance
95
+ actualDuration
96
+ createdAt
97
+ }
98
+ }
99
+ `;
100
+
101
+ const UPDATE_ROUTE_MUTATION = `
102
+ mutation UpdateRoute($id: ID!, $input: UpdateRouteInput!) {
103
+ updateRoute(id: $id, input: $input) {
104
+ id
105
+ name
106
+ status
107
+ shipmentId
108
+ vehicleId
109
+ driverId
110
+ waypoints
111
+ estimatedDistance
112
+ estimatedDuration
113
+ actualDistance
114
+ actualDuration
115
+ createdAt
116
+ }
117
+ }
118
+ `;
119
+
120
+ const DELETE_ROUTE_MUTATION = `
121
+ mutation DeleteRoute($id: ID!) {
122
+ deleteRoute(id: $id)
123
+ }
124
+ `;
125
+
126
+ const OPTIMIZE_ROUTE_MUTATION = `
127
+ mutation OptimizeRoute($id: ID!) {
128
+ optimizeRoute(id: $id) {
129
+ id
130
+ name
131
+ status
132
+ shipmentId
133
+ vehicleId
134
+ driverId
135
+ waypoints
136
+ estimatedDistance
137
+ estimatedDuration
138
+ actualDistance
139
+ actualDuration
140
+ createdAt
141
+ }
142
+ }
143
+ `;
144
+
145
+ function mapRouteStatus(status: string): Route['status'] {
146
+ switch (status) {
147
+ case 'IN_PROGRESS':
148
+ return 'in_progress';
149
+ case 'COMPLETED':
150
+ return 'completed';
151
+ case 'CANCELLED':
152
+ return 'cancelled';
153
+ default:
154
+ return 'planned';
155
+ }
156
+ }
157
+
158
+ function toApiRouteStatus(status: Route['status'] | undefined): string | undefined {
159
+ if (!status) return undefined;
160
+ switch (status) {
161
+ case 'in_progress':
162
+ return 'IN_PROGRESS';
163
+ case 'completed':
164
+ return 'COMPLETED';
165
+ case 'cancelled':
166
+ return 'CANCELLED';
167
+ default:
168
+ return 'PLANNED';
169
+ }
170
+ }
171
+
172
+ function mapWaypoints(waypoints: unknown): RouteStop[] {
173
+ if (!Array.isArray(waypoints)) return [];
174
+ return waypoints.map((point, index) => {
175
+ const item = (point ?? {}) as Record<string, unknown>;
176
+ return {
177
+ id: String(item.id ?? `stop-${index + 1}`),
178
+ orderId: String(item.orderId ?? ''),
179
+ sequence: Number(item.sequence ?? index + 1),
180
+ address: {
181
+ street: String(item.street ?? ''),
182
+ city: String(item.city ?? ''),
183
+ state: String(item.state ?? ''),
184
+ postalCode: String(item.postalCode ?? ''),
185
+ country: String(item.country ?? ''),
186
+ latitude: Number(item.latitude ?? 0),
187
+ longitude: Number(item.longitude ?? 0),
188
+ },
189
+ type: item.type === 'pickup' ? 'pickup' : 'delivery',
190
+ scheduledTime: item.scheduledTime ? new Date(String(item.scheduledTime)) : new Date(),
191
+ actualTime: item.actualTime ? new Date(String(item.actualTime)) : undefined,
192
+ duration: Number(item.duration ?? 0),
193
+ status: (item.status as RouteStop['status']) ?? 'pending',
194
+ notes: item.notes ? String(item.notes) : undefined,
195
+ };
196
+ });
197
+ }
198
+
199
+ function mapRoute(route: ApiRoute): Route {
200
+ return {
201
+ id: route.id,
202
+ name: route.name,
203
+ driverId: route.driverId ?? '',
204
+ vehicleId: route.vehicleId ?? '',
205
+ status: mapRouteStatus(route.status),
206
+ stops: mapWaypoints(route.waypoints),
207
+ distance: route.estimatedDistance ?? 0,
208
+ estimatedDuration: route.estimatedDuration ?? 0,
209
+ actualDuration: route.actualDuration ?? undefined,
210
+ startTime: undefined,
211
+ endTime: undefined,
212
+ optimized: false,
213
+ trafficConditions: [],
214
+ createdAt: new Date(route.createdAt),
215
+ };
216
+ }
217
+
218
+ function useGraphqlOptions() {
219
+ const { apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser } =
220
+ useMoveTheWheels();
221
+ return useMemo(
222
+ () => ({
223
+ apiGatewayUrl,
224
+ authToken,
225
+ workspaceToken,
226
+ workspaceId,
227
+ tenantId,
228
+ actorId: currentUser?.id,
229
+ }),
230
+ [apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser?.id]
231
+ );
232
+ }
233
+
234
+ export function useRoutes(options: UseRoutesOptions = {}): UseRoutesResult {
235
+ const { driverId, vehicleId, status } = options;
236
+ const graphQLOptions = useGraphqlOptions();
237
+ const [routes, setRoutes] = useState<Route[]>([]);
238
+ const [isLoading, setIsLoading] = useState(false);
239
+ const [error, setError] = useState<Error | null>(null);
240
+
241
+ const load = useCallback(async () => {
242
+ setIsLoading(true);
243
+ setError(null);
244
+ try {
245
+ const data = await executeGraphQL<{ routes: ApiRoute[] }>(graphQLOptions, LIST_ROUTES_QUERY, {
246
+ filter: {
247
+ driverId,
248
+ vehicleId,
249
+ status: status ? toApiRouteStatus(status) : undefined,
250
+ },
251
+ pagination: { limit: 500, offset: 0 },
252
+ });
253
+
254
+ setRoutes(data.routes.map(mapRoute));
255
+ } catch (fetchError) {
256
+ setError(fetchError instanceof Error ? fetchError : new Error('Failed to load routes'));
257
+ } finally {
258
+ setIsLoading(false);
259
+ }
260
+ }, [driverId, graphQLOptions, status, vehicleId]);
261
+
262
+ useEffect(() => {
263
+ void load();
264
+ }, [load]);
265
+
266
+ const refetch = useCallback(() => {
267
+ void load();
268
+ }, [load]);
269
+
270
+ const createRoute = useCallback(
271
+ async (route: Partial<Route>): Promise<Route> => {
272
+ const data = await executeGraphQL<{ createRoute: ApiRoute }>(
273
+ graphQLOptions,
274
+ CREATE_ROUTE_MUTATION,
275
+ {
276
+ input: {
277
+ name: route.name ?? `Route ${new Date().toLocaleDateString()}`,
278
+ status: toApiRouteStatus(route.status),
279
+ shipmentId: undefined,
280
+ vehicleId: route.vehicleId,
281
+ driverId: route.driverId,
282
+ waypoints: route.stops ?? [],
283
+ estimatedDistance: route.distance ?? 0,
284
+ estimatedDuration: route.estimatedDuration ?? 0,
285
+ actualDistance: route.distance,
286
+ actualDuration: route.actualDuration,
287
+ },
288
+ }
289
+ );
290
+
291
+ const created = mapRoute(data.createRoute);
292
+ setRoutes((previous) => [created, ...previous]);
293
+ return created;
294
+ },
295
+ [graphQLOptions]
296
+ );
297
+
298
+ const updateRoute = useCallback(
299
+ async (id: string, updates: Partial<Route>): Promise<Route> => {
300
+ const data = await executeGraphQL<{ updateRoute: ApiRoute }>(
301
+ graphQLOptions,
302
+ UPDATE_ROUTE_MUTATION,
303
+ {
304
+ id,
305
+ input: {
306
+ name: updates.name,
307
+ status: toApiRouteStatus(updates.status),
308
+ shipmentId: undefined,
309
+ vehicleId: updates.vehicleId,
310
+ driverId: updates.driverId,
311
+ waypoints: updates.stops,
312
+ estimatedDistance: updates.distance,
313
+ estimatedDuration: updates.estimatedDuration,
314
+ actualDistance: updates.distance,
315
+ actualDuration: updates.actualDuration,
316
+ },
317
+ }
318
+ );
319
+
320
+ const updated = mapRoute(data.updateRoute);
321
+ setRoutes((previous) => previous.map((item) => (item.id === id ? updated : item)));
322
+ return updated;
323
+ },
324
+ [graphQLOptions]
325
+ );
326
+
327
+ const deleteRoute = useCallback(
328
+ async (id: string): Promise<void> => {
329
+ await executeGraphQL<{ deleteRoute: boolean }>(graphQLOptions, DELETE_ROUTE_MUTATION, { id });
330
+ setRoutes((previous) => previous.filter((item) => item.id !== id));
331
+ },
332
+ [graphQLOptions]
333
+ );
334
+
335
+ const optimizeRoute = useCallback(
336
+ async (routeId: string): Promise<Route> => {
337
+ const data = await executeGraphQL<{ optimizeRoute: ApiRoute }>(
338
+ graphQLOptions,
339
+ OPTIMIZE_ROUTE_MUTATION,
340
+ {
341
+ id: routeId,
342
+ }
343
+ );
344
+ const optimized = { ...mapRoute(data.optimizeRoute), optimized: true };
345
+ setRoutes((previous) => previous.map((item) => (item.id === routeId ? optimized : item)));
346
+ return optimized;
347
+ },
348
+ [graphQLOptions]
349
+ );
350
+
351
+ const updateStopStatus = useCallback(
352
+ async (routeId: string, stopId: string, stopStatus: RouteStop['status']): Promise<Route> => {
353
+ const route = routes.find((item) => item.id === routeId);
354
+ if (!route) {
355
+ throw new Error('Route not found');
356
+ }
357
+ const updatedStops = route.stops.map((stop) =>
358
+ stop.id === stopId
359
+ ? {
360
+ ...stop,
361
+ status: stopStatus,
362
+ actualTime: stopStatus === 'completed' ? new Date() : stop.actualTime,
363
+ }
364
+ : stop
365
+ );
366
+ return updateRoute(routeId, {
367
+ stops: updatedStops,
368
+ status: updatedStops.every((item) => item.status === 'completed')
369
+ ? 'completed'
370
+ : route.status,
371
+ });
372
+ },
373
+ [routes, updateRoute]
374
+ );
375
+
376
+ return {
377
+ routes,
378
+ totalRoutes: routes.length,
379
+ isLoading,
380
+ error,
381
+ refetch,
382
+ createRoute,
383
+ updateRoute,
384
+ deleteRoute,
385
+ optimizeRoute,
386
+ updateStopStatus,
387
+ };
388
+ }
389
+
390
+ export function useRoute(routeId: string | undefined) {
391
+ const graphQLOptions = useGraphqlOptions();
392
+ const [route, setRoute] = useState<Route | null>(null);
393
+ const [isLoading, setIsLoading] = useState(false);
394
+ const [error, setError] = useState<Error | null>(null);
395
+
396
+ const load = useCallback(async () => {
397
+ if (!routeId) {
398
+ setRoute(null);
399
+ return;
400
+ }
401
+
402
+ setIsLoading(true);
403
+ setError(null);
404
+ try {
405
+ const data = await executeGraphQL<{ route: ApiRoute | null }>(
406
+ graphQLOptions,
407
+ GET_ROUTE_QUERY,
408
+ {
409
+ id: routeId,
410
+ }
411
+ );
412
+ setRoute(data.route ? mapRoute(data.route) : null);
413
+ } catch (fetchError) {
414
+ setError(fetchError instanceof Error ? fetchError : new Error('Failed to load route'));
415
+ } finally {
416
+ setIsLoading(false);
417
+ }
418
+ }, [graphQLOptions, routeId]);
419
+
420
+ useEffect(() => {
421
+ void load();
422
+ }, [load]);
423
+
424
+ return {
425
+ route,
426
+ isLoading,
427
+ error,
428
+ refetch: () => void load(),
429
+ };
430
+ }
431
+
432
+ export function useRouteStats() {
433
+ const { routes, isLoading, error } = useRoutes();
434
+
435
+ const stats = useMemo(() => {
436
+ const totalDistance = routes.reduce((sum, route) => sum + route.distance, 0);
437
+ const totalDuration = routes.reduce((sum, route) => sum + route.estimatedDuration, 0);
438
+ const totalRoutes = routes.length;
439
+
440
+ return {
441
+ totalRoutes,
442
+ totalDistance,
443
+ totalDuration,
444
+ avgDistance: totalRoutes > 0 ? totalDistance / totalRoutes : 0,
445
+ avgDuration: totalRoutes > 0 ? totalDuration / totalRoutes : 0,
446
+ completedRoutes: routes.filter((route) => route.status === 'completed').length,
447
+ inProgressRoutes: routes.filter((route) => route.status === 'in_progress').length,
448
+ plannedRoutes: routes.filter((route) => route.status === 'planned').length,
449
+ };
450
+ }, [routes]);
451
+
452
+ return { stats, isLoading, error };
453
+ }