import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { epicsApi } from '@/lib/api/pm'; import type { Epic, CreateEpicDto, UpdateEpicDto, WorkItemStatus } from '@/types/project'; import { toast } from 'sonner'; // ==================== Query Hooks ==================== export function useEpics(projectId?: string) { return useQuery({ queryKey: ['epics', projectId], queryFn: async () => { console.log('[useEpics] Fetching epics...', { projectId }); try { const result = await epicsApi.list(projectId); console.log('[useEpics] Fetch successful:', result); return result; } catch (error) { console.error('[useEpics] Fetch failed:', error); throw error; } }, staleTime: 5 * 60 * 1000, // 5 minutes retry: 1, }); } export function useEpic(id: string) { return useQuery({ queryKey: ['epics', id], queryFn: () => epicsApi.get(id), enabled: !!id, }); } // ==================== Mutation Hooks ==================== export function useCreateEpic() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: CreateEpicDto) => epicsApi.create(data), onSuccess: (newEpic) => { // Invalidate all epic queries (including filtered by projectId) queryClient.invalidateQueries({ queryKey: ['epics'] }); // Also invalidate project details if exists queryClient.invalidateQueries({ queryKey: ['projects', newEpic.projectId] }); toast.success('Epic created successfully!'); }, onError: (error: any) => { console.error('[useCreateEpic] Error:', error); toast.error(error.response?.data?.detail || 'Failed to create epic'); }, }); } export function useUpdateEpic() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: string; data: UpdateEpicDto }) => epicsApi.update(id, data), onMutate: async ({ id, data }) => { // Cancel outgoing refetches await queryClient.cancelQueries({ queryKey: ['epics', id] }); // Snapshot previous value const previousEpic = queryClient.getQueryData(['epics', id]); // Optimistically update queryClient.setQueryData(['epics', id], (old) => ({ ...old!, ...data, })); return { previousEpic }; }, onError: (error: any, variables, context) => { console.error('[useUpdateEpic] Error:', error); // Rollback if (context?.previousEpic) { queryClient.setQueryData(['epics', variables.id], context.previousEpic); } toast.error(error.response?.data?.detail || 'Failed to update epic'); }, onSuccess: (updatedEpic) => { toast.success('Epic updated successfully!'); }, onSettled: (_, __, variables) => { queryClient.invalidateQueries({ queryKey: ['epics', variables.id] }); queryClient.invalidateQueries({ queryKey: ['epics'] }); }, }); } export function useDeleteEpic() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => epicsApi.delete(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: ['epics'] }); queryClient.removeQueries({ queryKey: ['epics', id] }); toast.success('Epic deleted successfully!'); }, onError: (error: any) => { console.error('[useDeleteEpic] Error:', error); toast.error(error.response?.data?.detail || 'Failed to delete epic'); }, }); } export function useChangeEpicStatus() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, status }: { id: string; status: WorkItemStatus }) => epicsApi.changeStatus(id, status), onMutate: async ({ id, status }) => { await queryClient.cancelQueries({ queryKey: ['epics', id] }); const previousEpic = queryClient.getQueryData(['epics', id]); queryClient.setQueryData(['epics', id], (old) => ({ ...old!, status, })); return { previousEpic }; }, onError: (error: any, variables, context) => { console.error('[useChangeEpicStatus] Error:', error); if (context?.previousEpic) { queryClient.setQueryData(['epics', variables.id], context.previousEpic); } toast.error(error.response?.data?.detail || 'Failed to change epic status'); }, onSuccess: () => { toast.success('Epic status changed successfully!'); }, onSettled: (_, __, variables) => { queryClient.invalidateQueries({ queryKey: ['epics', variables.id] }); queryClient.invalidateQueries({ queryKey: ['epics'] }); }, }); } export function useAssignEpic() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, assigneeId }: { id: string; assigneeId: string }) => epicsApi.assign(id, assigneeId), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ['epics', variables.id] }); queryClient.invalidateQueries({ queryKey: ['epics'] }); toast.success('Epic assigned successfully!'); }, onError: (error: any) => { console.error('[useAssignEpic] Error:', error); toast.error(error.response?.data?.detail || 'Failed to assign epic'); }, }); }