Replace all console.log/warn/error statements with unified logger utility. Changes: - Replaced console in lib/hooks/use-stories.ts - Replaced console in lib/signalr/SignalRContext.tsx - Replaced console in lib/hooks/useProjectHub.ts - Replaced console in lib/hooks/use-tasks.ts - Replaced console in lib/hooks/useNotificationHub.ts - Replaced console in lib/hooks/use-projects.ts - Replaced console in app/(dashboard)/projects/[id]/kanban/page.tsx Logger respects NODE_ENV (debug disabled in production). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
165 lines
5.1 KiB
TypeScript
165 lines
5.1 KiB
TypeScript
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<Task[]>({
|
|
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<Task>({
|
|
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<Task>(['tasks', id]);
|
|
|
|
queryClient.setQueryData<Task>(['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<Task>(['tasks', id]);
|
|
|
|
queryClient.setQueryData<Task>(['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');
|
|
},
|
|
});
|
|
}
|