import { useState, useCallback, useEffect } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { api } from '../services/api'; import type { ScanPreset } from '@icc/shared'; interface ScanStartResponse { jobId: string; dateRange: any; status: string; } interface ScanStatusResponse { jobId: string; status: string; dateRange: any; progress: { emailsFound: number; emailsProcessed: number; emailsSkipped: number; emailsErrored: number; currentEmail?: string; }; startedAt: string; completedAt?: string; } export function useEmailScan() { const queryClient = useQueryClient(); const [activeJobId, setActiveJobId] = useState(null); const startScanMutation = useMutation({ mutationFn: (params: { preset: ScanPreset; startDate?: string; endDate?: string; forceRescan?: boolean; }) => api.post('/scan/start', params), onSuccess: (data) => { setActiveJobId(data.jobId); }, }); const scanStatusQuery = useQuery({ queryKey: ['scanStatus', activeJobId], queryFn: () => api.get(`/scan/status/${activeJobId}`), enabled: !!activeJobId, retry: (failureCount, error: any) => { // Stop retrying on 404 (stale job ID after server restart) if (error?.status === 404 || error?.response?.status === 404) return false; return failureCount < 3; }, refetchInterval: (query) => { const data = query.state.data; if (data?.status === 'completed' || data?.status === 'failed') { return false; // Stop polling } // Stop polling on error (404, network error, etc.) if (query.state.status === 'error') { return false; } return 1000; // Poll every 1s while scanning }, }); const startScan = useCallback(( preset: ScanPreset, options?: { startDate?: string; endDate?: string; forceRescan?: boolean } ) => { startScanMutation.mutate({ preset, ...options }); }, [startScanMutation]); // Clear stale job ID on 404 (server restarted, job no longer exists) useEffect(() => { if (activeJobId && scanStatusQuery.isError) { setActiveJobId(null); } }, [activeJobId, scanStatusQuery.isError]); const isScanning = scanStatusQuery.data?.status === 'scanning' || scanStatusQuery.data?.status === 'parsing' || scanStatusQuery.data?.status === 'queued'; const clearJob = useCallback(() => { setActiveJobId(null); queryClient.invalidateQueries({ queryKey: ['transactions'] }); queryClient.invalidateQueries({ queryKey: ['transactionStats'] }); }, [queryClient]); return { startScan, isScanning, activeJobId, scanStatus: scanStatusQuery.data ?? null, scanError: startScanMutation.error, isStarting: startScanMutation.isPending, clearJob, }; } export function useScanHistory() { return useQuery({ queryKey: ['scanHistory'], queryFn: () => api.get<{ scans: any[] }>('/scan/history'), }); }