@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.
- package/dist/{AIAssistantPage-AvsMbunM.js → AIAssistantPage-C3cPsWt-.js} +100 -73
- package/dist/AnalyticsPage-Dz6g2MvK.js +215 -0
- package/dist/{CreateOrderPage-CjlnEtk3.js → CreateOrderPage-C5iwnjp4.js} +36 -67
- package/dist/CustomerDetailsPage-B6nwFyLO.js +406 -0
- package/dist/{CustomersPage-CGGfD4ki.js → CustomersPage-2Y05LVyh.js} +26 -28
- package/dist/{DashboardPage-D2HRqwg9.js → DashboardPage-BVjQi0EB.js} +85 -83
- package/dist/{DataTable-CD9ZhpLB.js → DataTable-DmciIKKM.js} +3 -3
- package/dist/DriverDetailsPage-CN3WQ0B9.js +507 -0
- package/dist/{DriversPage-DKiynISH.js → DriversPage-B0Zpx8OQ.js} +25 -27
- package/dist/{FinancePage-Vg8WPs38.js → FinancePage-DEDykSOP.js} +9 -9
- package/dist/FleetPage-DqkFi3s3.js +365 -0
- package/dist/FormError-BlOjnXOq.js +15 -0
- package/dist/ImportExportPage-DKJRCezl.js +290 -0
- package/dist/InventoryPage-DwfgCUQq.js +253 -0
- package/dist/{LiveTrackingPage-DQ1q4Id3.js → LiveTrackingPage-DCX-K-LR.js} +60 -56
- package/dist/MarketplacePage-CIQ97_zp.js +247 -0
- package/dist/{MetricCard-B7yMyvMX.js → MetricCard-BY0QrMD4.js} +1 -1
- package/dist/{OrderDetailsPage-CFASgsYX.js → OrderDetailsPage-DYiZ-9UK.js} +83 -85
- package/dist/{OrdersListPage-BHeT8S5o.js → OrdersListPage-BjDlCKJd.js} +31 -33
- package/dist/{PageLayout-D3N8MCTm.js → PageLayout-D4LDHJJq.js} +483 -422
- package/dist/ProductDetailsPage-C9uOiYLp.js +350 -0
- package/dist/{ProductsPage--xEpbQ8A.js → ProductsPage-C1G9h7Xd.js} +22 -24
- package/dist/ReportsPage-Zp53q3_8.js +284 -0
- package/dist/RouteDetailsPage-DUStXSf9.js +531 -0
- package/dist/RoutesPage-DMfoicM1.js +144 -0
- package/dist/SettingsPage-Bn-v0mbQ.js +421 -0
- package/dist/StatusBadge-BZurYHMy.js +152 -0
- package/dist/{TrackingPage-CcRc_dva.js → TrackingPage-W-jtdM3Y.js} +59 -61
- package/dist/VehicleDetailsPage-Bceo5oz3.js +410 -0
- package/dist/{VehiclesPage-UlxPWP9K.js → VehiclesPage-VBtOep91.js} +20 -22
- package/dist/WarehouseDetailsPage-L_6sWigM.js +493 -0
- package/dist/{WarehousesPage-CuTO-OPp.js → WarehousesPage-DCbXXXWE.js} +16 -18
- package/dist/{arrow-left-CBpZKkpq.js → arrow-left-n48Y-Wis.js} +1 -1
- package/dist/{box-CAaiElsb.js → box-D9Pal4HM.js} +1 -1
- package/dist/{chart-column-D1Ym15-S.js → chart-column-BFIJysDR.js} +1 -1
- package/dist/{chevron-right-BIFGi0UA.js → chevron-right-EMIIyyJ1.js} +1 -1
- package/dist/{circle-alert-DjmNo5_L.js → circle-alert-iY24lSlp.js} +1 -1
- package/dist/{circle-check-big-CjuPVo3_.js → circle-check-big-w0TrLKMF.js} +1 -1
- package/dist/{clock-BjkdnddD.js → clock-DJZtdl8u.js} +1 -1
- package/dist/{dollar-sign-IB4O0wuQ.js → dollar-sign-CgKQRz_e.js} +1 -1
- package/dist/{download-Dpa9vqoG.js → download-DRXxoiQR.js} +1 -1
- package/dist/{file-text-C8Uioy11.js → file-text-DWWY6_f5.js} +1 -1
- package/dist/{filter-Da5OZaHI.js → filter-DloLC2eE.js} +1 -1
- package/dist/index.js +186 -162
- package/dist/{map-_Per4sT9.js → map-CkJaULb2.js} +1 -1
- package/dist/{map-pin-BSaf5IDL.js → map-pin-BGiCvyCs.js} +1 -1
- package/dist/{navigation-DaTbrhw2.js → navigation-D36prnAn.js} +1 -1
- package/dist/{package-BE7U0wQE.js → package-BChUzpJS.js} +1 -1
- package/dist/{phone-DV4StZiA.js → phone-Ac9s3mo_.js} +1 -1
- package/dist/{plus-Dl1rLeJR.js → plus-Z76q-FZm.js} +1 -1
- package/dist/{refresh-cw-CWfERAit.js → refresh-cw-DKt6f5T6.js} +1 -1
- package/dist/{save-XH8vydOn.js → save-CojywNhG.js} +1 -1
- package/dist/{search-OxoIiL_C.js → search-CDGefqb3.js} +1 -1
- package/dist/{square-pen-hLIbFQFV.js → square-pen-CopE9GmB.js} +1 -1
- package/dist/{star-YsVV3MNQ.js → star-nHlLjDKR.js} +1 -1
- package/dist/{store-RCBl7HB-.js → store-BR9Voi5E.js} +1 -1
- package/dist/trash-2-BKhar6Hs.js +32 -0
- package/dist/{trending-up-C-85AwGT.js → trending-up-Dohgd4t_.js} +1 -1
- package/dist/{triangle-alert-BOhfGgmh.js → triangle-alert-i-XQL9Rd.js} +1 -1
- package/dist/{truck-CtDjNFbi.js → truck-C0v_hbLR.js} +1 -1
- package/dist/{useAnalytics-ph7eTIK6.js → useAnalytics-DS8n0xgg.js} +40 -40
- package/dist/useAssistant-D4v3oxD3.js +84 -0
- package/dist/{useCustomers-bS3a4ytk.js → useCustomers-BmTNGxiI.js} +24 -24
- package/dist/{useFleet-BdETplNE.js → useFleet-DKqXZ16m.js} +133 -109
- package/dist/useImportExport-DHr-SWSw.js +55 -0
- package/dist/{useInventory-Dwn18FPz.js → useInventory-DkyiVY_A.js} +95 -75
- package/dist/{useOrders-D_3_hGMp.js → useOrders-Cfk1GLiq.js} +42 -42
- package/dist/useReports-CTqZr-AB.js +78 -0
- package/dist/{useRoutes-v4aBaS-E.js → useRoutes-BQWO607N.js} +31 -31
- package/dist/useSettings-CkDH_-gM.js +81 -0
- package/dist/{useTracking-De2KIUNu.js → useTracking-B-va1kO3.js} +22 -22
- package/dist/{user-Dz5TJZFs.js → user-jyFU3lXk.js} +1 -1
- package/dist/{users-g2N0eL-Z.js → users-DZ-ilS6B.js} +1 -1
- package/dist/{warehouse-D3WWDeTX.js → warehouse-6m5kXMLu.js} +1 -1
- package/dist/{wrench-CmxY1n4I.js → wrench-CANXWxF-.js} +1 -1
- package/package.json +1 -1
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +31 -3
- package/src/movethewheels/components/FormError.tsx +25 -0
- package/src/movethewheels/components/StatusBadge.tsx +18 -37
- package/src/movethewheels/constants/index.ts +67 -26
- package/src/movethewheels/hooks/index.ts +12 -0
- package/src/movethewheels/hooks/useAnalytics.ts +20 -6
- package/src/movethewheels/hooks/useAssistant.ts +74 -0
- package/src/movethewheels/hooks/useFleet.ts +170 -59
- package/src/movethewheels/hooks/useImportExport.ts +101 -0
- package/src/movethewheels/hooks/useInventory.ts +107 -0
- package/src/movethewheels/hooks/useReports.ts +158 -0
- package/src/movethewheels/hooks/useSettings.ts +120 -0
- package/src/movethewheels/pages/AIAssistantPage.tsx +53 -15
- package/src/movethewheels/pages/AnalyticsPage.tsx +67 -64
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +154 -21
- package/src/movethewheels/pages/CustomersPage.tsx +1 -1
- package/src/movethewheels/pages/DashboardPage.tsx +42 -22
- package/src/movethewheels/pages/DriverDetailsPage.tsx +160 -3
- package/src/movethewheels/pages/DriversPage.tsx +6 -2
- package/src/movethewheels/pages/FleetPage.tsx +96 -38
- package/src/movethewheels/pages/ImportExportPage.tsx +174 -68
- package/src/movethewheels/pages/InventoryPage.tsx +66 -28
- package/src/movethewheels/pages/LiveTrackingPage.tsx +12 -3
- package/src/movethewheels/pages/MarketplacePage.tsx +87 -45
- package/src/movethewheels/pages/OrderDetailsPage.tsx +2 -2
- package/src/movethewheels/pages/ProductDetailsPage.tsx +146 -3
- package/src/movethewheels/pages/ProductsPage.tsx +5 -1
- package/src/movethewheels/pages/ReportsPage.tsx +194 -90
- package/src/movethewheels/pages/RouteDetailsPage.tsx +290 -36
- package/src/movethewheels/pages/RoutesPage.tsx +4 -9
- package/src/movethewheels/pages/SettingsPage.tsx +234 -56
- package/src/movethewheels/pages/TrackingPage.tsx +6 -6
- package/src/movethewheels/pages/VehicleDetailsPage.tsx +158 -2
- package/src/movethewheels/pages/VehiclesPage.tsx +5 -1
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +224 -2
- package/src/movethewheels/pages/WarehousesPage.tsx +5 -1
- package/src/movethewheels/providers/MoveTheWheelsProvider.tsx +7 -6
- package/src/movethewheels/store/movethewheelsStore.ts +1 -1
- package/dist/AnalyticsPage-CCLfHgQZ.js +0 -212
- package/dist/CustomerDetailsPage-I6cdDHUi.js +0 -265
- package/dist/DriverDetailsPage-rg_pGNuP.js +0 -330
- package/dist/FleetPage-DddXt8r8.js +0 -332
- package/dist/ImportExportPage-BW6qO3kc.js +0 -248
- package/dist/InventoryPage-rMGuaJN-.js +0 -222
- package/dist/MarketplacePage-DKrXAv62.js +0 -219
- package/dist/ProductDetailsPage-CFGcbaHo.js +0 -195
- package/dist/ReportsPage-Bb7_90rK.js +0 -235
- package/dist/RouteDetailsPage-CVtDVb1N.js +0 -282
- package/dist/RoutesPage-s57PtgcQ.js +0 -163
- package/dist/SettingsPage-BYNCBOpN.js +0 -298
- package/dist/StatusBadge-CijYQXtb.js +0 -209
- package/dist/VehicleDetailsPage-3YDMPsPs.js +0 -220
- package/dist/WarehouseDetailsPage-Db7KeN-d.js +0 -238
- package/dist/graphqlClient-CdJyR_ed.js +0 -55
- package/dist/navigation-BgnOfsVd.js +0 -6
- package/dist/route-Dp9JiN_E.js +0 -51
- package/dist/settings-3Rw5NwFF.js +0 -28
- package/src/movethewheels/constants/mockData.ts +0 -1342
- /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
|
|
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,
|
|
9
|
-
pending:
|
|
10
|
-
confirmed:
|
|
11
|
-
assigned:
|
|
12
|
-
picked_up:
|
|
13
|
-
in_transit:
|
|
14
|
-
out_for_delivery:
|
|
15
|
-
delivered:
|
|
16
|
-
cancelled:
|
|
17
|
-
failed:
|
|
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,
|
|
22
|
-
available:
|
|
23
|
-
assigned:
|
|
24
|
-
maintenance:
|
|
25
|
-
out_of_service:
|
|
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,
|
|
30
|
-
available:
|
|
31
|
-
busy:
|
|
32
|
-
offline:
|
|
33
|
-
on_break:
|
|
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,
|
|
38
|
-
low:
|
|
39
|
-
normal:
|
|
40
|
-
high:
|
|
41
|
-
urgent:
|
|
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:
|
|
182
|
-
customerSatisfaction:
|
|
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:
|
|
221
|
+
onTimeDelivery: data.dashboardMetrics.onTimeDeliveryRate,
|
|
213
222
|
damagedItems: 0,
|
|
214
223
|
customerComplaints: 0,
|
|
215
|
-
driverRatings:
|
|
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:
|
|
325
|
-
avgDriverRating:
|
|
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
|
-
|
|
717
|
-
|
|
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
|
-
}, [
|
|
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
|
+
}
|