import { keepPreviousData, useMutation, useQuery, useQueryClient, } from "@tanstack/react-query"; import { adminDeleteScheduledTask, adminListScheduledTaskRuns, adminTriggerScheduledTask, adminUpdateScheduledTask, createScheduledTask, deleteScheduledTask, deleteScheduledTaskRun, getSchedulerThread, getScheduledTaskDeliveryProfile, getScheduledTaskRun, getScheduledTaskRunMessages, listAllScheduledTasks, listPublishedScheduledTasks, listScheduledTaskRuns, listScheduledTasks, pauseScheduledTask, publishScheduledTask, resumeScheduledTask, subscribeScheduledTask, triggerScheduledTask, unpublishScheduledTask, unsubscribeScheduledTask, updateScheduledTaskDeliveryProfile, updateScheduledTask, updateScheduledTaskSubscription, } from "./api"; export function useSchedulerThread() { return useQuery({ queryKey: ["scheduled-tasks", "thread"], queryFn: getSchedulerThread, refetchOnWindowFocus: false, }); } /** A task is currently executing — used to drive live polling of the task list. */ function taskIsLive(status?: string | null): boolean { return status === "running" || status === "pending"; } export function useScheduledTasks( params: { search?: string; tagIds?: string[] } = {}, ) { const search = params.search ?? ""; const tagIds = params.tagIds ?? []; return useQuery({ queryKey: ["scheduled-tasks", "list", search, [...tagIds].sort()], queryFn: () => listScheduledTasks({ search, tagIds }), refetchOnWindowFocus: false, placeholderData: keepPreviousData, // Poll the list while any task is currently executing so its last_status // and last_run_at refresh without a manual reload. refetchInterval: (query) => (query.state.data ?? []).some((task) => taskIsLive(task.last_status)) ? 5000 : false, }); } export function useScheduledTaskRuns(taskId?: string | null) { return useQuery({ queryKey: ["scheduled-tasks", taskId, "runs"], queryFn: () => listScheduledTaskRuns(taskId!), enabled: !!taskId, refetchOnWindowFocus: false, // Poll the run list while any run is in flight so the user can watch // status transitions (pending → running → succeeded/failed) without // refreshing the page. refetchInterval: (query) => (query.state.data ?? []).some((run) => runIsLive(run.status)) ? 4000 : false, }); } /** A run still in flight — used to drive live polling of the run/messages. */ function runIsLive(status?: string | null): boolean { return status === "running" || status === "pending"; } export function useScheduledTaskRun(runId?: string | null) { return useQuery({ queryKey: ["scheduled-tasks", "runs", runId], queryFn: () => getScheduledTaskRun(runId!), enabled: !!runId, refetchOnWindowFocus: false, // Keep refreshing while the run is still executing so the detail page // reflects status / result changes without a manual reload. refetchInterval: (query) => (runIsLive(query.state.data?.status) ? 5000 : false), }); } /** Agent messages for a run; polls every 4s while the run is still executing. */ export function useScheduledTaskRunMessages( runId?: string | null, options: { live?: boolean } = {}, ) { return useQuery({ queryKey: ["scheduled-tasks", "runs", runId, "messages"], queryFn: () => getScheduledTaskRunMessages(runId!), enabled: !!runId, refetchOnWindowFocus: false, refetchInterval: options.live ? 4000 : false, }); } export function useScheduledTaskDeliveryProfile() { return useQuery({ queryKey: ["scheduled-tasks", "delivery-profile"], queryFn: getScheduledTaskDeliveryProfile, refetchOnWindowFocus: false, }); } /** Admin: every scheduled task across all users. */ export function useAllScheduledTasks( params: { search?: string; enabled?: boolean } = {}, ) { const search = params.search ?? ""; return useQuery({ queryKey: ["scheduled-tasks", "admin", "list", search], queryFn: () => listAllScheduledTasks(search), enabled: params.enabled ?? true, refetchOnWindowFocus: false, placeholderData: keepPreviousData, // Mirror the user list polling so the admin view also refreshes live. refetchInterval: (query) => (query.state.data ?? []).some((task) => taskIsLive(task.last_status)) ? 5000 : false, }); } /** Admin: runs for any task regardless of owner. */ export function useAdminScheduledTaskRuns(taskId?: string | null) { return useQuery({ queryKey: ["scheduled-tasks", "admin", taskId, "runs"], queryFn: () => adminListScheduledTaskRuns(taskId!), enabled: !!taskId, refetchOnWindowFocus: false, // Poll while any run is still executing so admins see live progress. refetchInterval: (query) => (query.state.data ?? []).some((run) => runIsLive(run.status)) ? 4000 : false, }); } /** Admin mutations: edit / delete / trigger any task. */ export function useAdminScheduledTaskActions() { const queryClient = useQueryClient(); const invalidate = () => queryClient.invalidateQueries({ queryKey: ["scheduled-tasks"] }); return { update: useMutation({ mutationFn: ({ taskId, data, }: { taskId: string; data: Parameters[1]; }) => adminUpdateScheduledTask(taskId, data), onSuccess: invalidate, }), remove: useMutation({ mutationFn: adminDeleteScheduledTask, onSuccess: invalidate, }), // The DELETE /runs/{id} route is admin-aware, so the same call deletes // any user's run when the caller is an admin. removeRun: useMutation({ mutationFn: deleteScheduledTaskRun, onSuccess: invalidate, }), trigger: useMutation({ mutationFn: adminTriggerScheduledTask, onSuccess: invalidate, }), }; } export function usePublishedScheduledTasks(enabled: boolean = true) { return useQuery({ queryKey: ["scheduled-tasks", "published"], queryFn: () => listPublishedScheduledTasks({ limit: 100 }), enabled, refetchOnWindowFocus: false, }); } export function useScheduledTaskActions() { const queryClient = useQueryClient(); const invalidate = () => queryClient.invalidateQueries({ queryKey: ["scheduled-tasks"] }); const invalidateRuns = () => queryClient.invalidateQueries({ queryKey: ["scheduled-tasks"] }); return { update: useMutation({ mutationFn: ({ taskId, data, }: { taskId: string; data: Parameters[1]; }) => updateScheduledTask(taskId, data), onSuccess: invalidate, }), create: useMutation({ mutationFn: createScheduledTask, onSuccess: invalidate, }), remove: useMutation({ mutationFn: deleteScheduledTask, onSuccess: invalidate, }), removeRun: useMutation({ mutationFn: deleteScheduledTaskRun, onSuccess: invalidate, }), pause: useMutation({ mutationFn: pauseScheduledTask, onSuccess: invalidate, }), resume: useMutation({ mutationFn: resumeScheduledTask, onSuccess: invalidate, }), trigger: useMutation({ mutationFn: triggerScheduledTask, onSuccess: invalidateRuns, }), updateDeliveryProfile: useMutation({ mutationFn: updateScheduledTaskDeliveryProfile, onSuccess: () => queryClient.invalidateQueries({ queryKey: ["scheduled-tasks", "delivery-profile"], }), }), publish: useMutation({ mutationFn: publishScheduledTask, onSuccess: invalidate, }), unpublish: useMutation({ mutationFn: unpublishScheduledTask, onSuccess: invalidate, }), subscribe: useMutation({ mutationFn: ({ taskId, notifyElement }: { taskId: string; notifyElement: boolean }) => subscribeScheduledTask(taskId, notifyElement), onSuccess: invalidate, }), updateSubscription: useMutation({ mutationFn: ({ taskId, notifyElement }: { taskId: string; notifyElement: boolean }) => updateScheduledTaskSubscription(taskId, notifyElement), onSuccess: invalidate, }), unsubscribe: useMutation({ mutationFn: unsubscribeScheduledTask, onSuccess: invalidate, }), }; }