import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { apiClient } from '../lib/api'; import type { Job, JobCreateRequest, VttUpdateRequest, BulkDeleteRequest, BulkApproveRequest, BulkReturnToQCRequest, TTSPreferences, AccessibleVideoMethod } from '../types/api'; // Query hooks export function useJobs(filters?: { status?: string; mine?: boolean; page?: number; size?: number }, options?: { enabled?: boolean }) { return useQuery({ queryKey: ['jobs', filters], queryFn: () => apiClient.getJobs(filters), enabled: options?.enabled ?? true, staleTime: 0, // Consider data stale immediately refetchOnMount: 'always', // Always fetch fresh data when component mounts }); } export function useJob(jobId: string) { return useQuery({ queryKey: ['jobs', jobId], queryFn: () => apiClient.getJob(jobId), enabled: !!jobId, staleTime: 30000, // 30 seconds refetchOnWindowFocus: false, refetchInterval: (query) => { const status = query.state.data?.status; const processingStatuses = new Set([ 'created', 'ingesting', 'ai_processing', 'translating', 'tts_generating', 'rendering_video', 'rendering_qc' ]); return status && processingStatuses.has(status) ? 10000 : false; }, }); } const EARLY_STATUSES = new Set(['created', 'ingesting', 'ai_processing']); export function useJobDownloads(jobId: string, jobStatus?: string) { return useQuery({ queryKey: ['jobs', jobId, 'downloads'], queryFn: () => apiClient.getJobDownloads(jobId), enabled: !!jobId && (jobStatus === undefined || !EARLY_STATUSES.has(jobStatus)), staleTime: 30000, // 30 seconds refetchOnWindowFocus: false, }); } export function useJobVttContent(jobId: string, language?: string) { return useQuery({ queryKey: ['jobs', jobId, 'vtt', language || 'source'], queryFn: () => apiClient.getJobVttContent(jobId, language), enabled: !!jobId, staleTime: 30000, // 30 seconds refetchOnWindowFocus: false, }); } export function useJobValidation(jobId: string) { return useQuery({ queryKey: ['jobs', jobId, 'validation'], queryFn: () => apiClient.validateJobAssets(jobId), enabled: !!jobId, }); } // Mutation hooks export function useCreateJob() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ data, file, onUploadProgress }: { data: JobCreateRequest; file: File; onUploadProgress?: (progressEvent: { loaded: number; total: number }) => void }) => apiClient.createJob(data, file, onUploadProgress), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useUpdateJob() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: string; data: Partial }) => apiClient.updateJob(id, data), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useApproveEnglish() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, notes, tts_preferences, accessible_video_method }: { id: string; notes?: string; tts_preferences?: TTSPreferences; accessible_video_method?: AccessibleVideoMethod; }) => apiClient.approveSource(id, notes, tts_preferences, accessible_video_method), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useApproveSource() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, notes, tts_preferences, accessible_video_method }: { id: string; notes?: string; tts_preferences?: TTSPreferences; accessible_video_method?: AccessibleVideoMethod; }) => apiClient.approveSource(id, notes, tts_preferences, accessible_video_method), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useRejectJob() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, notes }: { id: string; notes: string }) => apiClient.rejectJob(id, notes), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useCompleteJob() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, notes }: { id: string; notes?: string }) => apiClient.completeJob(id, notes), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useUpdateJobVtt() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: string; data: VttUpdateRequest }) => apiClient.updateJobVttContent(id, data), onSuccess: (_, { id, data }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); // Invalidate the VTT cache for the specific language or 'source' if not specified queryClient.invalidateQueries({ queryKey: ['jobs', id, 'vtt', data.language || 'source'] }); }, }); } export function useRejectFinalReview() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, notes }: { id: string; notes: string }) => apiClient.rejectFinalReview(id, notes), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useUpdateTTSPreferences() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, tts_preferences }: { id: string; tts_preferences: TTSPreferences }) => apiClient.updateTTSPreferences(id, tts_preferences), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); // Also invalidate accessible video edit state as TTS is being regenerated queryClient.invalidateQueries({ queryKey: ['jobs', id, 'accessible-video'] }); }, }); } export function useDeleteJob() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => apiClient.deleteJob(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useBulkDeleteJobs() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: BulkDeleteRequest) => apiClient.bulkDeleteJobs(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useBulkApproveJobs() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: BulkApproveRequest) => apiClient.bulkApproveJobs(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useAdjustVttTiming() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, offsetSeconds, language = 'en', adjustCaptions = true, adjustAudioDescription = true }: { id: string; offsetSeconds: number; language?: string; adjustCaptions?: boolean; adjustAudioDescription?: boolean; }) => apiClient.adjustVttTiming(id, { offset_seconds: offsetSeconds, language, adjust_captions: adjustCaptions, adjust_audio_description: adjustAudioDescription, }), onSuccess: (_, { id, language }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs', id, 'vtt', language] }); }, }); } export function useReprocessJob() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => apiClient.reprocessJob(id), onSuccess: (_, id) => { // Invalidate both the specific job and the jobs list to reflect status changes queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useCloneJob() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => apiClient.cloneJob(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useRetryTts() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => apiClient.retryTts(id), onSuccess: (_, id) => { // Invalidate both the specific job and the jobs list to reflect status changes queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useRetryJob() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, fromStep }: { id: string; fromStep?: string }) => apiClient.retryJob(id, fromStep), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useReturnToQC() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, notes }: { id: string; notes: string }) => apiClient.returnToQC(id, { notes }), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: ['jobs', id] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useBulkReturnToQC() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: BulkReturnToQCRequest) => apiClient.bulkReturnToQC(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useBriefs() { return useQuery({ queryKey: ['briefs'], queryFn: () => apiClient.listBriefs(), refetchInterval: 60_000, }); } export function useBrief(id: string) { return useQuery({ queryKey: ['briefs', id], queryFn: () => apiClient.getBrief(id), enabled: !!id, }); } export function useCreateBrief() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: import('../types/api').JobBriefCreate) => apiClient.createBrief(data), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['briefs'] }), }); } export function useSubmitBrief() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => apiClient.submitBrief(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: ['briefs', id] }); queryClient.invalidateQueries({ queryKey: ['briefs'] }); }, }); } export function useApproveBrief() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => apiClient.approveBrief(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: ['briefs', id] }); queryClient.invalidateQueries({ queryKey: ['briefs'] }); }, }); } export function useFailures(filters?: { step?: string; org_id?: string; limit?: number; skip?: number }) { return useQuery({ queryKey: ['failures', filters], queryFn: () => apiClient.listFailures(filters), refetchInterval: 30_000, }); } export function useBulkRetry() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ job_ids, strategy }: { job_ids: string[]; strategy?: 'auto' | 'from_scratch' }) => apiClient.bulkRetry(job_ids, strategy), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['failures'] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); } export function useProductionQueueStats() { return useQuery({ queryKey: ['production-queue-stats'], queryFn: () => apiClient.getProductionQueueStats(), refetchInterval: 10_000, }); } export function useUploadFinalVtt() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ jobId, language, vttFile, vttType, }: { jobId: string; language: string; vttFile: File; vttType?: 'captions' | 'ad' }) => apiClient.uploadFinalVtt(jobId, language, vttFile, vttType), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['failures'] }); queryClient.invalidateQueries({ queryKey: ['jobs'] }); }, }); }