@burdenoff/microfe-movethewheels 2026.929.3 → 2026.930.1

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 (72) hide show
  1. package/dist/components/CustomerPicker.js +52 -48
  2. package/dist/components/EntityCombobox.js +25 -23
  3. package/dist/components/OrderItemRow.js +123 -78
  4. package/dist/generated/wspace-types.d.ts +1547 -64
  5. package/dist/hooks/useCustomers.js +55 -26
  6. package/dist/hooks/useFleet.js +236 -157
  7. package/dist/hooks/useInventory.js +214 -153
  8. package/dist/hooks/useMultiModalShipments.js +30 -26
  9. package/dist/hooks/useRoutes.js +64 -24
  10. package/dist/hooks/useTracking.js +14 -16
  11. package/dist/movethewheels/components/CustomerPicker.d.ts +5 -1
  12. package/dist/movethewheels/components/EntityCombobox.d.ts +6 -3
  13. package/dist/movethewheels/components/OrderItemRow.d.ts +7 -1
  14. package/dist/movethewheels/hooks/useCustomers.d.ts +3 -0
  15. package/dist/movethewheels/hooks/useFleet.d.ts +15 -2
  16. package/dist/movethewheels/hooks/useInventory.d.ts +15 -12
  17. package/dist/movethewheels/hooks/useMultiModalShipments.d.ts +1 -0
  18. package/dist/movethewheels/hooks/useRoutes.d.ts +7 -1
  19. package/dist/movethewheels/types/index.d.ts +33 -23
  20. package/dist/movethewheels/utils/formatters.d.ts +1 -1
  21. package/dist/movethewheels/utils/orderValidation.d.ts +11 -0
  22. package/dist/pages/CreateOrderPage.js +349 -297
  23. package/dist/pages/CustomerDetailsPage.js +6 -6
  24. package/dist/pages/CustomersPage.js +2 -2
  25. package/dist/pages/DashboardPage.js +1 -1
  26. package/dist/pages/DriverDetailsPage.js +340 -332
  27. package/dist/pages/DriversPage.js +7 -5
  28. package/dist/pages/FleetPage.js +1 -1
  29. package/dist/pages/InventoryPage.js +11 -12
  30. package/dist/pages/LiveTrackingPage.js +4 -4
  31. package/dist/pages/MultiModalShipmentPage.js +139 -125
  32. package/dist/pages/OrderDetailsPage.js +132 -140
  33. package/dist/pages/ProductDetailsPage.js +408 -249
  34. package/dist/pages/ProductsPage.js +5 -5
  35. package/dist/pages/RouteDetailsPage.js +208 -210
  36. package/dist/pages/RoutesPage.js +2 -2
  37. package/dist/pages/TrackingPage.js +16 -9
  38. package/dist/pages/WarehouseDetailsPage.js +166 -163
  39. package/dist/pages/WarehousesPage.js +29 -21
  40. package/dist/utils/orderValidation.js +18 -0
  41. package/package.json +1 -1
  42. package/src/generated/wspace-types.ts +1762 -64
  43. package/src/movethewheels/components/CustomerPicker.tsx +12 -0
  44. package/src/movethewheels/components/EntityCombobox.tsx +30 -11
  45. package/src/movethewheels/components/OrderItemRow.tsx +44 -0
  46. package/src/movethewheels/hooks/useCustomers.ts +45 -9
  47. package/src/movethewheels/hooks/useFleet.ts +214 -36
  48. package/src/movethewheels/hooks/useInventory.ts +185 -49
  49. package/src/movethewheels/hooks/useMultiModalShipments.ts +9 -7
  50. package/src/movethewheels/hooks/useRoutes.ts +87 -16
  51. package/src/movethewheels/hooks/useTracking.ts +23 -15
  52. package/src/movethewheels/pages/CreateOrderPage.tsx +66 -2
  53. package/src/movethewheels/pages/CustomerDetailsPage.tsx +14 -8
  54. package/src/movethewheels/pages/CustomersPage.tsx +5 -2
  55. package/src/movethewheels/pages/DashboardPage.tsx +3 -1
  56. package/src/movethewheels/pages/DriverDetailsPage.tsx +25 -4
  57. package/src/movethewheels/pages/DriversPage.tsx +8 -2
  58. package/src/movethewheels/pages/FleetPage.tsx +1 -1
  59. package/src/movethewheels/pages/InventoryPage.tsx +20 -16
  60. package/src/movethewheels/pages/LiveTrackingPage.tsx +6 -4
  61. package/src/movethewheels/pages/MultiModalShipmentPage.tsx +15 -1
  62. package/src/movethewheels/pages/OrderDetailsPage.tsx +25 -25
  63. package/src/movethewheels/pages/ProductDetailsPage.tsx +228 -49
  64. package/src/movethewheels/pages/ProductsPage.tsx +13 -5
  65. package/src/movethewheels/pages/RouteDetailsPage.tsx +24 -12
  66. package/src/movethewheels/pages/RoutesPage.tsx +4 -2
  67. package/src/movethewheels/pages/TrackingPage.tsx +32 -12
  68. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +42 -29
  69. package/src/movethewheels/pages/WarehousesPage.tsx +23 -15
  70. package/src/movethewheels/types/index.ts +36 -24
  71. package/src/movethewheels/utils/formatters.ts +1 -1
  72. package/src/movethewheels/utils/orderValidation.ts +53 -0
