import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import {
listTasks,
getTask,
startTask,
stopTask,
getTaskOutput,
sendTaskMessage,
type TaskSummary,
type TaskWithSubtasks,
type TaskOutputResponse,
} from '../lib/api';
// Query keys for consistent cache management
export const taskKeys = {
all: ['tasks'] as const,
lists: () => [...taskKeys.all, 'list'] as const,
list: () => [...taskKeys.lists()] as const,
details: () => [...taskKeys.all, 'detail'] as const,
detail: (id: string) => [...taskKeys.details(), id] as const,
output: (id: string) => [...taskKeys.all, 'output', id] as const,
};
/**
* Hook to fetch the list of all tasks
* Automatically refetches every 5 seconds for live updates
*/
export function useTasks() {
return useQuery({
queryKey: taskKeys.list(),
queryFn: async () => {
const response = await listTasks();
return response.tasks;
},
refetchInterval: 5000, // Poll every 5 seconds for updates
staleTime: 2000, // Consider data stale after 2 seconds
});
}
/**
* Hook to fetch a specific task with its subtasks
*/
export function useTask(taskId: string | null) {
return useQuery({
queryKey: taskKeys.detail(taskId ?? ''),
queryFn: () => getTask(taskId!),
enabled: !!taskId,
refetchInterval: 5000,
});
}
/**
* Hook to fetch task output history
*/
export function useTaskOutput(taskId: string | null) {
return useQuery({
queryKey: taskKeys.output(taskId ?? ''),
queryFn: () => getTaskOutput(taskId!),
enabled: !!taskId,
refetchInterval: 3000, // More frequent updates for output
});
}
/**
* Hook to start a task
*/
export function useStartTask() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: startTask,
onSuccess: (updatedTask) => {
// Invalidate task list to refetch
queryClient.invalidateQueries({ queryKey: taskKeys.lists() });
// Update the specific task in cache
queryClient.setQueryData(
taskKeys.detail(updatedTask.id),
(old: TaskWithSubtasks | undefined) => {
if (old) {
return { ...old, ...updatedTask };
}
return old;
}
);
},
});
}
/**
* Hook to stop a task
*/
export function useStopTask() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: stopTask,
onSuccess: (updatedTask) => {
// Invalidate task list to refetch
queryClient.invalidateQueries({ queryKey: taskKeys.lists() });
// Update the specific task in cache
queryClient.setQueryData(
taskKeys.detail(updatedTask.id),
(old: TaskWithSubtasks | undefined) => {
if (old) {
return { ...old, ...updatedTask };
}
return old;
}
);
},
});
}
/**
* Hook to send a message to a task
*/
export function useSendTaskMessage() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ taskId, message }: { taskId: string; message: string }) =>
sendTaskMessage(taskId, message),
onSuccess: (_, { taskId }) => {
// Invalidate task output to refetch
queryClient.invalidateQueries({ queryKey: taskKeys.output(taskId) });
},
});
}
/**
* Helper to group tasks by status for display
*/
export function groupTasksByStatus(tasks: TaskSummary[]) {
const groups = {
running: [] as TaskSummary[],
pending: [] as TaskSummary[],
blocked: [] as TaskSummary[],
completed: [] as TaskSummary[],
};
for (const task of tasks) {
switch (task.status) {
case 'running':
case 'initializing':
case 'starting':
groups.running.push(task);
break;
case 'pending':
groups.pending.push(task);
break;
case 'blocked':
case 'paused':
groups.blocked.push(task);
break;
case 'done':
case 'failed':
case 'merged':
groups.completed.push(task);
break;
}
}
return groups;
}
/**
* Get counts for dashboard display
*/
export function getTaskCounts(tasks: TaskSummary[]) {
const counts = {
total: tasks.length,
running: 0,
pending: 0,
blocked: 0,
completed: 0,
failed: 0,
};
for (const task of tasks) {
switch (task.status) {
case 'running':
case 'initializing':
case 'starting':
counts.running++;
break;
case 'pending':
counts.pending++;
break;
case 'blocked':
case 'paused':
counts.blocked++;
break;
case 'done':
case 'merged':
counts.completed++;
break;
case 'failed':
counts.failed++;
break;
}
}
return counts;
}