/** * Workflow API module — TypeScript types and React Query hooks. * Matches backend endpoints from /api/v1/workflows. * * Backend: app/routes/workflows.py, app/models/workflow.py, app/schemas/workflow.py */ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { apiGet, apiPost, apiPatch, apiDelete } from './client'; import type { PaginatedResponse } from './types'; // ── Types ── export interface WorkflowStep { name: string; type: 'action' | 'approval' | 'notification' | 'condition'; config: Record; description?: string | null; } export interface Workflow { id: string; name: string; description?: string | null; trigger_event?: string | null; steps: WorkflowStep[]; is_active: boolean; created_by?: string | null; created_at?: string | null; updated_at?: string | null; } export interface WorkflowListResponse extends PaginatedResponse {} export interface WorkflowCreateInput { name: string; description?: string | null; trigger_event?: string | null; steps: WorkflowStep[]; is_active?: boolean; } export interface WorkflowUpdateInput { name?: string; description?: string | null; trigger_event?: string | null; steps?: WorkflowStep[]; is_active?: boolean; } export type InstanceStatus = | 'pending' | 'in_progress' | 'completed' | 'rejected' | 'cancelled'; export interface WorkflowInstance { id: string; workflow_id: string; status: InstanceStatus; current_step_index: number; context: Record; initiated_by?: string | null; completed_at?: string | null; timeout_hours?: number | null; timeout_at?: string | null; created_at?: string | null; updated_at?: string | null; } export interface StepHistoryEntry { id: string; instance_id: string; step_index: number; step_type: string; action: string; actor_id?: string | null; details?: Record | null; created_at?: string | null; } export interface WorkflowInstanceDetail extends WorkflowInstance { history: StepHistoryEntry[]; workflow_name?: string | null; } export interface InstanceListResponse extends PaginatedResponse {} export interface InstanceCreateInput { context?: Record; timeout_hours?: number | null; } export interface AdvanceRequest { decision: 'approve' | 'reject'; comment?: string | null; } // ── Workflow Definition Hooks ── export function useWorkflows(page = 1, pageSize = 20, isActive?: boolean) { const params = new URLSearchParams({ page: String(page), page_size: String(pageSize), }); if (isActive !== undefined) params.set('is_active', String(isActive)); return useQuery({ queryKey: ['workflows', page, pageSize, isActive], queryFn: () => apiGet(`/workflows?${params.toString()}`), }); } export function useWorkflow(id?: string) { return useQuery({ queryKey: ['workflows', id], queryFn: () => apiGet(`/workflows/${id}`), enabled: !!id, }); } export function useCreateWorkflow() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: WorkflowCreateInput) => apiPost('/workflows', data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['workflows'] }); }, }); } export function useUpdateWorkflow() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data, }: { id: string; data: WorkflowUpdateInput; }) => apiPatch(`/workflows/${id}`, data), onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['workflows'] }); queryClient.invalidateQueries({ queryKey: ['workflows', variables.id], }); }, }); } export function useDeleteWorkflow() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => apiDelete(`/workflows/${id}`), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['workflows'] }); }, }); } // ── Workflow Instance Hooks ── export function useWorkflowInstances( page = 1, pageSize = 20, statusFilter?: InstanceStatus ) { const params = new URLSearchParams({ page: String(page), page_size: String(pageSize), }); if (statusFilter) params.set('status', statusFilter); return useQuery({ queryKey: ['workflowInstances', page, pageSize, statusFilter], queryFn: () => apiGet( `/workflows/instances?${params.toString()}` ), }); } export function useWorkflowInstance(id?: string) { return useQuery({ queryKey: ['workflowInstances', id], queryFn: () => apiGet(`/workflows/instances/${id}`), enabled: !!id, }); } export function useCreateWorkflowInstance() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ workflowId, data, }: { workflowId: string; data: InstanceCreateInput; }) => apiPost( `/workflows/${workflowId}/instances`, data ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['workflowInstances'] }); }, }); } export function useAdvanceWorkflowInstance() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ instanceId, data, }: { instanceId: string; data: AdvanceRequest; }) => apiPost( `/workflows/instances/${instanceId}/advance`, data ), onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['workflowInstances'] }); queryClient.invalidateQueries({ queryKey: ['workflowInstances', variables.instanceId], }); }, }); } export function useCancelWorkflowInstance() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (instanceId: string) => apiPost( `/workflows/instances/${instanceId}/cancel` ), onSuccess: (_data, instanceId) => { queryClient.invalidateQueries({ queryKey: ['workflowInstances'] }); queryClient.invalidateQueries({ queryKey: ['workflowInstances', instanceId], }); }, }); }