@@ -45,7 +45,8 @@ export interface CreateDriverInput {
45
45
  licenseExpiry?: string;
46
46
  /** DriverStatus enum value, e.g. `AVAILABLE`, `OFF_DUTY`. */
47
47
  status?: string;
48
- vehicleId?: string;
48
+ vehicleId?: string | null;
49
+ vehicleIds?: string[];
49
50
  rating?: number;
50
51
  notes?: string;
51
52
  hireDate?: string;
@@ -97,6 +98,7 @@ export interface ApiVehicle {
97
98
  nextServiceDueMileage?: number | null;
98
99
  createdAt: string;
99
100
  updatedAt?: string;
101
+ drivers?: Array<{ id: string }>;
100
102
  }
101
103
 
102
104
  export interface ApiDriver {
@@ -114,6 +116,7 @@ export interface ApiDriver {
114
116
  currentLocation?: Record<string, unknown> | null;
115
117
  rating?: number | null;
116
118
  vehicleId?: string | null;
119
+ vehicles?: Array<{ id: string }>;
117
120
  notes?: string | null;
118
121
  hireDate?: string | null;
119
122
  baseSalary?: number | null;
@@ -148,6 +151,9 @@ const LIST_VEHICLES_QUERY = `
148
151
  nextServiceDueMileage
149
152
  createdAt
150
153
  updatedAt
154
+ drivers {
155
+ id
156
+ }
151
157
  }
152
158
  vehiclesCount(filter: $filter)
153
159
  }
@@ -179,6 +185,9 @@ const LIST_DRIVERS_QUERY = `
179
185
  terminatedAt
180
186
  terminationReason
181
187
  createdAt
188
+ vehicles {
189
+ id
190
+ }
182
191
  }
183
192
  driversCount(filter: $filter)
184
193
  }
@@ -207,6 +216,9 @@ const GET_VEHICLE_QUERY = `
207
216
  nextServiceDueMileage
208
217
  createdAt
209
218
  updatedAt
219
+ drivers {
220
+ id
221
+ }
210
222
  }
211
223
  }
212
224
  `;
@@ -237,6 +249,9 @@ const GET_DRIVER_QUERY = `
237
249
  terminatedAt
238
250
  terminationReason
239
251
  createdAt
252
+ vehicles {
253
+ id
254
+ }
240
255
  }
241
256
  }
242
257
  `;
@@ -267,6 +282,9 @@ const UPDATE_VEHICLE_STATUS_MUTATION = `
267
282
  mileage
268
283
  createdAt
269
284
  updatedAt
285
+ drivers {
286
+ id
287
+ }
270
288
  }
271
289
  }
272
290
  `;
@@ -297,6 +315,9 @@ const ASSIGN_DRIVER_VEHICLE_MUTATION = `
297
315
  terminatedAt
298
316
  terminationReason
299
317
  createdAt
318
+ vehicles {
319
+ id
320
+ }
300
321
  }
301
322
  }
302
323
  `;
@@ -327,6 +348,9 @@ const UPDATE_DRIVER_STATUS_MUTATION = `
327
348
  terminatedAt
328
349
  terminationReason
329
350
  createdAt
351
+ vehicles {
352
+ id
353
+ }
330
354
  }
331
355
  }
332
356
  `;
@@ -351,6 +375,9 @@ const CREATE_VEHICLE_MUTATION = `
351
375
  mileage
352
376
  createdAt
353
377
  updatedAt
378
+ drivers {
379
+ id
380
+ }
354
381
  }
355
382
  }
356
383
  `;
@@ -381,6 +408,9 @@ const CREATE_DRIVER_MUTATION = `
381
408
  terminatedAt
382
409
  terminationReason
383
410
  createdAt
411
+ vehicles {
412
+ id
413
+ }
384
414
  }
385
415
  }
386
416
  `;
@@ -405,6 +435,9 @@ const UPDATE_VEHICLE_MUTATION = `
405
435
  mileage
406
436
  createdAt
407
437
  updatedAt
438
+ drivers {
439
+ id
440
+ }
408
441
  }
409
442
  }
410
443
  `;
@@ -435,6 +468,9 @@ const UPDATE_DRIVER_MUTATION = `
435
468
  terminatedAt
436
469
  terminationReason
437
470
  createdAt
471
+ vehicles {
472
+ id
473
+ }
438
474
  }
439
475
  }
440
476
  `;
@@ -579,7 +615,8 @@ export function mapVehicle(vehicle: ApiVehicle): Vehicle {
579
615
  pallets: typeof vehicle.capacity?.pallets === 'number' ? vehicle.capacity.pallets : undefined,
580
616
  },
581
617
  status: mapVehicleStatus(vehicle.status),
582
- driverId: undefined,
618
+ driverIds: (vehicle.drivers ?? []).map((driver) => driver.id),
619
+ driverId: vehicle.drivers?.[0]?.id,
583
620
  currentLocation: parseCurrentLocation(vehicle.currentLocation),
584
621
  lastMaintenance: vehicle.lastServiceDate ? new Date(vehicle.lastServiceDate) : undefined,
585
622
  nextMaintenance: vehicle.nextServiceDueDate ? new Date(vehicle.nextServiceDueDate) : undefined,
@@ -604,11 +641,16 @@ export function mapDriver(driver: ApiDriver): Driver {
604
641
  licenseNumber: driver.licenseNumber ?? '',
605
642
  licenseType: driver.licenseClass ?? '',
606
643
  licenseExpiry: driver.licenseExpiry ? new Date(driver.licenseExpiry) : undefined,
607
- vehicleIds: driver.vehicleId ? [driver.vehicleId] : [],
608
- assignedVehicle: driver.vehicleId ?? undefined,
644
+ vehicleIds:
645
+ driver.vehicles && driver.vehicles.length > 0
646
+ ? driver.vehicles.map((vehicle) => vehicle.id)
647
+ : driver.vehicleId
648
+ ? [driver.vehicleId]
649
+ : [],
650
+ assignedVehicle: driver.vehicles?.[0]?.id ?? driver.vehicleId ?? undefined,
609
651
  status: mapDriverStatus(driver.status),
610
652
  currentLocation: parseCurrentLocation(driver.currentLocation),
611
- rating: driver.rating ?? 0,
653
+ rating: driver.rating ?? undefined,
612
654
  notes: driver.notes ?? undefined,
613
655
  hireDate: driver.hireDate ? new Date(driver.hireDate) : undefined,
614
656
  baseSalary: driver.baseSalary ?? undefined,
@@ -656,6 +698,8 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
656
698
  const graphQLOptions = useGraphqlOptions();
657
699
  const [vehicles, setVehicles] = useState<Vehicle[]>([]);
658
700
  const [drivers, setDrivers] = useState<Driver[]>([]);
701
+ const [totalVehicles, setTotalVehicles] = useState(0);
702
+ const [totalDrivers, setTotalDrivers] = useState(0);
659
703
  const [isLoading, setIsLoading] = useState(false);
660
704
  const [error, setError] = useState<Error | null>(null);
661
705
 
@@ -664,26 +708,36 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
664
708
  setError(null);
665
709
  try {
666
710
  const [vehicleData, driverData] = await Promise.all([
667
- executeGraphQL<{ vehicles: ApiVehicle[] }>(graphQLOptions, LIST_VEHICLES_QUERY, {
668
- filter: {
669
- search: filters?.search,
670
- type: filters?.vehicleType?.toUpperCase(),
671
- status: filters?.status ? toApiVehicleStatus(filters.status) : undefined,
672
- driverId: filters?.driverId,
673
- },
674
- pagination: { limit, offset: 0 },
675
- }),
676
- executeGraphQL<{ drivers: ApiDriver[] }>(graphQLOptions, LIST_DRIVERS_QUERY, {
677
- filter: {
678
- search: filters?.search,
679
- vehicleId: filters?.vehicleId,
680
- },
681
- pagination: { limit, offset: 0 },
682
- }),
711
+ executeGraphQL<{ vehicles: ApiVehicle[]; vehiclesCount: number }>(
712
+ graphQLOptions,
713
+ LIST_VEHICLES_QUERY,
714
+ {
715
+ filter: {
716
+ search: filters?.search,
717
+ type: filters?.vehicleType?.toUpperCase(),
718
+ status: filters?.status ? toApiVehicleStatus(filters.status) : undefined,
719
+ driverId: filters?.driverId,
720
+ },
721
+ pagination: { limit, offset: 0 },
722
+ }
723
+ ),
724
+ executeGraphQL<{ drivers: ApiDriver[]; driversCount: number }>(
725
+ graphQLOptions,
726
+ LIST_DRIVERS_QUERY,
727
+ {
728
+ filter: {
729
+ search: filters?.search,
730
+ vehicleId: filters?.vehicleId,
731
+ },
732
+ pagination: { limit, offset: 0 },
733
+ }
734
+ ),
683
735
  ]);
684
736
 
685
737
  setVehicles(vehicleData.vehicles.map(mapVehicle));
686
738
  setDrivers(driverData.drivers.map(mapDriver));
739
+ setTotalVehicles(vehicleData.vehiclesCount);
740
+ setTotalDrivers(driverData.driversCount);
687
741
  } catch (fetchError) {
688
742
  setError(fetchError instanceof Error ? fetchError : new Error('Failed to load fleet data'));
689
743
  } finally {
@@ -744,6 +798,7 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
744
798
  licenseExpiry: input.licenseExpiry,
745
799
  status: input.status,
746
800
  vehicleId: input.vehicleId,
801
+ vehicleIds: input.vehicleIds,
747
802
  rating: input.rating,
748
803
  notes: input.notes,
749
804
  hireDate: input.hireDate,
@@ -806,6 +861,7 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
806
861
  licenseExpiry: input.licenseExpiry,
807
862
  status: input.status,
808
863
  vehicleId: input.vehicleId,
864
+ vehicleIds: input.vehicleIds,
809
865
  rating: input.rating,
810
866
  notes: input.notes,
811
867
  hireDate: input.hireDate,
@@ -838,8 +894,8 @@ export function useFleet(options: UseFleetOptions = {}): UseFleetResult {
838
894
  return {
839
895
  vehicles,
840
896
  drivers,
841
- totalVehicles: vehicles.length,
842
- totalDrivers: drivers.length,
897
+ totalVehicles,
898
+ totalDrivers,
843
899
  isLoading,
844
900
  error,
845
901
  refetch,
@@ -872,8 +928,11 @@ interface UseVehiclesResult {
872
928
  /** True when the last fetch returned a full page (rawCount === limit). */
873
929
  hasMore: boolean;
874
930
  isLoading: boolean;
931
+ /** True while an additional server page is appended. */
932
+ isLoadingMore: boolean;
875
933
  error: Error | null;
876
934
  refetch: () => void;
935
+ loadMore: () => Promise<void>;
877
936
  updateVehicleStatus: (id: string, status: VehicleStatus) => Promise<Vehicle>;
878
937
  assignDriver: (vehicleId: string, driverId: string) => Promise<Vehicle>;
879
938
  deleteVehicle: (id: string) => Promise<void>;
@@ -886,13 +945,16 @@ export function useVehicles(options: UseVehiclesOptions = {}): UseVehiclesResult
886
945
  const [totalVehicles, setTotalVehicles] = useState(0);
887
946
  const [hasMore, setHasMore] = useState(false);
888
947
  const [isLoading, setIsLoading] = useState(false);
948
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
889
949
  const [error, setError] = useState<Error | null>(null);
890
950
  // Stable dependency key for the `ids` array — a caller passing a fresh array
891
951
  // literal each render must not defeat the callback's memoization.
892
952
  const idsKey = ids ? [...ids].sort().join(',') : '';
953
+ const idsFilterMode = ids === undefined ? 'none' : 'exact';
954
+ const exactIds = useMemo(() => (idsKey ? idsKey.split(',') : []), [idsKey]);
893
955
 
894
956
  const load = useCallback(async () => {
895
- if (ids && ids.length === 0) {
957
+ if (idsFilterMode === 'exact' && exactIds.length === 0) {
896
958
  // Nothing to look up — skip the request and clear stale results.
897
959
  setVehicles([]);
898
960
  setTotalVehicles(0);
@@ -911,11 +973,14 @@ export function useVehicles(options: UseVehiclesOptions = {}): UseVehiclesResult
911
973
  status: status ? toApiVehicleStatus(status) : undefined,
912
974
  type: vehicleType?.toUpperCase(),
913
975
  driverId: driverId || undefined,
914
- ids: ids && ids.length > 0 ? [...ids] : undefined,
976
+ ids: exactIds.length > 0 ? exactIds : undefined,
977
+ },
978
+ pagination: {
979
+ limit: idsFilterMode === 'exact' ? exactIds.length : limit,
980
+ offset: idsFilterMode === 'exact' ? 0 : offset,
915
981
  },
916
- pagination: { limit: ids && ids.length > 0 ? ids.length : limit, offset: ids ? 0 : offset },
917
982
  });
918
- setHasMore(offset + data.vehicles.length < data.vehiclesCount);
983
+ setHasMore(idsFilterMode === 'none' && offset + data.vehicles.length < data.vehiclesCount);
919
984
  setTotalVehicles(data.vehiclesCount);
920
985
  setVehicles(data.vehicles.map(mapVehicle));
921
986
  } catch (fetchError) {
@@ -923,7 +988,65 @@ export function useVehicles(options: UseVehiclesOptions = {}): UseVehiclesResult
923
988
  } finally {
924
989
  setIsLoading(false);
925
990
  }
926
- }, [graphQLOptions, limit, offset, search, status, vehicleType, driverId, idsKey]);
991
+ }, [
992
+ graphQLOptions,
993
+ limit,
994
+ offset,
995
+ search,
996
+ status,
997
+ vehicleType,
998
+ driverId,
999
+ idsFilterMode,
1000
+ exactIds,
1001
+ ]);
1002
+
1003
+ const loadMore = useCallback(async () => {
1004
+ if (idsFilterMode === 'exact' || !hasMore || isLoading || isLoadingMore) return;
1005
+ const nextOffset = offset + vehicles.length;
1006
+ setIsLoadingMore(true);
1007
+ setError(null);
1008
+ try {
1009
+ const data = await executeGraphQL<{ vehicles: ApiVehicle[]; vehiclesCount: number }>(
1010
+ graphQLOptions,
1011
+ LIST_VEHICLES_QUERY,
1012
+ {
1013
+ filter: {
1014
+ search: search || undefined,
1015
+ status: status ? toApiVehicleStatus(status) : undefined,
1016
+ type: vehicleType?.toUpperCase(),
1017
+ driverId: driverId || undefined,
1018
+ },
1019
+ pagination: { limit, offset: nextOffset },
1020
+ }
1021
+ );
1022
+ const mapped = data.vehicles.map(mapVehicle);
1023
+ setTotalVehicles(data.vehiclesCount);
1024
+ setHasMore(nextOffset + mapped.length < data.vehiclesCount);
1025
+ setVehicles((previous) => {
1026
+ const existingIds = new Set(previous.map((vehicle) => vehicle.id));
1027
+ return [...previous, ...mapped.filter((vehicle) => !existingIds.has(vehicle.id))];
1028
+ });
1029
+ } catch (fetchError) {
1030
+ setError(
1031
+ fetchError instanceof Error ? fetchError : new Error('Failed to load more vehicles')
1032
+ );
1033
+ } finally {
1034
+ setIsLoadingMore(false);
1035
+ }
1036
+ }, [
1037
+ driverId,
1038
+ graphQLOptions,
1039
+ hasMore,
1040
+ idsFilterMode,
1041
+ isLoading,
1042
+ isLoadingMore,
1043
+ limit,
1044
+ offset,
1045
+ search,
1046
+ status,
1047
+ vehicleType,
1048
+ vehicles.length,
1049
+ ]);
927
1050
 
928
1051
  useEffect(() => {
929
1052
  void load();
@@ -960,15 +1083,17 @@ export function useVehicles(options: UseVehiclesOptions = {}): UseVehiclesResult
960
1083
  vehicleId,
961
1084
  }
962
1085
  );
963
- const next = vehicles.find((item) => item.id === vehicleId);
964
- if (!next) {
965
- throw new Error('Vehicle not found');
966
- }
967
- const updated = { ...next, driverId, status: 'assigned' as VehicleStatus };
1086
+ const data = await executeGraphQL<{ vehicle: ApiVehicle | null }>(
1087
+ graphQLOptions,
1088
+ GET_VEHICLE_QUERY,
1089
+ { id: vehicleId }
1090
+ );
1091
+ if (!data.vehicle) throw new Error('Vehicle not found');
1092
+ const updated = mapVehicle(data.vehicle);
968
1093
  setVehicles((previous) => previous.map((item) => (item.id === vehicleId ? updated : item)));
969
1094
  return updated;
970
1095
  },
971
- [graphQLOptions, vehicles]
1096
+ [graphQLOptions]
972
1097
  );
