@burdenoff/microfe-movethewheels 2026.929.1 → 2026.929.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-movethewheels",
3
- "version": "2026.929.1",
3
+ "version": "2026.929.3",
4
4
  "description": "Burdenoff MoveTheWheels Microfrontend - Logistics & Supply Chain Management Platform",
5
5
  "type": "module",
6
6
  "files": [
@@ -54,8 +54,8 @@ export interface CreateDriverInput {
54
54
  payCurrency?: string;
55
55
  }
56
56
 
57
- /** Superset of `CreateDriverInput` with fields that only make sense on an existing driver. */
58
- export interface UpdateDriverInput extends CreateDriverInput {
57
+ /** Fields accepted when updating an existing driver; employee IDs are immutable. */
58
+ export interface UpdateDriverInput extends Omit<CreateDriverInput, 'employeeId'> {
59
59
  onLeaveUntil?: string | null;
60
60
  terminatedAt?: string;
61
61
  terminationReason?: string;
@@ -602,7 +602,7 @@ export function mapDriver(driver: ApiDriver): Driver {
602
602
  phone: driver.phone ?? '',
603
603
  employeeId: driver.employeeId,
604
604
  licenseNumber: driver.licenseNumber ?? '',
605
- licenseType: driver.licenseClass ?? 'standard',
605
+ licenseType: driver.licenseClass ?? '',
606
606
  licenseExpiry: driver.licenseExpiry ? new Date(driver.licenseExpiry) : undefined,
607
607
  vehicleIds: driver.vehicleId ? [driver.vehicleId] : [],
608
608
  assignedVehicle: driver.vehicleId ?? undefined,
@@ -53,6 +53,7 @@ import {
53
53
  useVehicle,
54
54
  useVehicles,
55
55
  toApiDriverStatus,
56
+ type CreateDriverInput,
56
57
  type UpdateDriverInput,
57
58
  } from '../hooks/useFleet';
58
59
  import { useDebouncedValue } from '../hooks/useDebouncedValue';
@@ -163,8 +164,7 @@ function NewDriverForm({ editId }: { editId?: string }) {
163
164
 
164
165
  const goBack = () => navigate?.(joinPath(basePath, 'fleet/drivers'));
165
166
 
166
- const buildPayload = (): UpdateDriverInput => ({
167
- employeeId: form.employeeId.trim(),
167
+ const buildPayload = (includeLeaveFields = true): UpdateDriverInput => ({
168
168
  firstName: form.firstName.trim(),
169
169
  lastName: form.lastName.trim(),
170
170
  email: form.email.trim() || undefined,
@@ -174,12 +174,18 @@ function NewDriverForm({ editId }: { editId?: string }) {
174
174
  licenseExpiry: fromDateInput(form.licenseExpiry),
175
175
  status: toApiDriverStatus(form.status),
176
176
  vehicleId: form.vehicleId || undefined,
177
- rating: form.rating ? Number(form.rating) : undefined,
177
+ rating: form.rating.trim() ? Number(form.rating) : undefined,
178
178
  notes: form.notes.trim() || undefined,
179
179
  hireDate: fromDateInput(form.hireDate),
180
- baseSalary: form.baseSalary ? Number(form.baseSalary) : undefined,
181
- bonusYtd: form.bonusYtd ? Number(form.bonusYtd) : undefined,
180
+ baseSalary: form.baseSalary.trim() ? Number(form.baseSalary) : undefined,
181
+ bonusYtd: form.bonusYtd.trim() ? Number(form.bonusYtd) : undefined,
182
182
  payCurrency: form.payCurrency.trim() || undefined,
183
+ ...(includeLeaveFields ? { onLeaveUntil: fromDateInput(form.onLeaveUntil) ?? null } : {}),
184
+ });
185
+
186
+ const buildCreatePayload = (): CreateDriverInput => ({
187
+ employeeId: form.employeeId.trim(),
188
+ ...buildPayload(false),
183
189
  });
184
190
 
185
191
  const handleSubmit = async (e: React.FormEvent) => {
@@ -199,7 +205,7 @@ function NewDriverForm({ editId }: { editId?: string }) {
199
205
  const updated = await updateDriver(editId, buildPayload());
200
206
  navigate?.(joinPath(basePath, `fleet/drivers/${updated.id}`));
201
207
  } else {
202
- const created = await createDriver(buildPayload());
208
+ const created = await createDriver(buildCreatePayload());
203
209
  navigate?.(joinPath(basePath, `fleet/drivers/${created.id}`));
204
210
  }
205
211
  } catch (err) {
@@ -280,7 +286,8 @@ function NewDriverForm({ editId }: { editId?: string }) {
280
286
  <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
281
287
  <div className="space-y-2">
282
288
  <Label htmlFor="employeeId">
283
- Employee ID <span className="text-status-error-text">*</span>
289
+ Employee ID {isEditing ? '(immutable)' : ''}{' '}
290
+ <span className="text-status-error-text">{!isEditing && '*'}</span>
284
291
  </Label>
285
292
  <Input
286
293
  id="employeeId"
@@ -288,6 +295,7 @@ function NewDriverForm({ editId }: { editId?: string }) {
288
295
  onChange={(e) => setForm((prev) => ({ ...prev, employeeId: e.target.value }))}
289
296
  placeholder="EMP-001"
290
297
  required
298
+ disabled={isEditing}
291
299
  />
292
300
  </div>
293
301
  <div className="space-y-2">
@@ -445,7 +453,19 @@ function NewDriverForm({ editId }: { editId?: string }) {
445
453
  />
446
454
  </div>
447
455
  <div className="space-y-2">
448
- <Label htmlFor="onLeaveUntil">On Leave Until</Label>
456
+ <div className="flex items-center justify-between gap-2">
457
+ <Label htmlFor="onLeaveUntil">On Leave Until</Label>
458
+ {form.onLeaveUntil && (
459
+ <Button
460
+ type="button"
461
+ variant="ghost"
462
+ size="sm"
463
+ onClick={() => setForm((prev) => ({ ...prev, onLeaveUntil: '' }))}
464
+ >
465
+ Clear
466
+ </Button>
467
+ )}
468
+ </div>
449
469
  <Input
450
470
  id="onLeaveUntil"
451
471
  type="date"
@@ -823,7 +843,9 @@ export default function DriverDetailsPage() {
823
843
  {driver.currentLocation.address || 'Location updating...'}
824
844
  </p>
825
845
  <p className="text-xs text-text-secondary mt-1">
826
- Updated {formatDate(driver.currentLocation.timestamp || new Date(), 'relative')}
846
+ {driver.currentLocation.timestamp
847
+ ? `Updated ${formatDate(driver.currentLocation.timestamp, 'relative')}`
848
+ : 'Update time not recorded'}
827
849
  </p>
828
850
  </CardContent>
829
851
  </Card>
@@ -18,7 +18,6 @@ import {
18
18
  ArrowUp,
19
19
  ArrowDown,
20
20
  Fuel,
21
- Search,
22
21
  } from 'lucide-react';
23
22
  import { PageLayout, LoadingState, ErrorState } from '../components/PageLayout';
24
23
  import { StatusBadge } from '../components/StatusBadge';
@@ -57,7 +56,6 @@ import { useAssignmentSuggestions } from '../hooks/useFleetIntelligence';
57
56
  import { AssignmentSuggestions } from '../components/AssignmentSuggestions';
58
57
  import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';
59
58
  import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
60
- import { useDebouncedValue } from '../hooks/useDebouncedValue';
61
59
  import { formatDate, formatAddress, formatCurrency } from '../utils/formatters';
62
60
  import { joinPath } from '../utils/navigation';
63
61
  import { ROUTE_STATUS_COLORS } from '../constants';
@@ -76,8 +74,8 @@ function NewRouteForm({ editId }: { editId?: string }) {
76
74
  const { basePath, navigate } = useMoveTheWheels();
77
75
  const { createRoute, updateRoute } = useRoutes();
78
76
  const { route } = useRoute(editId);
79
- const [fleetSearch, setFleetSearch] = useState('');
80
- const debouncedFleetSearch = useDebouncedValue(fleetSearch);
77
+ const [vehicleSearch, setVehicleSearch] = useState('');
78
+ const [driverSearch, setDriverSearch] = useState('');
81
79
  const isEditing = !!editId;
82
80
  const [form, setForm] = useState<{
83
81
  name: string;
@@ -92,14 +90,24 @@ function NewRouteForm({ editId }: { editId?: string }) {
92
90
  driverId: '',
93
91
  stops: [{ address: '', type: 'delivery' }],
94
92
  });
95
- const { vehicles, isLoading: vehiclesLoading } = useVehicles({
93
+ const {
94
+ vehicles,
95
+ isLoading: vehiclesLoading,
96
+ hasMore: vehiclesHaveMore,
97
+ totalVehicles,
98
+ } = useVehicles({
96
99
  limit: 20,
97
- search: debouncedFleetSearch || undefined,
100
+ search: vehicleSearch || undefined,
98
101
  driverId: form.driverId || undefined,
99
102
  });
100
- const { drivers, isLoading: driversLoading } = useDrivers({
103
+ const {
104
+ drivers,
105
+ isLoading: driversLoading,
106
+ hasMore: driversHaveMore,
107
+ totalDrivers,
108
+ } = useDrivers({
101
109
  limit: 20,
102
- search: debouncedFleetSearch || undefined,
110
+ search: driverSearch || undefined,
103
111
  vehicleId: form.vehicleId || undefined,
104
112
  });
105
113
  // The selected vehicle/driver may fall outside the current search page (e.g. when
@@ -286,22 +294,6 @@ function NewRouteForm({ editId }: { editId?: string }) {
286
294
  </Select>
287
295
  </div>
288
296
  </div>
289
- <div className="space-y-2">
290
- <Label htmlFor="fleet-search">Search vehicles &amp; drivers</Label>
291
- <div className="relative">
292
- <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" />
293
- <Input
294
- id="fleet-search"
295
- value={fleetSearch}
296
- onChange={(e) => setFleetSearch(e.target.value)}
297
- placeholder="Vehicle number, driver name or employee ID"
298
- className="pl-9"
299
- />
300
- </div>
301
- <p className="text-xs text-text-secondary">
302
- Narrows both pickers below as you type — up to 20 matches at a time.
303
- </p>
304
- </div>
305
297
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
306
298
  <div className="space-y-2">
307
299
  <Label htmlFor="vehicle">Vehicle (optional)</Label>
@@ -320,10 +312,13 @@ function NewRouteForm({ editId }: { editId?: string }) {
320
312
  )}
321
313
  selectedOption={selectedVehicle}
322
314
  selectedLoading={selectedVehicleLoading}
315
+ onSearchChange={setVehicleSearch}
316
+ searchPlaceholder="Search vehicle number or type…"
317
+ hasMore={vehiclesHaveMore}
318
+ totalCount={totalVehicles}
323
319
  required
324
320
  placeholder="Assign later"
325
321
  emptyText="No available vehicles."
326
- // Search is driven by the shared "Search fleet" box above.
327
322
  />
328
323
  </div>
329
324
  <div className="space-y-2">
@@ -343,6 +338,10 @@ function NewRouteForm({ editId }: { editId?: string }) {
343
338
  )}
344
339
  selectedOption={selectedDriver}
345
340
  selectedLoading={selectedDriverLoading}
341
+ onSearchChange={setDriverSearch}
342
+ searchPlaceholder="Search driver name or employee ID…"
343
+ hasMore={driversHaveMore}
344
+ totalCount={totalDrivers}
346
345
  required
347
346
  placeholder="Assign later"
348
347
  emptyText="No available drivers."