@burdenoff/microfe-movethewheels 2026.626.2 → 2026.703.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/{AIAssistantPage-AvsMbunM.js → AIAssistantPage-C3cPsWt-.js} +100 -73
  2. package/dist/AnalyticsPage-Dz6g2MvK.js +215 -0
  3. package/dist/{CreateOrderPage-CjlnEtk3.js → CreateOrderPage-C5iwnjp4.js} +36 -67
  4. package/dist/CustomerDetailsPage-B6nwFyLO.js +406 -0
  5. package/dist/{CustomersPage-CGGfD4ki.js → CustomersPage-2Y05LVyh.js} +26 -28
  6. package/dist/{DashboardPage-D2HRqwg9.js → DashboardPage-BVjQi0EB.js} +85 -83
  7. package/dist/{DataTable-CD9ZhpLB.js → DataTable-DmciIKKM.js} +3 -3
  8. package/dist/DriverDetailsPage-CN3WQ0B9.js +507 -0
  9. package/dist/{DriversPage-DKiynISH.js → DriversPage-B0Zpx8OQ.js} +25 -27
  10. package/dist/{FinancePage-Vg8WPs38.js → FinancePage-DEDykSOP.js} +9 -9
  11. package/dist/FleetPage-DqkFi3s3.js +365 -0
  12. package/dist/FormError-BlOjnXOq.js +15 -0
  13. package/dist/ImportExportPage-DKJRCezl.js +290 -0
  14. package/dist/InventoryPage-DwfgCUQq.js +253 -0
  15. package/dist/{LiveTrackingPage-DQ1q4Id3.js → LiveTrackingPage-DCX-K-LR.js} +60 -56
  16. package/dist/MarketplacePage-CIQ97_zp.js +247 -0
  17. package/dist/{MetricCard-B7yMyvMX.js → MetricCard-BY0QrMD4.js} +1 -1
  18. package/dist/{OrderDetailsPage-CFASgsYX.js → OrderDetailsPage-DYiZ-9UK.js} +83 -85
  19. package/dist/{OrdersListPage-BHeT8S5o.js → OrdersListPage-BjDlCKJd.js} +31 -33
  20. package/dist/{PageLayout-D3N8MCTm.js → PageLayout-D4LDHJJq.js} +483 -422
  21. package/dist/ProductDetailsPage-C9uOiYLp.js +350 -0
  22. package/dist/{ProductsPage--xEpbQ8A.js → ProductsPage-C1G9h7Xd.js} +22 -24
  23. package/dist/ReportsPage-Zp53q3_8.js +284 -0
  24. package/dist/RouteDetailsPage-DUStXSf9.js +531 -0
  25. package/dist/RoutesPage-DMfoicM1.js +144 -0
  26. package/dist/SettingsPage-Bn-v0mbQ.js +421 -0
  27. package/dist/StatusBadge-BZurYHMy.js +152 -0
  28. package/dist/{TrackingPage-CcRc_dva.js → TrackingPage-W-jtdM3Y.js} +59 -61
  29. package/dist/VehicleDetailsPage-Bceo5oz3.js +410 -0
  30. package/dist/{VehiclesPage-UlxPWP9K.js → VehiclesPage-VBtOep91.js} +20 -22
  31. package/dist/WarehouseDetailsPage-L_6sWigM.js +493 -0
  32. package/dist/{WarehousesPage-CuTO-OPp.js → WarehousesPage-DCbXXXWE.js} +16 -18
  33. package/dist/{arrow-left-CBpZKkpq.js → arrow-left-n48Y-Wis.js} +1 -1
  34. package/dist/{box-CAaiElsb.js → box-D9Pal4HM.js} +1 -1
  35. package/dist/{chart-column-D1Ym15-S.js → chart-column-BFIJysDR.js} +1 -1
  36. package/dist/{chevron-right-BIFGi0UA.js → chevron-right-EMIIyyJ1.js} +1 -1
  37. package/dist/{circle-alert-DjmNo5_L.js → circle-alert-iY24lSlp.js} +1 -1
  38. package/dist/{circle-check-big-CjuPVo3_.js → circle-check-big-w0TrLKMF.js} +1 -1
  39. package/dist/{clock-BjkdnddD.js → clock-DJZtdl8u.js} +1 -1
  40. package/dist/{dollar-sign-IB4O0wuQ.js → dollar-sign-CgKQRz_e.js} +1 -1
  41. package/dist/{download-Dpa9vqoG.js → download-DRXxoiQR.js} +1 -1
  42. package/dist/{file-text-C8Uioy11.js → file-text-DWWY6_f5.js} +1 -1
  43. package/dist/{filter-Da5OZaHI.js → filter-DloLC2eE.js} +1 -1
  44. package/dist/index.js +186 -162
  45. package/dist/{map-_Per4sT9.js → map-CkJaULb2.js} +1 -1
  46. package/dist/{map-pin-BSaf5IDL.js → map-pin-BGiCvyCs.js} +1 -1
  47. package/dist/{navigation-DaTbrhw2.js → navigation-D36prnAn.js} +1 -1
  48. package/dist/{package-BE7U0wQE.js → package-BChUzpJS.js} +1 -1
  49. package/dist/{phone-DV4StZiA.js → phone-Ac9s3mo_.js} +1 -1
  50. package/dist/{plus-Dl1rLeJR.js → plus-Z76q-FZm.js} +1 -1
  51. package/dist/{refresh-cw-CWfERAit.js → refresh-cw-DKt6f5T6.js} +1 -1
  52. package/dist/{save-XH8vydOn.js → save-CojywNhG.js} +1 -1
  53. package/dist/{search-OxoIiL_C.js → search-CDGefqb3.js} +1 -1
  54. package/dist/{square-pen-hLIbFQFV.js → square-pen-CopE9GmB.js} +1 -1
  55. package/dist/{star-YsVV3MNQ.js → star-nHlLjDKR.js} +1 -1
  56. package/dist/{store-RCBl7HB-.js → store-BR9Voi5E.js} +1 -1
  57. package/dist/trash-2-BKhar6Hs.js +32 -0
  58. package/dist/{trending-up-C-85AwGT.js → trending-up-Dohgd4t_.js} +1 -1
  59. package/dist/{triangle-alert-BOhfGgmh.js → triangle-alert-i-XQL9Rd.js} +1 -1
  60. package/dist/{truck-CtDjNFbi.js → truck-C0v_hbLR.js} +1 -1
  61. package/dist/{useAnalytics-ph7eTIK6.js → useAnalytics-DS8n0xgg.js} +40 -40
  62. package/dist/useAssistant-D4v3oxD3.js +84 -0
  63. package/dist/{useCustomers-bS3a4ytk.js → useCustomers-BmTNGxiI.js} +24 -24
  64. package/dist/{useFleet-BdETplNE.js → useFleet-DKqXZ16m.js} +133 -109
  65. package/dist/useImportExport-DHr-SWSw.js +55 -0
  66. package/dist/{useInventory-Dwn18FPz.js → useInventory-DkyiVY_A.js} +95 -75
  67. package/dist/{useOrders-D_3_hGMp.js → useOrders-Cfk1GLiq.js} +42 -42
  68. package/dist/useReports-CTqZr-AB.js +78 -0
  69. package/dist/{useRoutes-v4aBaS-E.js → useRoutes-BQWO607N.js} +31 -31
  70. package/dist/useSettings-CkDH_-gM.js +81 -0
  71. package/dist/{useTracking-De2KIUNu.js → useTracking-B-va1kO3.js} +22 -22
  72. package/dist/{user-Dz5TJZFs.js → user-jyFU3lXk.js} +1 -1
  73. package/dist/{users-g2N0eL-Z.js → users-DZ-ilS6B.js} +1 -1
  74. package/dist/{warehouse-D3WWDeTX.js → warehouse-6m5kXMLu.js} +1 -1
  75. package/dist/{wrench-CmxY1n4I.js → wrench-CANXWxF-.js} +1 -1
  76. package/package.json +1 -1
  77. package/src/movethewheels/MoveTheWheelsRoutes.tsx +31 -3
  78. package/src/movethewheels/components/FormError.tsx +25 -0
  79. package/src/movethewheels/components/StatusBadge.tsx +18 -37
  80. package/src/movethewheels/constants/index.ts +67 -26
  81. package/src/movethewheels/hooks/index.ts +12 -0
  82. package/src/movethewheels/hooks/useAnalytics.ts +20 -6
  83. package/src/movethewheels/hooks/useAssistant.ts +74 -0
  84. package/src/movethewheels/hooks/useFleet.ts +170 -59
  85. package/src/movethewheels/hooks/useImportExport.ts +101 -0
  86. package/src/movethewheels/hooks/useInventory.ts +107 -0
  87. package/src/movethewheels/hooks/useReports.ts +158 -0
  88. package/src/movethewheels/hooks/useSettings.ts +120 -0
  89. package/src/movethewheels/pages/AIAssistantPage.tsx +53 -15
  90. package/src/movethewheels/pages/AnalyticsPage.tsx +67 -64
  91. package/src/movethewheels/pages/CustomerDetailsPage.tsx +154 -21
  92. package/src/movethewheels/pages/CustomersPage.tsx +1 -1
  93. package/src/movethewheels/pages/DashboardPage.tsx +42 -22
  94. package/src/movethewheels/pages/DriverDetailsPage.tsx +160 -3
  95. package/src/movethewheels/pages/DriversPage.tsx +6 -2
  96. package/src/movethewheels/pages/FleetPage.tsx +96 -38
  97. package/src/movethewheels/pages/ImportExportPage.tsx +174 -68
  98. package/src/movethewheels/pages/InventoryPage.tsx +66 -28
  99. package/src/movethewheels/pages/LiveTrackingPage.tsx +12 -3
  100. package/src/movethewheels/pages/MarketplacePage.tsx +87 -45
  101. package/src/movethewheels/pages/OrderDetailsPage.tsx +2 -2
  102. package/src/movethewheels/pages/ProductDetailsPage.tsx +146 -3
  103. package/src/movethewheels/pages/ProductsPage.tsx +5 -1
  104. package/src/movethewheels/pages/ReportsPage.tsx +194 -90
  105. package/src/movethewheels/pages/RouteDetailsPage.tsx +290 -36
  106. package/src/movethewheels/pages/RoutesPage.tsx +4 -9
  107. package/src/movethewheels/pages/SettingsPage.tsx +234 -56
  108. package/src/movethewheels/pages/TrackingPage.tsx +6 -6
  109. package/src/movethewheels/pages/VehicleDetailsPage.tsx +158 -2
  110. package/src/movethewheels/pages/VehiclesPage.tsx +5 -1
  111. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +224 -2
  112. package/src/movethewheels/pages/WarehousesPage.tsx +5 -1
  113. package/src/movethewheels/providers/MoveTheWheelsProvider.tsx +7 -6
  114. package/src/movethewheels/store/movethewheelsStore.ts +1 -1
  115. package/dist/AnalyticsPage-CCLfHgQZ.js +0 -212
  116. package/dist/CustomerDetailsPage-I6cdDHUi.js +0 -265
  117. package/dist/DriverDetailsPage-rg_pGNuP.js +0 -330
  118. package/dist/FleetPage-DddXt8r8.js +0 -332
  119. package/dist/ImportExportPage-BW6qO3kc.js +0 -248
  120. package/dist/InventoryPage-rMGuaJN-.js +0 -222
  121. package/dist/MarketplacePage-DKrXAv62.js +0 -219
  122. package/dist/ProductDetailsPage-CFGcbaHo.js +0 -195
  123. package/dist/ReportsPage-Bb7_90rK.js +0 -235
  124. package/dist/RouteDetailsPage-CVtDVb1N.js +0 -282
  125. package/dist/RoutesPage-s57PtgcQ.js +0 -163
  126. package/dist/SettingsPage-BYNCBOpN.js +0 -298
  127. package/dist/StatusBadge-CijYQXtb.js +0 -209
  128. package/dist/VehicleDetailsPage-3YDMPsPs.js +0 -220
  129. package/dist/WarehouseDetailsPage-Db7KeN-d.js +0 -238
  130. package/dist/graphqlClient-CdJyR_ed.js +0 -55
  131. package/dist/navigation-BgnOfsVd.js +0 -6
  132. package/dist/route-Dp9JiN_E.js +0 -51
  133. package/dist/settings-3Rw5NwFF.js +0 -28
  134. package/src/movethewheels/constants/mockData.ts +0 -1342
  135. /package/dist/{formatters-_vJlC-47.js → formatters-CF2S2l1m.js} +0 -0