973
1098
 
974
1099
  const deleteVehicle = useCallback(
@@ -986,8 +1111,10 @@ export function useVehicles(options: UseVehiclesOptions = {}): UseVehiclesResult
986
1111
  totalVehicles,
987
1112
  hasMore,
988
1113
  isLoading,
1114
+ isLoadingMore,
989
1115
  error,
990
1116
  refetch,
1117
+ loadMore,
991
1118
  updateVehicleStatus,
992
1119
  assignDriver,
993
1120
  deleteVehicle,
@@ -1041,10 +1168,13 @@ interface UseDriversResult {
1041
1168
  /** True when the last fetch returned a full page (rawCount === limit). */
1042
1169
  hasMore: boolean;
1043
1170
  isLoading: boolean;
1171
+ /** True while an additional server page is appended. */
1172
+ isLoadingMore: boolean;
1044
1173
  error: Error | null;
1045
1174
  refetch: () => void;
1175
+ loadMore: () => Promise<void>;
1046
1176
  updateDriverStatus: (id: string, status: DriverStatus) => Promise<Driver>;
1047
- assignVehicle: (driverId: string, vehicleId: string) => Promise<Driver>;
1177
+ assignVehicle: (driverId: string, vehicleId: string | null) => Promise<Driver>;
1048
1178
  }
1049
1179
 
1050
1180
  interface UseDriversOptions extends UseListOptions {
@@ -1063,6 +1193,7 @@ export function useDrivers(options: UseDriversOptions = {}): UseDriversResult {
1063
1193
  const [totalDrivers, setTotalDrivers] = useState(0);
1064
1194
  const [hasMore, setHasMore] = useState(false);
1065
1195
  const [isLoading, setIsLoading] = useState(false);
1196
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
1066
1197
  const [error, setError] = useState<Error | null>(null);
1067
1198
 
1068
1199
  const load = useCallback(async () => {
@@ -1091,6 +1222,51 @@ export function useDrivers(options: UseDriversOptions = {}): UseDriversResult {
1091
1222
  }
1092
1223
  }, [graphQLOptions, limit, offset, status, search, licenseClass, vehicleId]);
1093
1224
 
1225
+ const loadMore = useCallback(async () => {
1226
+ if (!hasMore || isLoading || isLoadingMore) return;
1227
+ const nextOffset = offset + drivers.length;
1228
+ setIsLoadingMore(true);
1229
+ setError(null);
1230
+ try {
1231
+ const data = await executeGraphQL<{ drivers: ApiDriver[]; driversCount: number }>(
1232
+ graphQLOptions,
1233
+ LIST_DRIVERS_QUERY,
1234
+ {
1235
+ filter: {
1236
+ status: status ? toApiDriverStatus(status) : undefined,
1237
+ search: search || undefined,
1238
+ licenseClass: licenseClass || undefined,
1239
+ vehicleId: vehicleId || undefined,
1240
+ },
1241
+ pagination: { limit, offset: nextOffset },
1242
+ }
1243
+ );
1244
+ const mapped = data.drivers.map(mapDriver);
1245
+ setTotalDrivers(data.driversCount);
1246
+ setHasMore(nextOffset + mapped.length < data.driversCount);
1247
+ setDrivers((previous) => {
1248
+ const existingIds = new Set(previous.map((driver) => driver.id));
1249
+ return [...previous, ...mapped.filter((driver) => !existingIds.has(driver.id))];
1250
+ });
1251
+ } catch (fetchError) {
1252
+ setError(fetchError instanceof Error ? fetchError : new Error('Failed to load more drivers'));
1253
+ } finally {
1254
+ setIsLoadingMore(false);
1255
+ }
1256
+ }, [
1257
+ drivers.length,
1258
+ graphQLOptions,
1259
+ hasMore,
1260
+ isLoading,
1261
+ isLoadingMore,
1262
+ limit,
1263
+ licenseClass,
1264
+ offset,
1265
+ search,
1266
+ status,
1267
+ vehicleId,
1268
+ ]);
1269
+
1094
1270
  useEffect(() => {
1095
1271
  void load();
1096
1272
  }, [load]);
@@ -1117,7 +1293,7 @@ export function useDrivers(options: UseDriversOptions = {}): UseDriversResult {
1117
1293
  );
1118
1294
 
1119
1295
  const assignVehicle = useCallback(
1120
- async (driverId: string, vehicleId: string): Promise<Driver> => {
1296
+ async (driverId: string, vehicleId: string | null): Promise<Driver> => {
1121
1297
  const data = await executeGraphQL<{ assignDriverVehicle: ApiDriver }>(
1122
1298
  graphQLOptions,
1123
1299
  ASSIGN_DRIVER_VEHICLE_MUTATION,
@@ -1138,8 +1314,10 @@ export function useDrivers(options: UseDriversOptions = {}): UseDriversResult {
1138
1314
  totalDrivers,
1139
1315
  hasMore,
1140
1316
  isLoading,
1317
+ isLoadingMore,
1141
1318
  error,
1142
1319
  refetch,
1320
+ loadMore,
1143
1321
  updateDriverStatus,
1144
1322
  assignVehicle,
1145
1323
  };