import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { config } from '../../config'; import { useAuthStore } from '../../auth/auth-store'; import { useRefreshInterval } from './use-refresh-interval'; function authHeaders() { const token = useAuthStore.getState().accessToken; return { Authorization: `Bearer ${token}`, 'X-Cameleer-Protocol-Version': '1', }; } async function fetchJson(path: string, params?: Record): Promise { const qs = new URLSearchParams(); if (params) { for (const [k, v] of Object.entries(params)) { if (v != null) qs.set(k, v); } } const url = `${config.apiBaseUrl}${path}${qs.toString() ? `?${qs}` : ''}`; const res = await fetch(url, { headers: authHeaders() }); if (!res.ok) throw new Error(`Failed to fetch ${path}`); return res.json(); } // ── Timeseries by app (L1 charts) ───────────────────────────────────── export interface TimeseriesBucket { time: string; totalCount: number; failedCount: number; avgDurationMs: number; p99DurationMs: number; activeCount: number; } export interface GroupedTimeseries { [key: string]: { buckets: TimeseriesBucket[] }; } export function useTimeseriesByApp(from?: string, to?: string) { const refetchInterval = useRefreshInterval(30_000); return useQuery({ queryKey: ['dashboard', 'timeseries-by-app', from, to], queryFn: () => fetchJson('/search/stats/timeseries/by-app', { from, to, buckets: '24', }), enabled: !!from, placeholderData: (prev: GroupedTimeseries | undefined) => prev, refetchInterval, }); } // ── Timeseries by route (L2 charts) ─────────────────────────────────── export function useTimeseriesByRoute(from?: string, to?: string, application?: string) { const refetchInterval = useRefreshInterval(30_000); return useQuery({ queryKey: ['dashboard', 'timeseries-by-route', from, to, application], queryFn: () => fetchJson('/search/stats/timeseries/by-route', { from, to, application, buckets: '24', }), enabled: !!from && !!application, placeholderData: (prev: GroupedTimeseries | undefined) => prev, refetchInterval, }); } // ── Top errors (L2/L3) ──────────────────────────────────────────────── export interface TopError { errorType: string; routeId: string | null; processorId: string | null; count: number; velocity: number; trend: 'accelerating' | 'stable' | 'decelerating'; lastSeen: string; } export function useTopErrors(from?: string, to?: string, application?: string, routeId?: string) { const refetchInterval = useRefreshInterval(10_000); return useQuery({ queryKey: ['dashboard', 'top-errors', from, to, application, routeId], queryFn: () => fetchJson('/search/errors/top', { from, to, application, routeId, limit: '5', }), enabled: !!from, placeholderData: (prev: TopError[] | undefined) => prev, refetchInterval, }); } // ── Punchcard (weekday x hour heatmap, rolling 7 days) ──────────────── export interface PunchcardCell { weekday: number; hour: number; totalCount: number; failedCount: number; } export function usePunchcard(application?: string) { const refetchInterval = useRefreshInterval(60_000); return useQuery({ queryKey: ['dashboard', 'punchcard', application], queryFn: () => fetchJson('/search/stats/punchcard', { application }), placeholderData: (prev: PunchcardCell[] | undefined) => prev, refetchInterval, }); } // ── App settings ────────────────────────────────────────────────────── export interface AppSettings { appId: string; slaThresholdMs: number; healthErrorWarn: number; healthErrorCrit: number; healthSlaWarn: number; healthSlaCrit: number; createdAt: string; updatedAt: string; } export function useAppSettings(appId?: string) { return useQuery({ queryKey: ['app-settings', appId], queryFn: () => fetchJson(`/admin/app-settings/${appId}`), enabled: !!appId, staleTime: 60_000, }); } export function useAllAppSettings() { return useQuery({ queryKey: ['app-settings', 'all'], queryFn: () => fetchJson('/admin/app-settings'), staleTime: 60_000, }); } export function useUpdateAppSettings() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ appId, settings }: { appId: string; settings: Omit }) => { const token = useAuthStore.getState().accessToken; const res = await fetch(`${config.apiBaseUrl}/admin/app-settings/${appId}`, { method: 'PUT', headers: { ...authHeaders(), 'Content-Type': 'application/json' }, body: JSON.stringify(settings), }); if (!res.ok) throw new Error('Failed to update app settings'); return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['app-settings'] }); }, }); }