@@ -1,44 +1,85 @@
1
1
  /**
2
2
  * Constants barrel export
3
+ *
4
+ * Status color maps use fe-libs semantic status tokens (never raw palette
5
+ * classes) so they adapt automatically across light/dark and product themes.
6
+ * Definitions live in fe-libs/src/shared/styles/app-shell.css.
3
7
  */
4
8
 
5
- export * from './mockData';
9
+ export interface StatusColor {
10
+ bg: string;
11
+ text: string;
12
+ border?: string;
13
+ }
14
+
15
+ const SUCCESS: StatusColor = {
16
+ bg: 'bg-status-success-bg-subtle',
17
+ text: 'text-status-success-text',
18
+ border: 'border-status-success-border',
19
+ };
20
+ const WARNING: StatusColor = {
21
+ bg: 'bg-status-warning-bg-subtle',
22
+ text: 'text-status-warning-text',
23
+ border: 'border-status-warning-border',
24
+ };
25
+ const ERROR: StatusColor = {
26
+ bg: 'bg-status-error-bg-subtle',
27
+ text: 'text-status-error-text',
28
+ border: 'border-status-error-border',
29
+ };
30
+ const INFO: StatusColor = {
31
+ bg: 'bg-status-info-bg-subtle',
32
+ text: 'text-status-info-text',
33
+ border: 'border-status-info-border',
34
+ };
35
+ const NEUTRAL: StatusColor = {
36
+ bg: 'bg-bg-sunken',
37
+ text: 'text-text-secondary',
38
+ };
6
39
 
