feat: add PDF reports, timezone management, GPS QR codes, and fix GPS tracking gaps

Issue #1: QR button on GPS Devices tab for re-enrollment
Issue #2: App-wide timezone setting with TimezoneContext, useFormattedDate hook,
  and admin timezone selector. All date displays now respect the configured timezone.
Issue #3: PDF export for Accountability Roster using @react-pdf/renderer with
  professional styling matching VIPSchedulePDF. Added Signal send button.
Issue #4: Fixed GPS "teleporting" gaps - syncPositions now fetches position history
  per device instead of only latest position. Changed cron to every 30s, added
  unique constraint on deviceId+timestamp for deduplication, lowered min interval to 10s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 07:36:51 +01:00
parent 0f0f1cbf38
commit a4d360aae9
33 changed files with 2136 additions and 361 deletions

View File

@@ -8,6 +8,7 @@ import type {
GpsSettings,
EnrollmentResponse,
MyGpsStatus,
DeviceQrInfo,
} from '@/types/gps';
import toast from 'react-hot-toast';
@@ -78,6 +79,20 @@ export function useGpsDevices() {
});
}
/**
* Get QR code info for an enrolled device (on demand)
*/
export function useDeviceQr(driverId: string | null) {
return useQuery<DeviceQrInfo>({
queryKey: ['gps', 'devices', driverId, 'qr'],
queryFn: async () => {
const { data } = await api.get(`/gps/devices/${driverId}/qr`);
return data;
},
enabled: !!driverId,
});
}
/**
* Get all active driver locations (for map)
*/
@@ -88,7 +103,7 @@ export function useDriverLocations() {
const { data } = await api.get('/gps/locations');
return data;
},
refetchInterval: 30000, // Refresh every 30 seconds
refetchInterval: 15000, // Refresh every 15 seconds
});
}
@@ -103,7 +118,7 @@ export function useDriverLocation(driverId: string) {
return data;
},
enabled: !!driverId,
refetchInterval: 30000,
refetchInterval: 15000,
});
}