import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { api } from '@/lib/api'; import type { DriverLocation, GpsDevice, DriverStats, GpsStatus, GpsSettings, EnrollmentResponse, MyGpsStatus, DeviceQrInfo, LocationHistoryResponse, } from '@/types/gps'; import toast from 'react-hot-toast'; import { queryKeys } from '@/lib/query-keys'; // ============================================ // Admin GPS Hooks // ============================================ /** * Get GPS system status */ export function useGpsStatus() { return useQuery({ queryKey: queryKeys.gps.status, queryFn: async () => { const { data } = await api.get('/gps/status'); return data; }, refetchInterval: 30000, // Refresh every 30 seconds }); } /** * Get GPS settings */ export function useGpsSettings() { return useQuery({ queryKey: queryKeys.gps.settings, queryFn: async () => { const { data } = await api.get('/gps/settings'); return data; }, }); } /** * Update GPS settings */ export function useUpdateGpsSettings() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (settings: Partial) => { const { data } = await api.patch('/gps/settings', settings); return data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.gps.settings }); queryClient.invalidateQueries({ queryKey: queryKeys.gps.status }); toast.success('GPS settings updated'); }, onError: (error: any) => { toast.error(error.response?.data?.message || 'Failed to update GPS settings'); }, }); } /** * Get all enrolled GPS devices */ export function useGpsDevices() { return useQuery({ queryKey: queryKeys.gps.devices, queryFn: async () => { const { data } = await api.get('/gps/devices'); return data; }, refetchInterval: 30000, // Refresh every 30 seconds to update lastActive }); } /** * Get QR code info for an enrolled device (on demand) */ export function useDeviceQr(driverId: string | null) { return useQuery({ queryKey: driverId ? queryKeys.gps.deviceQr(driverId) : ['gps', 'devices', null, 'qr'], queryFn: async () => { const { data } = await api.get(`/gps/devices/${driverId}/qr`); return data; }, enabled: !!driverId, }); } /** * Get all active driver locations (for map) */ export function useDriverLocations() { return useQuery({ queryKey: queryKeys.gps.locations.all, queryFn: async () => { const { data } = await api.get('/gps/locations'); return data; }, refetchInterval: 15000, // Refresh every 15 seconds }); } /** * Get a specific driver's location */ export function useDriverLocation(driverId: string) { return useQuery({ queryKey: queryKeys.gps.locations.detail(driverId), queryFn: async () => { const { data } = await api.get(`/gps/locations/${driverId}`); return data; }, enabled: !!driverId, refetchInterval: 15000, }); } /** * Get driver location history (for route trail display) * By default, requests road-snapped routes from OSRM map matching */ export function useDriverLocationHistory(driverId: string | null, from?: string, to?: string) { return useQuery({ queryKey: ['gps', 'locations', driverId, 'history', from, to], queryFn: async () => { const params = new URLSearchParams(); if (from) params.append('from', from); if (to) params.append('to', to); params.append('matched', 'true'); // Request road-snapped route const { data } = await api.get(`/gps/locations/${driverId}/history?${params}`); return data; }, enabled: !!driverId, refetchInterval: 60000, // Match routes less frequently (60s) since OSRM has rate limits staleTime: 30000, // Consider data fresh for 30s }); } /** * Get driver stats */ export function useDriverStats(driverId: string, from?: string, to?: string) { return useQuery({ queryKey: queryKeys.gps.stats(driverId, from, to), queryFn: async () => { const params = new URLSearchParams(); if (from) params.append('from', from); if (to) params.append('to', to); const { data } = await api.get(`/gps/stats/${driverId}?${params.toString()}`); return data; }, enabled: !!driverId, }); } /** * Enroll a driver for GPS tracking */ export function useEnrollDriver() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ driverId, sendSignalMessage = true }) => { const { data } = await api.post(`/gps/enroll/${driverId}`, { sendSignalMessage }); return data; }, onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: queryKeys.gps.devices }); queryClient.invalidateQueries({ queryKey: queryKeys.gps.status }); queryClient.invalidateQueries({ queryKey: queryKeys.drivers.all }); if (data.signalMessageSent) { toast.success('Driver enrolled! Setup instructions sent via Signal.'); } else { toast.success('Driver enrolled! Share the setup instructions with them.'); } }, onError: (error: any) => { toast.error(error.response?.data?.message || 'Failed to enroll driver'); }, }); } /** * Unenroll a driver from GPS tracking */ export function useUnenrollDriver() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (driverId: string) => { const { data } = await api.delete(`/gps/devices/${driverId}`); return data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.gps.devices }); queryClient.invalidateQueries({ queryKey: queryKeys.gps.status }); queryClient.invalidateQueries({ queryKey: queryKeys.gps.locations.all }); queryClient.invalidateQueries({ queryKey: queryKeys.drivers.all }); toast.success('Driver unenrolled from GPS tracking'); }, onError: (error: any) => { toast.error(error.response?.data?.message || 'Failed to unenroll driver'); }, }); } // ============================================ // Driver Self-Service Hooks // ============================================ /** * Get my GPS enrollment status (for drivers) */ export function useMyGpsStatus() { return useQuery({ queryKey: queryKeys.gps.me.status, queryFn: async () => { const { data } = await api.get('/gps/me'); return data; }, }); } /** * Get my GPS stats (for drivers) */ export function useMyGpsStats(from?: string, to?: string) { return useQuery({ queryKey: queryKeys.gps.me.stats(from, to), queryFn: async () => { const params = new URLSearchParams(); if (from) params.append('from', from); if (to) params.append('to', to); const { data } = await api.get(`/gps/me/stats?${params.toString()}`); return data; }, }); } /** * Get my current location (for drivers) */ export function useMyLocation() { return useQuery({ queryKey: queryKeys.gps.me.location, queryFn: async () => { const { data } = await api.get('/gps/me/location'); return data; }, refetchInterval: 30000, }); } /** * Confirm/revoke GPS tracking consent (for drivers) */ export function useUpdateGpsConsent() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (consentGiven: boolean) => { const { data } = await api.post('/gps/me/consent', { consentGiven }); return data; }, onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: queryKeys.gps.me.status }); toast.success(data.message); }, onError: (error: any) => { toast.error(error.response?.data?.message || 'Failed to update consent'); }, }); } // ============================================ // Traccar Admin Hooks // ============================================ /** * Check Traccar setup status */ export function useTraccarSetupStatus() { return useQuery<{ needsSetup: boolean; isAvailable: boolean }>({ queryKey: queryKeys.gps.traccar.status, queryFn: async () => { const { data } = await api.get('/gps/traccar/status'); return data; }, }); } /** * Perform initial Traccar setup */ export function useTraccarSetup() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async () => { const { data } = await api.post('/gps/traccar/setup'); return data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.gps.traccar.status }); queryClient.invalidateQueries({ queryKey: queryKeys.gps.status }); toast.success('Traccar setup complete!'); }, onError: (error: any) => { toast.error(error.response?.data?.message || 'Failed to setup Traccar'); }, }); } /** * Sync VIP admins to Traccar */ export function useSyncAdminsToTraccar() { return useMutation({ mutationFn: async () => { const { data } = await api.post('/gps/traccar/sync-admins'); return data; }, onSuccess: (data: { synced: number; failed: number }) => { toast.success(`Synced ${data.synced} admins to Traccar`); }, onError: (error: any) => { toast.error(error.response?.data?.message || 'Failed to sync admins'); }, }); } /** * Get Traccar admin URL for current user */ export function useTraccarAdminUrl() { return useQuery<{ url: string; directAccess: boolean }>({ queryKey: queryKeys.gps.traccar.adminUrl, queryFn: async () => { const { data } = await api.get('/gps/traccar/admin-url'); return data; }, enabled: false, // Only fetch when explicitly called }); } /** * Open Traccar admin (fetches URL and opens in new tab) */ export function useOpenTraccarAdmin() { return useMutation({ mutationFn: async () => { const { data } = await api.get('/gps/traccar/admin-url'); return data; }, onSuccess: (data: { url: string; directAccess: boolean }) => { // Open Traccar in new tab window.open(data.url, '_blank'); }, onError: (error: any) => { toast.error(error.response?.data?.message || 'Failed to open Traccar admin'); }, }); }