7
40
  // Order status colors
8
- export const ORDER_STATUS_COLORS: Record<string, { bg: string; text: string; border: string }> = {
9
- pending: { bg: 'bg-yellow-100', text: 'text-yellow-800', border: 'border-yellow-200' },
10
- confirmed: { bg: 'bg-blue-100', text: 'text-blue-800', border: 'border-blue-200' },
11
- assigned: { bg: 'bg-purple-100', text: 'text-purple-800', border: 'border-purple-200' },
12
- picked_up: { bg: 'bg-indigo-100', text: 'text-indigo-800', border: 'border-indigo-200' },
13
- in_transit: { bg: 'bg-cyan-100', text: 'text-cyan-800', border: 'border-cyan-200' },
14
- out_for_delivery: { bg: 'bg-orange-100', text: 'text-orange-800', border: 'border-orange-200' },
15
- delivered: { bg: 'bg-green-100', text: 'text-green-800', border: 'border-green-200' },
16
- cancelled: { bg: 'bg-red-100', text: 'text-red-800', border: 'border-red-200' },
17
- failed: { bg: 'bg-red-100', text: 'text-red-800', border: 'border-red-200' },
41
+ export const ORDER_STATUS_COLORS: Record<string, StatusColor> = {
42
+ pending: WARNING,
43
+ confirmed: INFO,
44
+ assigned: INFO,
45
+ picked_up: INFO,
46
+ in_transit: INFO,
47
+ out_for_delivery: WARNING,
48
+ delivered: SUCCESS,
49
+ cancelled: ERROR,
50
+ failed: ERROR,
18
51
  };
19
52
 
20
53
  // Vehicle status colors
21
- export const VEHICLE_STATUS_COLORS: Record<string, { bg: string; text: string }> = {
22
- available: { bg: 'bg-green-100', text: 'text-green-800' },
23
- assigned: { bg: 'bg-blue-100', text: 'text-blue-800' },
24
- maintenance: { bg: 'bg-yellow-100', text: 'text-yellow-800' },
25
- out_of_service: { bg: 'bg-red-100', text: 'text-red-800' },
54
+ export const VEHICLE_STATUS_COLORS: Record<string, StatusColor> = {
55
+ available: SUCCESS,
56
+ assigned: INFO,
57
+ maintenance: WARNING,
58
+ out_of_service: ERROR,
26
59
  };
27
60
 
28
61
  // Driver status colors
29
- export const DRIVER_STATUS_COLORS: Record<string, { bg: string; text: string }> = {
30
- available: { bg: 'bg-green-100', text: 'text-green-800' },
31
- busy: { bg: 'bg-blue-100', text: 'text-blue-800' },
32
- offline: { bg: 'bg-gray-100', text: 'text-gray-800' },
33
- on_break: { bg: 'bg-yellow-100', text: 'text-yellow-800' },
62
+ export const DRIVER_STATUS_COLORS: Record<string, StatusColor> = {
63
+ available: SUCCESS,
64
+ busy: INFO,
65
+ offline: NEUTRAL,
66
+ on_break: WARNING,
34
67
  };
35
68
 
36
69
  // Priority colors
37
- export const PRIORITY_COLORS: Record<string, { bg: string; text: string }> = {
38
- low: { bg: 'bg-gray-100', text: 'text-gray-600' },
39
- normal: { bg: 'bg-blue-100', text: 'text-blue-600' },
40
- high: { bg: 'bg-orange-100', text: 'text-orange-600' },
41
- urgent: { bg: 'bg-red-100', text: 'text-red-600' },
70
+ export const PRIORITY_COLORS: Record<string, StatusColor> = {
71
+ low: NEUTRAL,
72
+ normal: INFO,
73
+ high: WARNING,
74
+ urgent: ERROR,
75
+ };
76
+
77
+ // Route status colors (shared by RoutesPage + RouteDetailsPage)
78
+ export const ROUTE_STATUS_COLORS: Record<string, StatusColor> = {
79
+ planned: INFO,
80
+ in_progress: WARNING,
81
+ completed: SUCCESS,
82
+ cancelled: ERROR,
42
83
  };
43
84
 
44
85
  // Customer type labels
@@ -53,3 +53,15 @@ export {
53
53
  useTrackingAlerts,
54
54
  useGeofence,
55
55
  } from './useTracking';
56
+
57
+ // Assistant
58
+ export { useAssistant } from './useAssistant';
59
+
60
+ // Reports
61
+ export { useReports } from './useReports';
62
+
63
+ // Import / Export
64
+ export { useImportExport } from './useImportExport';
65
+
66
+ // Module settings
67
+ export { useModuleSettings } from './useSettings';
@@ -40,11 +40,14 @@ const ANALYTICS_QUERY = `
40
40
  activeVehicles
41
41
  lowStockItems
42
42
  totalRevenue
43
+ onTimeDeliveryRate
44
+ customerSatisfaction
43
45
  }
44
46
  fleetStats {
45
47
  totalVehicles
46
48
  activeVehicles
47
49
  maintenanceVehicles
50
+ avgDriverRating
48
51
  }
49
52
  }
50
53
  `;
@@ -58,6 +61,8 @@ const DASHBOARD_QUERY = `
58
61
  activeVehicles
59
62
  lowStockItems
60
63
  totalRevenue
64
+ onTimeDeliveryRate
65
+ customerSatisfaction
61
66
  }
62
67
  orderStats {
63
68
  total
@@ -74,6 +79,7 @@ const DASHBOARD_QUERY = `
74
79
  totalDrivers
75
80
  availableDrivers
76
81
  onDeliveryDrivers
82
+ avgDriverRating
77
83
  }
78
84
  financeMetrics {
79
85
  revenue {
@@ -165,11 +171,14 @@ export function useAnalytics(_options: UseAnalyticsOptions = {}): UseAnalyticsRe
165
171
  totalOrders: number;
166
172
  totalRevenue: number;
167
173
  activeVehicles: number;
174
+ onTimeDeliveryRate: number;
175
+ customerSatisfaction: number;
168
176
  };
169
177
  fleetStats: {
170
178
  totalVehicles: number;
171
179
  activeVehicles: number;
172
180
  maintenanceVehicles: number;
181
+ avgDriverRating: number;
173
182
  };
174
183
  }>(graphQLOptions, ANALYTICS_QUERY);
175
184
 
@@ -178,8 +187,8 @@ export function useAnalytics(_options: UseAnalyticsOptions = {}): UseAnalyticsRe
178
187
  totalOrders: data.dashboardMetrics.totalOrders,
179
188
  totalRevenue: data.dashboardMetrics.totalRevenue,
180
189
  activeVehicles: data.dashboardMetrics.activeVehicles,
181
- onTimeDeliveryRate: 0,
182
- customerSatisfaction: 0,
190
+ onTimeDeliveryRate: data.dashboardMetrics.onTimeDeliveryRate,
191
+ customerSatisfaction: data.dashboardMetrics.customerSatisfaction,
183
192
  },
184
193
  trends: {
185
194
  dailyOrders: data.analytics.orderTrend.map((item) => ({
@@ -209,10 +218,10 @@ export function useAnalytics(_options: UseAnalyticsOptions = {}): UseAnalyticsRe
209
218
  },
210
219
  performance: {
211
220
  onTimePickup: 0,
212
- onTimeDelivery: 0,
221
+ onTimeDelivery: data.dashboardMetrics.onTimeDeliveryRate,
213
222
  damagedItems: 0,
214
223
  customerComplaints: 0,
215
- driverRatings: 0,
224
+ driverRatings: data.fleetStats.avgDriverRating,
216
225
  },
217
226
  });
218
227
  } catch (fetchError) {
@@ -281,6 +290,10 @@ export function useDashboardMetrics() {
281
290
  setError(null);
282
291
  try {
283
292
  const data = await executeGraphQL<{
293
+ dashboardMetrics: {
294
+ onTimeDeliveryRate: number;
295
+ customerSatisfaction: number;
296
+ };
284
297
  orderStats: {
285
298
  total: number;
286
299
  pending: number;
@@ -292,6 +305,7 @@ export function useDashboardMetrics() {
292
305
  activeVehicles: number;
293
306
  totalDrivers: number;
294
307
  availableDrivers: number;
308
+ avgDriverRating: number;
295
309
  };
296
310
  financeMetrics: {
297
311
  revenue: {
@@ -321,8 +335,8 @@ export function useDashboardMetrics() {
321
335
  currency: 'USD',
322
336
  },
323
337
  performance: {
324
- onTimeDeliveryRate: 0,
325
- avgDriverRating: 0,
338
+ onTimeDeliveryRate: data.dashboardMetrics.onTimeDeliveryRate,
339
+ avgDriverRating: data.fleetStats.avgDriverRating,
326
340
  },
327
341
  });
328
342
  } catch (fetchError) {
@@ -0,0 +1,74 @@
1
+ import { useCallback, useMemo, useState } from 'react';
2
+ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
3
+ import { executeGraphQL } from '../utils/graphqlClient';
4
+
5
+ export interface AssistantAnswer {
6
+ answer: string;
7
+ generatedAt: string;
8
+ question: string;
9
+ }
10
+
11
+ const ASSISTANT_INSIGHTS_QUERY = `
12
+ query MovethewheelsAssistantInsights($question: String!) {
13
+ assistantInsights(question: $question) {
14
+ answer
15
+ generatedAt
16
+ question
17
+ }
18
+ }
19
+ `;
20
+
21
+ interface UseAssistantResult {
22
+ ask: (question: string) => Promise<AssistantAnswer>;
23
+ isLoading: boolean;
24
+ error: Error | null;
25
+ }
26
+
27
+ /**
28
+ * useAssistant — queries the real logistics assistant (Query.assistantInsights).
29
+ * The backend answers from the caller's own operational data; there is no
30
+ * client-side canned response.
31
+ */
32
+ export function useAssistant(): UseAssistantResult {
33
+ const { apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser } =
34
+ useMoveTheWheels();
35
+ const [isLoading, setIsLoading] = useState(false);
36
+ const [error, setError] = useState<Error | null>(null);
37
+
38
+ const graphQLOptions = useMemo(
39
+ () => ({
40
+ apiGatewayUrl,
41
+ authToken,
42
+ workspaceToken,
43
+ workspaceId,
44
+ tenantId,
45
+ actorId: currentUser?.id,
46
+ }),
47
+ [apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser?.id]
48
+ );
49
+
50
+ const ask = useCallback(
51
+ async (question: string): Promise<AssistantAnswer> => {
52
+ setIsLoading(true);
53
+ setError(null);
54
+ try {
55
+ const data = await executeGraphQL<{ assistantInsights: AssistantAnswer }>(
56
+ graphQLOptions,
57
+ ASSISTANT_INSIGHTS_QUERY,
58
+ { question }
59
+ );
60
+ return data.assistantInsights;
61
+ } catch (fetchError) {
62
+ const normalized =
63
+ fetchError instanceof Error ? fetchError : new Error('Failed to get assistant response');
64
+ setError(normalized);
65
+ throw normalized;
66
+ } finally {
67
+ setIsLoading(false);
68
+ }
69
+ },
70
+ [graphQLOptions]
71
+ );
72
+
73
+ return { ask, isLoading, error };
74
+ }
@@ -7,6 +7,24 @@ interface UseFleetOptions {
7
7
  filters?: FleetFilter;
8
8
  }
9
9
 
10
+ export interface CreateVehicleInput {
11
+ vehicleNumber: string;
12
+ /** VehicleType enum value, e.g. `TRUCK`, `VAN`. */
13
+ type: string;
14
+ make?: string;
15
+ model?: string;
16
+ licensePlate?: string;
17
+ }
18
+
19
+ export interface CreateDriverInput {
20
+ employeeId: string;
21
+ firstName: string;
22
+ lastName: string;
23
+ email?: string;
24
+ phone?: string;
25
+ licenseNumber?: string;
26
+ }
27
+
10
28
  interface UseFleetResult {
11
29
  vehicles: Vehicle[];
12
30
  drivers: Driver[];
@@ -15,6 +33,8 @@ interface UseFleetResult {
15
33
  isLoading: boolean;
16
34
  error: Error | null;
17
35
  refetch: () => void;
36
+ createVehicle: (input: CreateVehicleInput) => Promise<Vehicle>;
37
+ createDriver: (input: CreateDriverInput) => Promise<Driver>;
18
38
  }
19
39
 
20
40
  interface ApiVehicle {
@@ -189,6 +209,46 @@ const UPDATE_DRIVER_STATUS_MUTATION = `
189
209
  }
190
210
  `;
191
211
 
212
+ const CREATE_VEHICLE_MUTATION = `
213
+ mutation CreateVehicle($input: CreateVehicleInput!) {
214
+ createVehicle(input: $input) {
215
+ id
216
+ vehicleNumber
217
+ type
218
+ make
219
+ model
220
+ year
221
+ capacity
222
+ status
223
+ currentLocation
224
+ fuelLevel
225
+ mileage
226
+ createdAt
227
+ updatedAt
228
+ }
229
+ }
230
+ `;
231
+
232
+ const CREATE_DRIVER_MUTATION = `
233
+ mutation CreateDriver($input: CreateDriverInput!) {
234
+ createDriver(input: $input) {
235
+ id
236
+ userId
237
+ firstName
238
+ lastName
239
+ employeeId
240
+ licenseNumber
241
+ licenseClass
242
+ licenseExpiry
243
+ status
244
+ currentLocation
245
+ rating
246
+ vehicleId
247
+ createdAt
248
+ }
249
+ }
250
+ `;
251
+
192
252
  const FLEET_STATS_QUERY = `
193
253
  query GetFleetStats {
194
254
  fleetStats {
@@ -199,6 +259,7 @@ const FLEET_STATS_QUERY = `
199
259
  totalDrivers
200
260
  availableDrivers
201
261
  onDeliveryDrivers
262
+ avgDriverRating
202
263
  }
203
264
  }
204
265
  `;
@@ -415,6 +476,51 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
415
476
  void load();
416
477
  }, [load]);
417
478
 
479
+ const createVehicle = useCallback(
480
+ async (input: CreateVehicleInput): Promise<Vehicle> => {
481
+ const data = await executeGraphQL<{ createVehicle: ApiVehicle }>(
482
+ graphQLOptions,
483
+ CREATE_VEHICLE_MUTATION,
484
+ {
485
+ input: {
486
+ vehicleNumber: input.vehicleNumber,
487
+ type: input.type,
488
+ make: input.make,
489
+ model: input.model,
490
+ licensePlate: input.licensePlate,
491
+ },
492
+ }
493
+ );
494
+ const created = mapVehicle(data.createVehicle);
495
+ setVehicles((previous) => [created, ...previous]);
496
+ return created;
497
+ },
498
+ [graphQLOptions]
499
+ );
500
+
501
+ const createDriver = useCallback(
502
+ async (input: CreateDriverInput): Promise<Driver> => {
503
+ const data = await executeGraphQL<{ createDriver: ApiDriver }>(
504
+ graphQLOptions,
505
+ CREATE_DRIVER_MUTATION,
506
+ {
507
+ input: {
508
+ employeeId: input.employeeId,
509
+ firstName: input.firstName,
510
+ lastName: input.lastName,
511
+ email: input.email,
512
+ phone: input.phone,
513
+ licenseNumber: input.licenseNumber,
514
+ },
515
+ }
516
+ );
517
+ const created = mapDriver(data.createDriver);
518
+ setDrivers((previous) => [created, ...previous]);
519
+ return created;
520
+ },
521
+ [graphQLOptions]
522
+ );
523
+
418
524
  return {
419
525
  vehicles,
420
526
  drivers,
@@ -423,6 +529,8 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
423
529
  isLoading,
424
530
  error,
425
531
  refetch,
532
+ createVehicle,
533
+ createDriver,
426
534
  };
427
535
  }
428
536
 
@@ -712,67 +820,70 @@ export function useFleetStats() {
712
820
  const [isLoading, setIsLoading] = useState(false);
713
821
  const [error, setError] = useState<Error | null>(null);
714
822
 
823
+ const load = useCallback(async () => {
824
+ setIsLoading(true);
825
+ setError(null);
826
+ try {
827
+ const data = await executeGraphQL<{
828
+ fleetStats: {
829
+ totalVehicles: number;
830
+ activeVehicles: number;
831
+ inTransitVehicles: number;
832
+ maintenanceVehicles: number;
833
+ totalDrivers: number;
834
+ availableDrivers: number;
835
+ onDeliveryDrivers: number;
836
+ avgDriverRating: number;
837
+ };
838
+ }>(graphQLOptions, FLEET_STATS_QUERY);
839
+
840
+ setStats({
841
+ totalVehicles: data.fleetStats.totalVehicles,
842
+ totalDrivers: data.fleetStats.totalDrivers,
843
+ vehiclesByStatus: {
844
+ available: data.fleetStats.activeVehicles,
845
+ assigned: data.fleetStats.inTransitVehicles,
846
+ maintenance: data.fleetStats.maintenanceVehicles,
847
+ out_of_service: Math.max(
848
+ data.fleetStats.totalVehicles -
849
+ data.fleetStats.activeVehicles -
850
+ data.fleetStats.inTransitVehicles -
851
+ data.fleetStats.maintenanceVehicles,
852
+ 0
853
+ ),
854
+ },
855
+ driversByStatus: {
856
+ available: data.fleetStats.availableDrivers,
857
+ busy: data.fleetStats.onDeliveryDrivers,
858
+ offline: Math.max(
859
+ data.fleetStats.totalDrivers -
860
+ data.fleetStats.availableDrivers -
861
+ data.fleetStats.onDeliveryDrivers,
862
+ 0
863
+ ),
864
+ on_break: 0,
865
+ },
866
+ avgFuelLevel: 0,
867
+ avgDriverRating: data.fleetStats.avgDriverRating,
868
+ utilization:
869
+ data.fleetStats.totalVehicles > 0
870
+ ? (data.fleetStats.activeVehicles / data.fleetStats.totalVehicles) * 100
871
+ : 0,
872
+ });
873
+ } catch (fetchError) {
874
+ setError(fetchError instanceof Error ? fetchError : new Error('Failed to load fleet stats'));
875
+ } finally {
876
+ setIsLoading(false);
877
+ }
878
+ }, [graphQLOptions]);
879
+
715
880
  useEffect(() => {
716
- const load = async () => {
717
- setIsLoading(true);
718
- setError(null);
719
- try {
720
- const data = await executeGraphQL<{
721
- fleetStats: {
722
- totalVehicles: number;
723
- activeVehicles: number;
724
- inTransitVehicles: number;
725
- maintenanceVehicles: number;
726
- totalDrivers: number;
727
- availableDrivers: number;
728
- onDeliveryDrivers: number;
729
- };
730
- }>(graphQLOptions, FLEET_STATS_QUERY);
731
-
732
- setStats({
733
- totalVehicles: data.fleetStats.totalVehicles,
734
- totalDrivers: data.fleetStats.totalDrivers,
735
- vehiclesByStatus: {
736
- available: data.fleetStats.activeVehicles,
737
- assigned: data.fleetStats.inTransitVehicles,
738
- maintenance: data.fleetStats.maintenanceVehicles,
739
- out_of_service: Math.max(
740
- data.fleetStats.totalVehicles -
741
- data.fleetStats.activeVehicles -
742
- data.fleetStats.inTransitVehicles -
743
- data.fleetStats.maintenanceVehicles,
744
- 0
745
- ),
746
- },
747
- driversByStatus: {
748
- available: data.fleetStats.availableDrivers,
749
- busy: data.fleetStats.onDeliveryDrivers,
750
- offline: Math.max(
751
- data.fleetStats.totalDrivers -
752
- data.fleetStats.availableDrivers -
753
- data.fleetStats.onDeliveryDrivers,
754
- 0
755
- ),
756
- on_break: 0,
757
- },
758
- avgFuelLevel: 0,
759
- avgDriverRating: 0,
760
- utilization:
761
- data.fleetStats.totalVehicles > 0
762
- ? (data.fleetStats.activeVehicles / data.fleetStats.totalVehicles) * 100
763
- : 0,
764
- });
765
- } catch (fetchError) {
766
- setError(
767
- fetchError instanceof Error ? fetchError : new Error('Failed to load fleet stats')
768
- );
769
- } finally {
770
- setIsLoading(false);
771
- }
772
- };
881
+ void load();
882
+ }, [load]);
773
883
 
884
+ const refetch = useCallback(() => {
774
885
  void load();
775
- }, [graphQLOptions]);
886
+ }, [load]);
776
887
 
777
- return { stats, isLoading, error };
888
+ return { stats, isLoading, error, refetch };
778
889
  }
@@ -0,0 +1,101 @@
1
+ import { useCallback, useMemo, useState } from 'react';
2
+ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
3
+ import { executeGraphQL } from '../utils/graphqlClient';
4
+
5
+ export interface ImportExportResult {
6
+ success: boolean;
7
+ message: string;
8
+ payload?: Record<string, unknown> | null;
9
+ }
10
+
11
+ const EXPORT_DATA_MUTATION = `
12
+ mutation MovethewheelsExportData($input: MovethewheelsExportDataInput!) {
13
+ exportData(input: $input) {
14
+ success
15
+ message
16
+ payload
17
+ }
18
+ }
19
+ `;
20
+
21
+ const IMPORT_DATA_MUTATION = `
22
+ mutation MovethewheelsImportData($input: MovethewheelsImportDataInput!) {
23
+ importData(input: $input) {
24
+ success
25
+ message
26
+ payload
27
+ }
28
+ }
29
+ `;
30
+
31
+ interface UseImportExportResult {
32
+ exportData: (resource: string, format?: string) => Promise<ImportExportResult>;
33
+ importData: (resource: string, payload: Record<string, unknown>) => Promise<ImportExportResult>;
34
+ isBusy: boolean;
35
+ error: Error | null;
36
+ }
37
+
38
+ export function useImportExport(): UseImportExportResult {
39
+ const { apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser } =
40
+ useMoveTheWheels();
41
+ const [isBusy, setIsBusy] = useState(false);
42
+ const [error, setError] = useState<Error | null>(null);
43
+
44
+ const graphQLOptions = useMemo(
45
+ () => ({
46
+ apiGatewayUrl,
47
+ authToken,
48
+ workspaceToken,
49
+ workspaceId,
50
+ tenantId,
51
+ actorId: currentUser?.id,
52
+ }),
53
+ [apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser?.id]
54
+ );
55
+
56
+ const exportData = useCallback(
57
+ async (resource: string, format?: string): Promise<ImportExportResult> => {
58
+ setIsBusy(true);
59
+ setError(null);
60
+ try {
61
+ const data = await executeGraphQL<{ exportData: ImportExportResult }>(
62
+ graphQLOptions,
63
+ EXPORT_DATA_MUTATION,
64
+ { input: { resource, format } }
65
+ );
66
+ return data.exportData;
67
+ } catch (fetchError) {
68
+ const normalized = fetchError instanceof Error ? fetchError : new Error('Export failed');
69
+ setError(normalized);
70
+ throw normalized;
71
+ } finally {
72
+ setIsBusy(false);
73
+ }
74
+ },
75
+ [graphQLOptions]
76
+ );
77
+
78
+ const importData = useCallback(
79
+ async (resource: string, payload: Record<string, unknown>): Promise<ImportExportResult> => {
80
+ setIsBusy(true);
81
+ setError(null);
82
+ try {
83
+ const data = await executeGraphQL<{ importData: ImportExportResult }>(
84
+ graphQLOptions,
85
+ IMPORT_DATA_MUTATION,
86
+ { input: { resource, payload } }
87
+ );
88
+ return data.importData;
89
+ } catch (fetchError) {
90
+ const normalized = fetchError instanceof Error ? fetchError : new Error('Import failed');
91
+ setError(normalized);
92
+ throw normalized;
93
+ } finally {
94
+ setIsBusy(false);
95
+ }
96
+ },
97
+ [graphQLOptions]
98
+ );
99
+
100
+ return { exportData, importData, isBusy, error };
101
+ }