@burdenoff/microfe-movethewheels 2026.925.1 → 2026.925.3
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/components/StatusBadge.js +31 -31
- package/dist/constants/index.js +19 -5
- package/dist/hooks/useCustomers.js +0 -3
- package/dist/hooks/useFleet.js +197 -175
- package/dist/hooks/useInventory.js +144 -145
- package/dist/hooks/useRoutes.js +5 -14
- package/dist/movethewheels/components/StatusBadge.d.ts +3 -1
- package/dist/movethewheels/constants/index.d.ts +2 -0
- package/dist/movethewheels/hooks/useFleet.d.ts +53 -1
- package/dist/movethewheels/hooks/useInventory.d.ts +10 -0
- package/dist/movethewheels/types/index.d.ts +29 -108
- package/dist/node_modules/lucide-react/dist/esm/icons/sticky-note.js +11 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/user-x.js +30 -0
- package/dist/pages/CustomerDetailsPage.js +666 -344
- package/dist/pages/CustomersPage.js +71 -80
- package/dist/pages/DriverDetailsPage.js +702 -421
- package/dist/pages/DriversPage.js +106 -117
- package/dist/pages/FleetPage.js +5 -13
- package/dist/pages/InventoryPage.js +4 -4
- package/dist/pages/OrderDetailsPage.js +279 -274
- package/dist/pages/RouteDetailsPage.js +300 -263
- package/dist/pages/RoutesPage.js +130 -67
- package/dist/pages/VehicleDetailsPage.js +562 -287
- package/dist/pages/VehiclesPage.js +119 -70
- package/dist/pages/WarehouseDetailsPage.js +410 -370
- package/dist/pages/WarehousesPage.js +131 -82
- package/package.json +1 -1
- package/src/movethewheels/components/StatusBadge.tsx +16 -2
- package/src/movethewheels/constants/index.ts +21 -0
- package/src/movethewheels/hooks/useCustomers.ts +0 -3
- package/src/movethewheels/hooks/useFleet.ts +236 -62
- package/src/movethewheels/hooks/useInventory.ts +76 -32
- package/src/movethewheels/hooks/useRoutes.ts +17 -12
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +376 -109
- package/src/movethewheels/pages/CustomersPage.tsx +6 -15
- package/src/movethewheels/pages/DriverDetailsPage.tsx +516 -156
- package/src/movethewheels/pages/DriversPage.tsx +28 -11
- package/src/movethewheels/pages/FleetPage.tsx +9 -11
- package/src/movethewheels/pages/InventoryPage.tsx +14 -5
- package/src/movethewheels/pages/OrderDetailsPage.tsx +12 -7
- package/src/movethewheels/pages/RouteDetailsPage.tsx +89 -41
- package/src/movethewheels/pages/RoutesPage.tsx +66 -2
- package/src/movethewheels/pages/VehicleDetailsPage.tsx +282 -20
- package/src/movethewheels/pages/VehiclesPage.tsx +68 -3
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +213 -135
- package/src/movethewheels/pages/WarehousesPage.tsx +77 -16
- package/src/movethewheels/types/index.ts +30 -108
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
3
|
-
import type { Driver, DriverStatus, FleetFilter, Vehicle, VehicleStatus } from '../types';
|
|
3
|
+
import type { Driver, DriverStatus, FleetFilter, Location, Vehicle, VehicleStatus } from '../types';
|
|
4
4
|
import { executeGraphQL } from '../utils/graphqlClient';
|
|
5
5
|
|
|
6
6
|
interface UseFleetOptions {
|
|
@@ -15,6 +15,14 @@ export interface CreateVehicleInput {
|
|
|
15
15
|
make?: string;
|
|
16
16
|
model?: string;
|
|
17
17
|
licensePlate?: string;
|
|
18
|
+
/** Free-text, e.g. "owned", "leased". */
|
|
19
|
+
ownershipType?: string;
|
|
20
|
+
year?: number;
|
|
21
|
+
/** VehicleStatus enum value, e.g. `ACTIVE`, `MAINTENANCE`. */
|
|
22
|
+
status?: string;
|
|
23
|
+
fuelType?: string;
|
|
24
|
+
fuelEfficiencyLPer100Km?: number;
|
|
25
|
+
capacity?: { weight?: number; volume?: number; pallets?: number };
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
export interface CreateDriverInput {
|
|
@@ -24,6 +32,24 @@ export interface CreateDriverInput {
|
|
|
24
32
|
email?: string;
|
|
25
33
|
phone?: string;
|
|
26
34
|
licenseNumber?: string;
|
|
35
|
+
licenseClass?: string;
|
|
36
|
+
licenseExpiry?: string;
|
|
37
|
+
/** DriverStatus enum value, e.g. `AVAILABLE`, `OFF_DUTY`. */
|
|
38
|
+
status?: string;
|
|
39
|
+
vehicleId?: string;
|
|
40
|
+
rating?: number;
|
|
41
|
+
notes?: string;
|
|
42
|
+
hireDate?: string;
|
|
43
|
+
baseSalary?: number;
|
|
44
|
+
bonusYtd?: number;
|
|
45
|
+
payCurrency?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Superset of `CreateDriverInput` with fields that only make sense on an existing driver. */
|
|
49
|
+
export interface UpdateDriverInput extends CreateDriverInput {
|
|
50
|
+
onLeaveUntil?: string | null;
|
|
51
|
+
terminatedAt?: string;
|
|
52
|
+
terminationReason?: string;
|
|
27
53
|
}
|
|
28
54
|
|
|
29
55
|
interface UseFleetResult {
|
|
@@ -37,7 +63,8 @@ interface UseFleetResult {
|
|
|
37
63
|
createVehicle: (input: CreateVehicleInput) => Promise<Vehicle>;
|
|
38
64
|
createDriver: (input: CreateDriverInput) => Promise<Driver>;
|
|
39
65
|
updateVehicle: (id: string, input: CreateVehicleInput) => Promise<Vehicle>;
|
|
40
|
-
updateDriver: (id: string, input:
|
|
66
|
+
updateDriver: (id: string, input: UpdateDriverInput) => Promise<Driver>;
|
|
67
|
+
deleteVehicle: (id: string) => Promise<void>;
|
|
41
68
|
}
|
|
42
69
|
|
|
43
70
|
export interface ApiVehicle {
|
|
@@ -52,6 +79,8 @@ export interface ApiVehicle {
|
|
|
52
79
|
capacity?: Record<string, unknown> | null;
|
|
53
80
|
status: string;
|
|
54
81
|
currentLocation?: Record<string, unknown> | null;
|
|
82
|
+
fuelType?: string | null;
|
|
83
|
+
fuelEfficiencyLPer100Km?: number | null;
|
|
55
84
|
fuelLevel?: number | null;
|
|
56
85
|
mileage?: number | null;
|
|
57
86
|
lastServiceDate?: string | null;
|
|
@@ -76,6 +105,14 @@ export interface ApiDriver {
|
|
|
76
105
|
currentLocation?: Record<string, unknown> | null;
|
|
77
106
|
rating?: number | null;
|
|
78
107
|
vehicleId?: string | null;
|
|
108
|
+
notes?: string | null;
|
|
109
|
+
hireDate?: string | null;
|
|
110
|
+
baseSalary?: number | null;
|
|
111
|
+
bonusYtd?: number | null;
|
|
112
|
+
payCurrency?: string | null;
|
|
113
|
+
onLeaveUntil?: string | null;
|
|
114
|
+
terminatedAt?: string | null;
|
|
115
|
+
terminationReason?: string | null;
|
|
79
116
|
createdAt: string;
|
|
80
117
|
}
|
|
81
118
|
|
|
@@ -93,6 +130,8 @@ const LIST_VEHICLES_QUERY = `
|
|
|
93
130
|
capacity
|
|
94
131
|
status
|
|
95
132
|
currentLocation
|
|
133
|
+
fuelType
|
|
134
|
+
fuelEfficiencyLPer100Km
|
|
96
135
|
fuelLevel
|
|
97
136
|
mileage
|
|
98
137
|
lastServiceDate
|
|
@@ -122,6 +161,14 @@ const LIST_DRIVERS_QUERY = `
|
|
|
122
161
|
currentLocation
|
|
123
162
|
rating
|
|
124
163
|
vehicleId
|
|
164
|
+
notes
|
|
165
|
+
hireDate
|
|
166
|
+
baseSalary
|
|
167
|
+
bonusYtd
|
|
168
|
+
payCurrency
|
|
169
|
+
onLeaveUntil
|
|
170
|
+
terminatedAt
|
|
171
|
+
terminationReason
|
|
125
172
|
createdAt
|
|
126
173
|
}
|
|
127
174
|
driversCount(filter: $filter)
|
|
@@ -142,6 +189,8 @@ const GET_VEHICLE_QUERY = `
|
|
|
142
189
|
capacity
|
|
143
190
|
status
|
|
144
191
|
currentLocation
|
|
192
|
+
fuelType
|
|
193
|
+
fuelEfficiencyLPer100Km
|
|
145
194
|
fuelLevel
|
|
146
195
|
mileage
|
|
147
196
|
lastServiceDate
|
|
@@ -170,11 +219,25 @@ const GET_DRIVER_QUERY = `
|
|
|
170
219
|
currentLocation
|
|
171
220
|
rating
|
|
172
221
|
vehicleId
|
|
222
|
+
notes
|
|
223
|
+
hireDate
|
|
224
|
+
baseSalary
|
|
225
|
+
bonusYtd
|
|
226
|
+
payCurrency
|
|
227
|
+
onLeaveUntil
|
|
228
|
+
terminatedAt
|
|
229
|
+
terminationReason
|
|
173
230
|
createdAt
|
|
174
231
|
}
|
|
175
232
|
}
|
|
176
233
|
`;
|
|
177
234
|
|
|
235
|
+
const DELETE_VEHICLE_MUTATION = `
|
|
236
|
+
mutation DeleteVehicle($id: ID!) {
|
|
237
|
+
deleteVehicle(id: $id)
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
|
|
178
241
|
const UPDATE_VEHICLE_STATUS_MUTATION = `
|
|
179
242
|
mutation UpdateVehicleStatus($id: ID!, $status: VehicleStatus!) {
|
|
180
243
|
updateVehicleStatus(id: $id, status: $status) {
|
|
@@ -189,6 +252,8 @@ const UPDATE_VEHICLE_STATUS_MUTATION = `
|
|
|
189
252
|
capacity
|
|
190
253
|
status
|
|
191
254
|
currentLocation
|
|
255
|
+
fuelType
|
|
256
|
+
fuelEfficiencyLPer100Km
|
|
192
257
|
fuelLevel
|
|
193
258
|
mileage
|
|
194
259
|
createdAt
|
|
@@ -214,6 +279,14 @@ const ASSIGN_DRIVER_VEHICLE_MUTATION = `
|
|
|
214
279
|
currentLocation
|
|
215
280
|
rating
|
|
216
281
|
vehicleId
|
|
282
|
+
notes
|
|
283
|
+
hireDate
|
|
284
|
+
baseSalary
|
|
285
|
+
bonusYtd
|
|
286
|
+
payCurrency
|
|
287
|
+
onLeaveUntil
|
|
288
|
+
terminatedAt
|
|
289
|
+
terminationReason
|
|
217
290
|
createdAt
|
|
218
291
|
}
|
|
219
292
|
}
|
|
@@ -236,6 +309,14 @@ const UPDATE_DRIVER_STATUS_MUTATION = `
|
|
|
236
309
|
currentLocation
|
|
237
310
|
rating
|
|
238
311
|
vehicleId
|
|
312
|
+
notes
|
|
313
|
+
hireDate
|
|
314
|
+
baseSalary
|
|
315
|
+
bonusYtd
|
|
316
|
+
payCurrency
|
|
317
|
+
onLeaveUntil
|
|
318
|
+
terminatedAt
|
|
319
|
+
terminationReason
|
|
239
320
|
createdAt
|
|
240
321
|
}
|
|
241
322
|
}
|
|
@@ -255,6 +336,8 @@ const CREATE_VEHICLE_MUTATION = `
|
|
|
255
336
|
capacity
|
|
256
337
|
status
|
|
257
338
|
currentLocation
|
|
339
|
+
fuelType
|
|
340
|
+
fuelEfficiencyLPer100Km
|
|
258
341
|
fuelLevel
|
|
259
342
|
mileage
|
|
260
343
|
createdAt
|
|
@@ -280,6 +363,14 @@ const CREATE_DRIVER_MUTATION = `
|
|
|
280
363
|
currentLocation
|
|
281
364
|
rating
|
|
282
365
|
vehicleId
|
|
366
|
+
notes
|
|
367
|
+
hireDate
|
|
368
|
+
baseSalary
|
|
369
|
+
bonusYtd
|
|
370
|
+
payCurrency
|
|
371
|
+
onLeaveUntil
|
|
372
|
+
terminatedAt
|
|
373
|
+
terminationReason
|
|
283
374
|
createdAt
|
|
284
375
|
}
|
|
285
376
|
}
|
|
@@ -299,6 +390,8 @@ const UPDATE_VEHICLE_MUTATION = `
|
|
|
299
390
|
capacity
|
|
300
391
|
status
|
|
301
392
|
currentLocation
|
|
393
|
+
fuelType
|
|
394
|
+
fuelEfficiencyLPer100Km
|
|
302
395
|
fuelLevel
|
|
303
396
|
mileage
|
|
304
397
|
createdAt
|
|
@@ -324,6 +417,14 @@ const UPDATE_DRIVER_MUTATION = `
|
|
|
324
417
|
currentLocation
|
|
325
418
|
rating
|
|
326
419
|
vehicleId
|
|
420
|
+
notes
|
|
421
|
+
hireDate
|
|
422
|
+
baseSalary
|
|
423
|
+
bonusYtd
|
|
424
|
+
payCurrency
|
|
425
|
+
onLeaveUntil
|
|
426
|
+
terminatedAt
|
|
427
|
+
terminationReason
|
|
327
428
|
createdAt
|
|
328
429
|
}
|
|
329
430
|
}
|
|
@@ -367,12 +468,20 @@ function mapVehicleStatus(status: string): VehicleStatus {
|
|
|
367
468
|
return 'assigned';
|
|
368
469
|
case 'MAINTENANCE':
|
|
369
470
|
return 'maintenance';
|
|
471
|
+
case 'DECOMMISSIONED':
|
|
472
|
+
return 'decommissioned';
|
|
370
473
|
default:
|
|
371
474
|
return 'out_of_service';
|
|
372
475
|
}
|
|
373
476
|
}
|
|
374
477
|
|
|
375
|
-
|
|
478
|
+
/**
|
|
479
|
+
* Exported so the vehicle edit form's status field can round-trip (BOFF-7909).
|
|
480
|
+
* `decommissioned` must stay distinct from `out_of_service` — collapsing them
|
|
481
|
+
* used to silently rewrite a DECOMMISSIONED vehicle to INACTIVE on any save
|
|
482
|
+
* that touched status, even without the user changing it.
|
|
483
|
+
*/
|
|
484
|
+
export function toApiVehicleStatus(status: VehicleStatus): string {
|
|
376
485
|
switch (status) {
|
|
377
486
|
case 'available':
|
|
378
487
|
return 'ACTIVE';
|
|
@@ -380,6 +489,8 @@ function toApiVehicleStatus(status: VehicleStatus): string {
|
|
|
380
489
|
return 'IN_TRANSIT';
|
|
381
490
|
case 'maintenance':
|
|
382
491
|
return 'MAINTENANCE';
|
|
492
|
+
case 'decommissioned':
|
|
493
|
+
return 'DECOMMISSIONED';
|
|
383
494
|
default:
|
|
384
495
|
return 'INACTIVE';
|
|
385
496
|
}
|
|
@@ -393,12 +504,15 @@ function mapDriverStatus(status: string): DriverStatus {
|
|
|
393
504
|
return 'busy';
|
|
394
505
|
case 'ON_BREAK':
|
|
395
506
|
return 'on_break';
|
|
507
|
+
case 'INACTIVE':
|
|
508
|
+
return 'inactive';
|
|
396
509
|
default:
|
|
397
510
|
return 'offline';
|
|
398
511
|
}
|
|
399
512
|
}
|
|
400
513
|
|
|
401
|
-
|
|
514
|
+
/** Exported so the driver edit form's status field can round-trip losslessly (BOFF-7909). */
|
|
515
|
+
export function toApiDriverStatus(status: DriverStatus): string {
|
|
402
516
|
switch (status) {
|
|
403
517
|
case 'available':
|
|
404
518
|
return 'AVAILABLE';
|
|
@@ -406,11 +520,40 @@ function toApiDriverStatus(status: DriverStatus): string {
|
|
|
406
520
|
return 'ON_DELIVERY';
|
|
407
521
|
case 'on_break':
|
|
408
522
|
return 'ON_BREAK';
|
|
523
|
+
case 'inactive':
|
|
524
|
+
return 'INACTIVE';
|
|
409
525
|
default:
|
|
410
526
|
return 'OFF_DUTY';
|
|
411
527
|
}
|
|
412
528
|
}
|
|
413
529
|
|
|
530
|
+
/**
|
|
531
|
+
* `currentLocation` is an untyped JSON blob; every real writer (the IoT
|
|
532
|
+
* telemetry consumer — `toVehicleLocation()` in
|
|
533
|
+
* wspace-movethewheels-svc/src/events/iot-location-consumer.ts — and the
|
|
534
|
+
* `updateVehicleLocation`/`updateDriverLocation`-style manual mutations)
|
|
535
|
+
* stores `lat`/`lng`/`recordedAt`, never `latitude`/`longitude`. Reading the
|
|
536
|
+
* latter (as this used to) silently produced (0, 0) for every real fix and
|
|
537
|
+
* fabricated the "last updated" timestamp as "now" on every render. Parse
|
|
538
|
+
* the actual shape instead.
|
|
539
|
+
*/
|
|
540
|
+
function parseCurrentLocation(
|
|
541
|
+
raw: Record<string, unknown> | null | undefined
|
|
542
|
+
): Location | undefined {
|
|
543
|
+
if (!raw) return undefined;
|
|
544
|
+
const lat = raw.lat as number | undefined;
|
|
545
|
+
const lng = raw.lng as number | undefined;
|
|
546
|
+
if (typeof lat !== 'number' || typeof lng !== 'number') return undefined;
|
|
547
|
+
const recordedAt = raw.recordedAt as string | undefined;
|
|
548
|
+
return {
|
|
549
|
+
latitude: lat,
|
|
550
|
+
longitude: lng,
|
|
551
|
+
address: (raw.address as string | undefined) ?? undefined,
|
|
552
|
+
accuracy: typeof raw.accuracy === 'number' ? raw.accuracy : undefined,
|
|
553
|
+
timestamp: recordedAt ? new Date(recordedAt) : undefined,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
414
557
|
export function mapVehicle(vehicle: ApiVehicle): Vehicle {
|
|
415
558
|
return {
|
|
416
559
|
id: vehicle.id,
|
|
@@ -420,7 +563,7 @@ export function mapVehicle(vehicle: ApiVehicle): Vehicle {
|
|
|
420
563
|
model: vehicle.model ?? '',
|
|
421
564
|
licensePlate: vehicle.licensePlate ?? '',
|
|
422
565
|
ownershipType: vehicle.ownershipType ?? undefined,
|
|
423
|
-
year: vehicle.year ??
|
|
566
|
+
year: vehicle.year ?? undefined,
|
|
424
567
|
capacity: {
|
|
425
568
|
weight: 0,
|
|
426
569
|
volume: 0,
|
|
@@ -428,17 +571,12 @@ export function mapVehicle(vehicle: ApiVehicle): Vehicle {
|
|
|
428
571
|
},
|
|
429
572
|
status: mapVehicleStatus(vehicle.status),
|
|
430
573
|
driverId: undefined,
|
|
431
|
-
currentLocation: vehicle.currentLocation
|
|
432
|
-
? {
|
|
433
|
-
latitude: Number((vehicle.currentLocation.latitude as number | undefined) ?? 0),
|
|
434
|
-
longitude: Number((vehicle.currentLocation.longitude as number | undefined) ?? 0),
|
|
435
|
-
address: (vehicle.currentLocation.address as string | undefined) ?? undefined,
|
|
436
|
-
timestamp: new Date(),
|
|
437
|
-
}
|
|
438
|
-
: undefined,
|
|
574
|
+
currentLocation: parseCurrentLocation(vehicle.currentLocation),
|
|
439
575
|
lastMaintenance: vehicle.lastServiceDate ? new Date(vehicle.lastServiceDate) : undefined,
|
|
440
576
|
nextMaintenance: vehicle.nextServiceDueDate ? new Date(vehicle.nextServiceDueDate) : undefined,
|
|
441
577
|
nextMaintenanceMileage: vehicle.nextServiceDueMileage ?? undefined,
|
|
578
|
+
fuelType: vehicle.fuelType ?? undefined,
|
|
579
|
+
fuelEfficiencyLPer100Km: vehicle.fuelEfficiencyLPer100Km ?? undefined,
|
|
442
580
|
fuelLevel: vehicle.fuelLevel ?? undefined,
|
|
443
581
|
mileage: vehicle.mileage ?? undefined,
|
|
444
582
|
documents: [],
|
|
@@ -460,52 +598,17 @@ export function mapDriver(driver: ApiDriver): Driver {
|
|
|
460
598
|
vehicleIds: driver.vehicleId ? [driver.vehicleId] : [],
|
|
461
599
|
assignedVehicle: driver.vehicleId ?? undefined,
|
|
462
600
|
status: mapDriverStatus(driver.status),
|
|
463
|
-
currentLocation: driver.currentLocation
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
completedOrders: 0,
|
|
474
|
-
todayOrders: 0,
|
|
475
|
-
specializations: [],
|
|
476
|
-
workingHours: {
|
|
477
|
-
startTime: '09:00',
|
|
478
|
-
endTime: '18:00',
|
|
479
|
-
workingDays: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
|
|
480
|
-
maxHoursPerDay: 8,
|
|
481
|
-
maxHoursPerWeek: 40,
|
|
482
|
-
currentHoursToday: 0,
|
|
483
|
-
currentHoursThisWeek: 0,
|
|
484
|
-
},
|
|
485
|
-
ratings: {
|
|
486
|
-
overall: driver.rating ?? 0,
|
|
487
|
-
punctuality: driver.rating ?? 0,
|
|
488
|
-
communication: driver.rating ?? 0,
|
|
489
|
-
carefulHandling: driver.rating ?? 0,
|
|
490
|
-
professionalism: driver.rating ?? 0,
|
|
491
|
-
onTimeDelivery: driver.rating ?? 0,
|
|
492
|
-
totalRatings: 0,
|
|
493
|
-
},
|
|
601
|
+
currentLocation: parseCurrentLocation(driver.currentLocation),
|
|
602
|
+
rating: driver.rating ?? 0,
|
|
603
|
+
notes: driver.notes ?? undefined,
|
|
604
|
+
hireDate: driver.hireDate ? new Date(driver.hireDate) : undefined,
|
|
605
|
+
baseSalary: driver.baseSalary ?? undefined,
|
|
606
|
+
bonusYtd: driver.bonusYtd ?? undefined,
|
|
607
|
+
payCurrency: driver.payCurrency ?? undefined,
|
|
608
|
+
onLeaveUntil: driver.onLeaveUntil ? new Date(driver.onLeaveUntil) : undefined,
|
|
609
|
+
terminatedAt: driver.terminatedAt ? new Date(driver.terminatedAt) : undefined,
|
|
610
|
+
terminationReason: driver.terminationReason ?? undefined,
|
|
494
611
|
documents: [],
|
|
495
|
-
earnings: {
|
|
496
|
-
totalEarnings: 0,
|
|
497
|
-
weeklyEarnings: 0,
|
|
498
|
-
monthlyEarnings: 0,
|
|
499
|
-
pendingPayments: 0,
|
|
500
|
-
completedDeliveries: 0,
|
|
501
|
-
bonuses: 0,
|
|
502
|
-
},
|
|
503
|
-
compliance: {
|
|
504
|
-
hoursOfService: true,
|
|
505
|
-
documentation: true,
|
|
506
|
-
vehicleInspection: true,
|
|
507
|
-
lastUpdate: new Date(),
|
|
508
|
-
},
|
|
509
612
|
createdAt: new Date(driver.createdAt),
|
|
510
613
|
};
|
|
511
614
|
}
|
|
@@ -599,6 +702,12 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
|
|
|
599
702
|
make: input.make,
|
|
600
703
|
model: input.model,
|
|
601
704
|
licensePlate: input.licensePlate,
|
|
705
|
+
ownershipType: input.ownershipType,
|
|
706
|
+
year: input.year,
|
|
707
|
+
status: input.status,
|
|
708
|
+
fuelType: input.fuelType,
|
|
709
|
+
fuelEfficiencyLPer100Km: input.fuelEfficiencyLPer100Km,
|
|
710
|
+
capacity: input.capacity,
|
|
602
711
|
},
|
|
603
712
|
}
|
|
604
713
|
);
|
|
@@ -622,6 +731,16 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
|
|
|
622
731
|
email: input.email,
|
|
623
732
|
phone: input.phone,
|
|
624
733
|
licenseNumber: input.licenseNumber,
|
|
734
|
+
licenseClass: input.licenseClass,
|
|
735
|
+
licenseExpiry: input.licenseExpiry,
|
|
736
|
+
status: input.status,
|
|
737
|
+
vehicleId: input.vehicleId,
|
|
738
|
+
rating: input.rating,
|
|
739
|
+
notes: input.notes,
|
|
740
|
+
hireDate: input.hireDate,
|
|
741
|
+
baseSalary: input.baseSalary,
|
|
742
|
+
bonusYtd: input.bonusYtd,
|
|
743
|
+
payCurrency: input.payCurrency,
|
|
625
744
|
},
|
|
626
745
|
}
|
|
627
746
|
);
|
|
@@ -645,6 +764,12 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
|
|
|
645
764
|
make: input.make,
|
|
646
765
|
model: input.model,
|
|
647
766
|
licensePlate: input.licensePlate,
|
|
767
|
+
ownershipType: input.ownershipType,
|
|
768
|
+
year: input.year,
|
|
769
|
+
status: input.status,
|
|
770
|
+
fuelType: input.fuelType,
|
|
771
|
+
fuelEfficiencyLPer100Km: input.fuelEfficiencyLPer100Km,
|
|
772
|
+
capacity: input.capacity,
|
|
648
773
|
},
|
|
649
774
|
}
|
|
650
775
|
);
|
|
@@ -656,7 +781,7 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
|
|
|
656
781
|
);
|
|
657
782
|
|
|
658
783
|
const updateDriver = useCallback(
|
|
659
|
-
async (id: string, input:
|
|
784
|
+
async (id: string, input: UpdateDriverInput): Promise<Driver> => {
|
|
660
785
|
const data = await executeGraphQL<{ updateDriver: ApiDriver }>(
|
|
661
786
|
graphQLOptions,
|
|
662
787
|
UPDATE_DRIVER_MUTATION,
|
|
@@ -668,6 +793,19 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
|
|
|
668
793
|
email: input.email,
|
|
669
794
|
phone: input.phone,
|
|
670
795
|
licenseNumber: input.licenseNumber,
|
|
796
|
+
licenseClass: input.licenseClass,
|
|
797
|
+
licenseExpiry: input.licenseExpiry,
|
|
798
|
+
status: input.status,
|
|
799
|
+
vehicleId: input.vehicleId,
|
|
800
|
+
rating: input.rating,
|
|
801
|
+
notes: input.notes,
|
|
802
|
+
hireDate: input.hireDate,
|
|
803
|
+
baseSalary: input.baseSalary,
|
|
804
|
+
bonusYtd: input.bonusYtd,
|
|
805
|
+
payCurrency: input.payCurrency,
|
|
806
|
+
onLeaveUntil: input.onLeaveUntil,
|
|
807
|
+
terminatedAt: input.terminatedAt,
|
|
808
|
+
terminationReason: input.terminationReason,
|
|
671
809
|
},
|
|
672
810
|
}
|
|
673
811
|
);
|
|
@@ -678,6 +816,16 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
|
|
|
678
816
|
[graphQLOptions]
|
|
679
817
|
);
|
|
680
818
|
|
|
819
|
+
const deleteVehicle = useCallback(
|
|
820
|
+
async (id: string): Promise<void> => {
|
|
821
|
+
await executeGraphQL<{ deleteVehicle: boolean }>(graphQLOptions, DELETE_VEHICLE_MUTATION, {
|
|
822
|
+
id,
|
|
823
|
+
});
|
|
824
|
+
setVehicles((previous) => previous.filter((item) => item.id !== id));
|
|
825
|
+
},
|
|
826
|
+
[graphQLOptions]
|
|
827
|
+
);
|
|
828
|
+
|
|
681
829
|
return {
|
|
682
830
|
vehicles,
|
|
683
831
|
drivers,
|
|
@@ -690,6 +838,7 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
|
|
|
690
838
|
createDriver,
|
|
691
839
|
updateVehicle,
|
|
692
840
|
updateDriver,
|
|
841
|
+
deleteVehicle,
|
|
693
842
|
};
|
|
694
843
|
}
|
|
695
844
|
|
|
@@ -704,6 +853,8 @@ interface UseVehiclesOptions extends UseListOptions {
|
|
|
704
853
|
vehicleType?: Vehicle['type'];
|
|
705
854
|
/** Narrows to vehicles linked to this driver server-side (VehicleFilterInput.driverId). */
|
|
706
855
|
driverId?: string;
|
|
856
|
+
/** Fetch exactly these vehicles by id, regardless of how many there are. */
|
|
857
|
+
ids?: readonly string[];
|
|
707
858
|
}
|
|
708
859
|
|
|
709
860
|
interface UseVehiclesResult {
|
|
@@ -716,18 +867,29 @@ interface UseVehiclesResult {
|
|
|
716
867
|
refetch: () => void;
|
|
717
868
|
updateVehicleStatus: (id: string, status: VehicleStatus) => Promise<Vehicle>;
|
|
718
869
|
assignDriver: (vehicleId: string, driverId: string) => Promise<Vehicle>;
|
|
870
|
+
deleteVehicle: (id: string) => Promise<void>;
|
|
719
871
|
}
|
|
720
872
|
|
|
721
873
|
export function useVehicles(options: UseVehiclesOptions = {}): UseVehiclesResult {
|
|
722
|
-
const { limit = 20, offset = 0, search, status, vehicleType, driverId } = options;
|
|
874
|
+
const { limit = 20, offset = 0, search, status, vehicleType, driverId, ids } = options;
|
|
723
875
|
const graphQLOptions = useGraphqlOptions();
|
|
724
876
|
const [vehicles, setVehicles] = useState<Vehicle[]>([]);
|
|
725
877
|
const [totalVehicles, setTotalVehicles] = useState(0);
|
|
726
878
|
const [hasMore, setHasMore] = useState(false);
|
|
727
879
|
const [isLoading, setIsLoading] = useState(false);
|
|
728
880
|
const [error, setError] = useState<Error | null>(null);
|
|
881
|
+
// Stable dependency key for the `ids` array — a caller passing a fresh array
|
|
882
|
+
// literal each render must not defeat the callback's memoization.
|
|
883
|
+
const idsKey = ids ? [...ids].sort().join(',') : '';
|
|
729
884
|
|
|
730
885
|
const load = useCallback(async () => {
|
|
886
|
+
if (ids && ids.length === 0) {
|
|
887
|
+
// Nothing to look up — skip the request and clear stale results.
|
|
888
|
+
setVehicles([]);
|
|
889
|
+
setTotalVehicles(0);
|
|
890
|
+
setHasMore(false);
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
731
893
|
setIsLoading(true);
|
|
732
894
|
setError(null);
|
|
733
895
|
try {
|
|
@@ -740,8 +902,9 @@ export function useVehicles(options: UseVehiclesOptions = {}): UseVehiclesResult
|
|
|
740
902
|
status: status ? toApiVehicleStatus(status) : undefined,
|
|
741
903
|
type: vehicleType?.toUpperCase(),
|
|
742
904
|
driverId: driverId || undefined,
|
|
905
|
+
ids: ids && ids.length > 0 ? [...ids] : undefined,
|
|
743
906
|
},
|
|
744
|
-
pagination: { limit, offset },
|
|
907
|
+
pagination: { limit: ids && ids.length > 0 ? ids.length : limit, offset: ids ? 0 : offset },
|
|
745
908
|
});
|
|
746
909
|
setHasMore(offset + data.vehicles.length < data.vehiclesCount);
|
|
747
910
|
setTotalVehicles(data.vehiclesCount);
|
|
@@ -751,7 +914,7 @@ export function useVehicles(options: UseVehiclesOptions = {}): UseVehiclesResult
|
|
|
751
914
|
} finally {
|
|
752
915
|
setIsLoading(false);
|
|
753
916
|
}
|
|
754
|
-
}, [graphQLOptions, limit, offset, search, status, vehicleType, driverId]);
|
|
917
|
+
}, [graphQLOptions, limit, offset, search, status, vehicleType, driverId, idsKey]);
|
|
755
918
|
|
|
756
919
|
useEffect(() => {
|
|
757
920
|
void load();
|
|
@@ -799,6 +962,16 @@ export function useVehicles(options: UseVehiclesOptions = {}): UseVehiclesResult
|
|
|
799
962
|
[graphQLOptions, vehicles]
|
|
800
963
|
);
|
|
801
964
|
|
|
965
|
+
const deleteVehicle = useCallback(
|
|
966
|
+
async (id: string): Promise<void> => {
|
|
967
|
+
await executeGraphQL<{ deleteVehicle: boolean }>(graphQLOptions, DELETE_VEHICLE_MUTATION, {
|
|
968
|
+
id,
|
|
969
|
+
});
|
|
970
|
+
setVehicles((previous) => previous.filter((item) => item.id !== id));
|
|
971
|
+
},
|
|
972
|
+
[graphQLOptions]
|
|
973
|
+
);
|
|
974
|
+
|
|
802
975
|
return {
|
|
803
976
|
vehicles,
|
|
804
977
|
totalVehicles,
|
|
@@ -808,6 +981,7 @@ export function useVehicles(options: UseVehiclesOptions = {}): UseVehiclesResult
|
|
|
808
981
|
refetch,
|
|
809
982
|
updateVehicleStatus,
|
|
810
983
|
assignDriver,
|
|
984
|
+
deleteVehicle,
|
|
811
985
|
};
|
|
812
986
|
}
|
|
813
987
|
|