new features
This commit is contained in:
244
src/hooks/usePlanning.ts
Normal file
244
src/hooks/usePlanning.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { planningService } from '@/services/planningService';
|
||||
import { Sprint, Task, TaskStatus } from '@/types/planning';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
|
||||
const QUERY_KEYS = {
|
||||
sprints: ['planning', 'sprints'],
|
||||
tasks: ['planning', 'tasks'],
|
||||
task: (id: string) => ['planning', 'task', id],
|
||||
backlog: ['planning', 'backlog'],
|
||||
sprintTasks: (sprintId: string) => ['planning', 'sprint-tasks', sprintId],
|
||||
allData: ['planning', 'all'],
|
||||
};
|
||||
|
||||
// ==================== SPRINT HOOKS ====================
|
||||
|
||||
export const useSprints = () => {
|
||||
return useQuery({
|
||||
queryKey: QUERY_KEYS.sprints,
|
||||
queryFn: planningService.getSprints,
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateSprint = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { toast } = useToast();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: planningService.createSprint,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.sprints });
|
||||
toast({ title: 'Sprint created successfully' });
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast({ title: 'Failed to create sprint', description: error.message, variant: 'destructive' });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateSprint = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, updates }: { id: string; updates: Partial<Sprint> }) =>
|
||||
planningService.updateSprint(id, updates),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.sprints });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteSprint = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { toast } = useToast();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: planningService.deleteSprint,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.sprints });
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.tasks });
|
||||
toast({ title: 'Sprint deleted' });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useStartSprint = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { toast } = useToast();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: planningService.startSprint,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.sprints });
|
||||
toast({ title: 'Sprint started!' });
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast({ title: 'Cannot start sprint', description: error.message, variant: 'destructive' });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useCompleteSprint = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { toast } = useToast();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: planningService.completeSprint,
|
||||
onSuccess: ({ returnedTasks }) => {
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.sprints });
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.tasks });
|
||||
if (returnedTasks.length > 0) {
|
||||
toast({
|
||||
title: 'Sprint completed',
|
||||
description: `${returnedTasks.length} unfinished task(s) moved to backlog`
|
||||
});
|
||||
} else {
|
||||
toast({ title: 'Sprint completed successfully!' });
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== TASK HOOKS ====================
|
||||
|
||||
export const useTasks = () => {
|
||||
return useQuery({
|
||||
queryKey: QUERY_KEYS.tasks,
|
||||
queryFn: planningService.getTasks,
|
||||
});
|
||||
};
|
||||
|
||||
export const useTask = (id: string) => {
|
||||
return useQuery({
|
||||
queryKey: QUERY_KEYS.task(id),
|
||||
queryFn: () => planningService.getTask(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
|
||||
export const useBacklogTasks = () => {
|
||||
return useQuery({
|
||||
queryKey: QUERY_KEYS.backlog,
|
||||
queryFn: planningService.getBacklogTasks,
|
||||
});
|
||||
};
|
||||
|
||||
export const useSprintTasks = (sprintId: string) => {
|
||||
return useQuery({
|
||||
queryKey: QUERY_KEYS.sprintTasks(sprintId),
|
||||
queryFn: () => planningService.getSprintTasks(sprintId),
|
||||
enabled: !!sprintId,
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateTask = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { toast } = useToast();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: planningService.createTask,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.tasks });
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.backlog });
|
||||
toast({ title: 'Task created' });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateTask = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, updates }: { id: string; updates: Partial<Task> }) =>
|
||||
planningService.updateTask(id, updates),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.tasks });
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.backlog });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteTask = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { toast } = useToast();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: planningService.deleteTask,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.tasks });
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.backlog });
|
||||
toast({ title: 'Task deleted' });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useMoveTask = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ taskId, sprintId }: { taskId: string; sprintId: string | null }) =>
|
||||
planningService.moveTaskToSprint(taskId, sprintId),
|
||||
onMutate: async ({ taskId, sprintId }) => {
|
||||
// Cancel outgoing refetches
|
||||
await queryClient.cancelQueries({ queryKey: QUERY_KEYS.tasks });
|
||||
|
||||
// Snapshot previous value
|
||||
const previousTasks = queryClient.getQueryData<Task[]>(QUERY_KEYS.tasks);
|
||||
|
||||
// Optimistically update
|
||||
queryClient.setQueryData<Task[]>(QUERY_KEYS.tasks, (old) =>
|
||||
old?.map(task => task.id === taskId ? { ...task, sprintId } : task) ?? []
|
||||
);
|
||||
|
||||
return { previousTasks };
|
||||
},
|
||||
onError: (_err, _vars, context) => {
|
||||
// Rollback on error
|
||||
if (context?.previousTasks) {
|
||||
queryClient.setQueryData(QUERY_KEYS.tasks, context.previousTasks);
|
||||
}
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.tasks });
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.backlog });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateTaskStatus = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ taskId, status }: { taskId: string; status: TaskStatus }) =>
|
||||
planningService.updateTaskStatus(taskId, status),
|
||||
onMutate: async ({ taskId, status }) => {
|
||||
await queryClient.cancelQueries({ queryKey: QUERY_KEYS.tasks });
|
||||
|
||||
const previousTasks = queryClient.getQueryData<Task[]>(QUERY_KEYS.tasks);
|
||||
|
||||
queryClient.setQueryData<Task[]>(QUERY_KEYS.tasks, (old) =>
|
||||
old?.map(task => task.id === taskId ? { ...task, status } : task) ?? []
|
||||
);
|
||||
|
||||
return { previousTasks };
|
||||
},
|
||||
onError: (_err, _vars, context) => {
|
||||
if (context?.previousTasks) {
|
||||
queryClient.setQueryData(QUERY_KEYS.tasks, context.previousTasks);
|
||||
}
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.tasks });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== COMBINED DATA ====================
|
||||
|
||||
export const usePlanningData = () => {
|
||||
return useQuery({
|
||||
queryKey: QUERY_KEYS.allData,
|
||||
queryFn: planningService.getAllData,
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user