/** * Tasks API hooks — CRUD, assign, status update, subtasks, dependencies, goals. */ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { apiGet, apiPost, apiPatch, apiDelete } from './client'; export type TaskStatus = 'open' | 'in_progress' | 'review' | 'blocked' | 'done' | 'cancelled'; export type TaskType = 'todo' | 'approval' | 'follow_up' | 'review' | 'goal' | 'milestone' | 'agent_subtask'; export type AssigneeType = 'user' | 'agent' | 'group'; export type CreatorType = 'user' | 'agent' | 'workflow' | 'system'; export interface Task { id: string; title: string; description: string | null; status: TaskStatus; priority: 'low' | 'medium' | 'high' | 'urgent'; due_date: string | null; assigned_to: string | null; contact_id: string | null; assignee_type: AssigneeType; assignee_id: string | null; entity_type: string | null; entity_id: string | null; creator_type: CreatorType; creator_id: string | null; parent_task_id: string | null; depends_on: string[]; task_type: TaskType; success_criteria: Record | null; target_date: string | null; progress: number; created_by: string | null; created_at: string; updated_at: string; } export interface TaskListResponse { items: Task[]; total: number; page: number; page_size: number; } export interface TaskCreateInput { title: string; description?: string | null; status?: TaskStatus; priority?: 'low' | 'medium' | 'high' | 'urgent'; due_date?: string | null; assigned_to?: string | null; contact_id?: string | null; assignee_type?: AssigneeType; assignee_id?: string | null; entity_type?: string | null; entity_id?: string | null; creator_type?: CreatorType; creator_id?: string | null; parent_task_id?: string | null; depends_on?: string[]; task_type?: TaskType; success_criteria?: Record | null; target_date?: string | null; progress?: number; } export interface TaskUpdateInput { title?: string; description?: string | null; status?: TaskStatus; priority?: 'low' | 'medium' | 'high' | 'urgent'; due_date?: string | null; assigned_to?: string | null; contact_id?: string | null; assignee_type?: AssigneeType; assignee_id?: string | null; entity_type?: string | null; entity_id?: string | null; creator_type?: CreatorType; creator_id?: string | null; parent_task_id?: string | null; depends_on?: string[]; task_type?: TaskType; success_criteria?: Record | null; target_date?: string | null; progress?: number; } export interface TaskFilter { status?: TaskStatus; priority?: string; assigned_to?: string; contact_id?: string; entity_type?: string; entity_id?: string; assignee_type?: AssigneeType; assignee_id?: string; parent_task_id?: string; task_type?: TaskType; search?: string; } export function useTasks(page = 1, pageSize = 25, filter?: TaskFilter) { const params = new URLSearchParams({ page: String(page), page_size: String(pageSize) }); if (filter?.status) params.set('status', filter.status); if (filter?.priority) params.set('priority', filter.priority); if (filter?.assigned_to) params.set('assigned_to', filter.assigned_to); if (filter?.contact_id) params.set('contact_id', filter.contact_id); if (filter?.entity_type) params.set('entity_type', filter.entity_type); if (filter?.entity_id) params.set('entity_id', filter.entity_id); if (filter?.assignee_type) params.set('assignee_type', filter.assignee_type); if (filter?.assignee_id) params.set('assignee_id', filter.assignee_id); if (filter?.parent_task_id) params.set('parent_task_id', filter.parent_task_id); if (filter?.task_type) params.set('task_type', filter.task_type); if (filter?.search) params.set('search', filter.search); return useQuery({ queryKey: ['tasks', page, pageSize, filter], queryFn: () => apiGet(`/tasks?${params.toString()}`), }); } export function useTask(id?: string) { return useQuery({ queryKey: ['tasks', id], queryFn: () => apiGet(`/tasks/${id}`), enabled: !!id, }); } export function useCreateTask() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: TaskCreateInput) => apiPost('/tasks', data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); }, }); } export function useUpdateTask() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: string; data: TaskUpdateInput }) => apiPatch(`/tasks/${id}`, data), onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); queryClient.invalidateQueries({ queryKey: ['tasks', variables.id] }); }, }); } export function useDeleteTask() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => apiDelete(`/tasks/${id}`), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); }, }); } export function useAssignTask() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, assigneeType, assigneeId }: { id: string; assigneeType: AssigneeType; assigneeId: string }) => apiPost(`/tasks/${id}/assign`, { assignee_type: assigneeType, assignee_id: assigneeId }), onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); queryClient.invalidateQueries({ queryKey: ['tasks', variables.id] }); }, }); } export function useUpdateTaskStatus() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, status }: { id: string; status: TaskStatus }) => apiPost(`/tasks/${id}/status`, { status }), onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); queryClient.invalidateQueries({ queryKey: ['tasks', variables.id] }); }, }); } export function useCreateSubtask() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ parentId, data }: { parentId: string; data: TaskCreateInput }) => apiPost(`/tasks/${parentId}/subtasks`, data), onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); queryClient.invalidateQueries({ queryKey: ['tasks', variables.parentId] }); }, }); } export function useListSubtasks(parentId?: string) { return useQuery({ queryKey: ['tasks', parentId, 'subtasks'], queryFn: () => apiGet(`/tasks/${parentId}/subtasks`), enabled: !!parentId, }); } export function useAddDependency() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, dependsOn }: { id: string; dependsOn: string }) => apiPost(`/tasks/${id}/dependencies`, { depends_on: dependsOn }), onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); queryClient.invalidateQueries({ queryKey: ['tasks', variables.id] }); }, }); } export function useRemoveDependency() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, dependsOn }: { id: string; dependsOn: string }) => apiDelete(`/tasks/${id}/dependencies/${dependsOn}`), onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); queryClient.invalidateQueries({ queryKey: ['tasks', variables.id] }); }, }); } export function useDecomposeGoal() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ goalId, subtasks }: { goalId: string; subtasks: TaskCreateInput[] }) => apiPost<{ goal: Task; subtasks: Task[] }>(`/tasks/${goalId}/decompose`, subtasks), onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); queryClient.invalidateQueries({ queryKey: ['tasks', variables.goalId] }); }, }); }