import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { tasksApi } from '@/lib/api/pm'; import type { Task, CreateTaskDto, UpdateTaskDto, WorkItemStatus } from '@/types/project'; import { toast } from 'sonner'; import { logger } from '@/lib/utils/logger'; // ==================== Query Hooks ==================== export function useTasks(storyId?: string) { return useQuery({ queryKey: ['tasks', storyId], queryFn: async () => { logger.debug('[useTasks] Fetching tasks...', { storyId }); try { const result = await tasksApi.list(storyId); logger.debug('[useTasks] Fetch successful:', result); return result; } catch (error) { logger.error('[useTasks] Fetch failed:', error); throw error; } }, staleTime: 5 * 60 * 1000, // 5 minutes retry: 1, }); } export function useTask(id: string) { return useQuery({ queryKey: ['tasks', id], queryFn: () => tasksApi.get(id), enabled: !!id, }); } // ==================== Mutation Hooks ==================== export function useCreateTask() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: CreateTaskDto) => tasksApi.create(data), onSuccess: (newTask) => { // Invalidate all task queries queryClient.invalidateQueries({ queryKey: ['tasks'] }); // Also invalidate story details queryClient.invalidateQueries({ queryKey: ['stories', newTask.storyId] }); toast.success('Task created successfully!'); }, onError: (error: any) => { logger.error('[useCreateTask] Error:', error); toast.error(error.response?.data?.detail || 'Failed to create task'); }, }); } export function useUpdateTask() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: string; data: UpdateTaskDto }) => tasksApi.update(id, data), onMutate: async ({ id, data }) => { await queryClient.cancelQueries({ queryKey: ['tasks', id] }); const previousTask = queryClient.getQueryData(['tasks', id]); queryClient.setQueryData(['tasks', id], (old) => ({ ...old!, ...data, })); return { previousTask }; }, onError: (error: any, variables, context) => { logger.error('[useUpdateTask] Error:', error); if (context?.previousTask) { queryClient.setQueryData(['tasks', variables.id], context.previousTask); } toast.error(error.response?.data?.detail || 'Failed to update task'); }, onSuccess: () => { toast.success('Task updated successfully!'); }, onSettled: (_, __, variables) => { queryClient.invalidateQueries({ queryKey: ['tasks', variables.id] }); queryClient.invalidateQueries({ queryKey: ['tasks'] }); }, }); } export function useDeleteTask() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => tasksApi.delete(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); queryClient.removeQueries({ queryKey: ['tasks', id] }); toast.success('Task deleted successfully!'); }, onError: (error: any) => { logger.error('[useDeleteTask] Error:', error); toast.error(error.response?.data?.detail || 'Failed to delete task'); }, }); } export function useChangeTaskStatus() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, status }: { id: string; status: WorkItemStatus }) => tasksApi.changeStatus(id, status), onMutate: async ({ id, status }) => { await queryClient.cancelQueries({ queryKey: ['tasks', id] }); const previousTask = queryClient.getQueryData(['tasks', id]); queryClient.setQueryData(['tasks', id], (old) => ({ ...old!, status, })); return { previousTask }; }, onError: (error: any, variables, context) => { logger.error('[useChangeTaskStatus] Error:', error); if (context?.previousTask) { queryClient.setQueryData(['tasks', variables.id], context.previousTask); } toast.error(error.response?.data?.detail || 'Failed to change task status'); }, onSuccess: () => { toast.success('Task status changed successfully!'); }, onSettled: (_, __, variables) => { queryClient.invalidateQueries({ queryKey: ['tasks', variables.id] }); queryClient.invalidateQueries({ queryKey: ['tasks'] }); }, }); } export function useAssignTask() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, assigneeId }: { id: string; assigneeId: string }) => tasksApi.assign(id, assigneeId), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ['tasks', variables.id] }); queryClient.invalidateQueries({ queryKey: ['tasks'] }); toast.success('Task assigned successfully!'); }, onError: (error: any) => { logger.error('[useAssignTask] Error:', error); toast.error(error.response?.data?.detail || 'Failed to assign task'); }, }); }