feat: add smart flight tracking with AviationStack API + visual progress
- Add 20+ flight fields (terminal, gate, delays, estimated times, etc.) - Smart polling cron with budget-aware priority queue (100 req/month) - Tracking phases: FAR_OUT → PRE_DEPARTURE → ACTIVE → LANDED - Visual FlightProgressBar with animated airplane between airports - FlightCard with status dots, delay badges, expandable details - FlightList rewrite: card-based, grouped by status, search/filter - Dashboard: enriched flight status widget with compact progress bars - CommandCenter: flight alerts + enriched arrivals with gate/terminal Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
65
frontend/src/hooks/useFlights.ts
Normal file
65
frontend/src/hooks/useFlights.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { api } from '@/lib/api';
|
||||
import { Flight, FlightBudget } from '@/types';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
export function useFlights() {
|
||||
return useQuery<Flight[]>({
|
||||
queryKey: ['flights'],
|
||||
queryFn: async () => {
|
||||
const { data } = await api.get('/flights');
|
||||
return data;
|
||||
},
|
||||
refetchInterval: 60000, // Refresh from DB every 60s (free, no API cost)
|
||||
});
|
||||
}
|
||||
|
||||
export function useFlightBudget() {
|
||||
return useQuery<FlightBudget>({
|
||||
queryKey: ['flights', 'budget'],
|
||||
queryFn: async () => {
|
||||
const { data } = await api.get('/flights/tracking/budget');
|
||||
return data;
|
||||
},
|
||||
refetchInterval: 300000, // Every 5 min
|
||||
});
|
||||
}
|
||||
|
||||
export function useRefreshFlight() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (flightId: string) => {
|
||||
const { data } = await api.post(`/flights/${flightId}/refresh`);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['flights'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['flights', 'budget'] });
|
||||
const status = data.status || 'unknown';
|
||||
toast.success(`Flight updated: ${data.flightNumber} (${status})`);
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast.error(error.response?.data?.message || 'Failed to refresh flight');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRefreshActiveFlights() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
const { data } = await api.post('/flights/refresh-active');
|
||||
return data;
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['flights'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['flights', 'budget'] });
|
||||
toast.success(`Refreshed ${data.refreshed} flights (${data.budgetRemaining} API calls remaining)`);
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast.error(error.response?.data?.message || 'Failed to refresh flights');
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user