feat(5.3): add workflow API frontend module with TypeScript types and React Query hooks
- Create frontend/src/api/workflows.ts with types matching backend workflow model - Workflow CRUD hooks: useWorkflows, useWorkflow, useCreateWorkflow, useUpdateWorkflow, useDeleteWorkflow - Instance hooks: useWorkflowInstances, useWorkflowInstance, useCreateWorkflowInstance, useAdvanceWorkflowInstance, useCancelWorkflowInstance - Step history types included in WorkflowInstanceDetail - Add comprehensive test suite (13 tests, all passing) - TSC: 0 new errors
This commit is contained in:
@@ -0,0 +1,298 @@
|
|||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
|
||||||
|
// Mock the API client
|
||||||
|
const mockApiGet = vi.fn();
|
||||||
|
const mockApiPost = vi.fn();
|
||||||
|
const mockApiPatch = vi.fn();
|
||||||
|
const mockApiDelete = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('@/api/client', () => ({
|
||||||
|
apiGet: (...args: any[]) => mockApiGet(...args),
|
||||||
|
apiPost: (...args: any[]) => mockApiPost(...args),
|
||||||
|
apiPatch: (...args: any[]) => mockApiPatch(...args),
|
||||||
|
apiDelete: (...args: any[]) => mockApiDelete(...args),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock react-query
|
||||||
|
vi.mock('@tanstack/react-query', () => ({
|
||||||
|
useQueryClient: () => ({
|
||||||
|
invalidateQueries: vi.fn(),
|
||||||
|
}),
|
||||||
|
useQuery: vi.fn(),
|
||||||
|
useMutation: vi.fn(({ mutationFn, onSuccess }) => ({
|
||||||
|
mutateAsync: async (args: any) => {
|
||||||
|
const result = await mutationFn(args);
|
||||||
|
if (onSuccess) onSuccess(result, args);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
isPending: false,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('Workflow API Hooks', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useWorkflows', () => {
|
||||||
|
it('calls apiGet with correct endpoint', async () => {
|
||||||
|
const { useWorkflows } = await import('../workflows');
|
||||||
|
const { useQuery } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useQuery as any).mockImplementation(({ queryKey, queryFn }: any) => {
|
||||||
|
expect(queryKey[0]).toBe('workflows');
|
||||||
|
queryFn();
|
||||||
|
return { data: undefined, isLoading: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
useWorkflows();
|
||||||
|
expect(mockApiGet).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('/workflows?')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes is_active filter when provided', async () => {
|
||||||
|
const { useWorkflows } = await import('../workflows');
|
||||||
|
const { useQuery } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useQuery as any).mockImplementation(({ queryFn }: any) => {
|
||||||
|
queryFn();
|
||||||
|
return { data: undefined, isLoading: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
useWorkflows(1, 20, true);
|
||||||
|
expect(mockApiGet).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('is_active=true')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useWorkflow', () => {
|
||||||
|
it('calls apiGet with correct endpoint', async () => {
|
||||||
|
const { useWorkflow } = await import('../workflows');
|
||||||
|
const { useQuery } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useQuery as any).mockImplementation(({ queryKey, queryFn, enabled }: any) => {
|
||||||
|
expect(queryKey).toEqual(['workflows', 'wf-123']);
|
||||||
|
expect(enabled).toBe(true);
|
||||||
|
queryFn();
|
||||||
|
return { data: undefined, isLoading: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
useWorkflow('wf-123');
|
||||||
|
expect(mockApiGet).toHaveBeenCalledWith('/workflows/wf-123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is disabled when id is undefined', async () => {
|
||||||
|
const { useWorkflow } = await import('../workflows');
|
||||||
|
const { useQuery } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useQuery as any).mockImplementation(({ enabled }: any) => {
|
||||||
|
expect(enabled).toBe(false);
|
||||||
|
return { data: undefined, isLoading: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
useWorkflow(undefined);
|
||||||
|
expect(mockApiGet).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useCreateWorkflow', () => {
|
||||||
|
it('calls apiPost with correct endpoint and data', async () => {
|
||||||
|
const { useCreateWorkflow } = await import('../workflows');
|
||||||
|
const { useMutation } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useMutation as any).mockImplementation(({ mutationFn, onSuccess }: any) => ({
|
||||||
|
mutateAsync: async (args: any) => {
|
||||||
|
const result = await mutationFn(args);
|
||||||
|
if (onSuccess) onSuccess(result, args);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
isPending: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { mutateAsync } = useCreateWorkflow();
|
||||||
|
const payload = {
|
||||||
|
name: 'Test WF',
|
||||||
|
steps: [{ name: 'S1', type: 'action' as const, config: {} }],
|
||||||
|
};
|
||||||
|
mockApiPost.mockResolvedValue({ id: 'wf-1', ...payload });
|
||||||
|
await mutateAsync(payload);
|
||||||
|
expect(mockApiPost).toHaveBeenCalledWith('/workflows', payload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useUpdateWorkflow', () => {
|
||||||
|
it('calls apiPatch with correct endpoint and data', async () => {
|
||||||
|
const { useUpdateWorkflow } = await import('../workflows');
|
||||||
|
const { useMutation } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useMutation as any).mockImplementation(({ mutationFn, onSuccess }: any) => ({
|
||||||
|
mutateAsync: async (args: any) => {
|
||||||
|
const result = await mutationFn(args);
|
||||||
|
if (onSuccess) onSuccess(result, args);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
isPending: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { mutateAsync } = useUpdateWorkflow();
|
||||||
|
const payload = { id: 'wf-1', data: { name: 'Updated' } };
|
||||||
|
mockApiPatch.mockResolvedValue({ id: 'wf-1', name: 'Updated' });
|
||||||
|
await mutateAsync(payload);
|
||||||
|
expect(mockApiPatch).toHaveBeenCalledWith('/workflows/wf-1', {
|
||||||
|
name: 'Updated',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useDeleteWorkflow', () => {
|
||||||
|
it('calls apiDelete with correct endpoint', async () => {
|
||||||
|
const { useDeleteWorkflow } = await import('../workflows');
|
||||||
|
const { useMutation } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useMutation as any).mockImplementation(({ mutationFn, onSuccess }: any) => ({
|
||||||
|
mutateAsync: async (args: any) => {
|
||||||
|
const result = await mutationFn(args);
|
||||||
|
if (onSuccess) onSuccess(result, args);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
isPending: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { mutateAsync } = useDeleteWorkflow();
|
||||||
|
mockApiDelete.mockResolvedValue(undefined);
|
||||||
|
await mutateAsync('wf-1');
|
||||||
|
expect(mockApiDelete).toHaveBeenCalledWith('/workflows/wf-1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useWorkflowInstances', () => {
|
||||||
|
it('calls apiGet with instances endpoint', async () => {
|
||||||
|
const { useWorkflowInstances } = await import('../workflows');
|
||||||
|
const { useQuery } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useQuery as any).mockImplementation(({ queryFn }: any) => {
|
||||||
|
queryFn();
|
||||||
|
return { data: undefined, isLoading: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
useWorkflowInstances();
|
||||||
|
expect(mockApiGet).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('/workflows/instances?')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes status filter when provided', async () => {
|
||||||
|
const { useWorkflowInstances } = await import('../workflows');
|
||||||
|
const { useQuery } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useQuery as any).mockImplementation(({ queryFn }: any) => {
|
||||||
|
queryFn();
|
||||||
|
return { data: undefined, isLoading: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
useWorkflowInstances(1, 20, 'completed');
|
||||||
|
expect(mockApiGet).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('status=completed')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useWorkflowInstance', () => {
|
||||||
|
it('calls apiGet with instance detail endpoint', async () => {
|
||||||
|
const { useWorkflowInstance } = await import('../workflows');
|
||||||
|
const { useQuery } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useQuery as any).mockImplementation(({ queryFn, enabled }: any) => {
|
||||||
|
expect(enabled).toBe(true);
|
||||||
|
queryFn();
|
||||||
|
return { data: undefined, isLoading: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
useWorkflowInstance('inst-1');
|
||||||
|
expect(mockApiGet).toHaveBeenCalledWith(
|
||||||
|
'/workflows/instances/inst-1'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useCreateWorkflowInstance', () => {
|
||||||
|
it('calls apiPost with correct endpoint and data', async () => {
|
||||||
|
const { useCreateWorkflowInstance } = await import('../workflows');
|
||||||
|
const { useMutation } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useMutation as any).mockImplementation(({ mutationFn, onSuccess }: any) => ({
|
||||||
|
mutateAsync: async (args: any) => {
|
||||||
|
const result = await mutationFn(args);
|
||||||
|
if (onSuccess) onSuccess(result, args);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
isPending: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { mutateAsync } = useCreateWorkflowInstance();
|
||||||
|
const payload = {
|
||||||
|
workflowId: 'wf-1',
|
||||||
|
data: { context: { foo: 'bar' } },
|
||||||
|
};
|
||||||
|
mockApiPost.mockResolvedValue({ id: 'inst-1' });
|
||||||
|
await mutateAsync(payload);
|
||||||
|
expect(mockApiPost).toHaveBeenCalledWith(
|
||||||
|
'/workflows/wf-1/instances',
|
||||||
|
{ context: { foo: 'bar' } }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useAdvanceWorkflowInstance', () => {
|
||||||
|
it('calls apiPost with advance endpoint and decision', async () => {
|
||||||
|
const { useAdvanceWorkflowInstance } = await import('../workflows');
|
||||||
|
const { useMutation } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useMutation as any).mockImplementation(({ mutationFn, onSuccess }: any) => ({
|
||||||
|
mutateAsync: async (args: any) => {
|
||||||
|
const result = await mutationFn(args);
|
||||||
|
if (onSuccess) onSuccess(result, args);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
isPending: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { mutateAsync } = useAdvanceWorkflowInstance();
|
||||||
|
const payload = {
|
||||||
|
instanceId: 'inst-1',
|
||||||
|
data: { decision: 'approve' as const, comment: 'LGTM' },
|
||||||
|
};
|
||||||
|
mockApiPost.mockResolvedValue({ id: 'inst-1', status: 'in_progress' });
|
||||||
|
await mutateAsync(payload);
|
||||||
|
expect(mockApiPost).toHaveBeenCalledWith(
|
||||||
|
'/workflows/instances/inst-1/advance',
|
||||||
|
{ decision: 'approve', comment: 'LGTM' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useCancelWorkflowInstance', () => {
|
||||||
|
it('calls apiPost with cancel endpoint', async () => {
|
||||||
|
const { useCancelWorkflowInstance } = await import('../workflows');
|
||||||
|
const { useMutation } = await import('@tanstack/react-query');
|
||||||
|
|
||||||
|
(useMutation as any).mockImplementation(({ mutationFn, onSuccess }: any) => ({
|
||||||
|
mutateAsync: async (args: any) => {
|
||||||
|
const result = await mutationFn(args);
|
||||||
|
if (onSuccess) onSuccess(result, args);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
isPending: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { mutateAsync } = useCancelWorkflowInstance();
|
||||||
|
mockApiPost.mockResolvedValue({ id: 'inst-1', status: 'cancelled' });
|
||||||
|
await mutateAsync('inst-1');
|
||||||
|
expect(mockApiPost).toHaveBeenCalledWith(
|
||||||
|
'/workflows/instances/inst-1/cancel'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
/**
|
||||||
|
* 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<string, unknown>;
|
||||||
|
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<Workflow> {}
|
||||||
|
|
||||||
|
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<string, unknown>;
|
||||||
|
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<string, unknown> | null;
|
||||||
|
created_at?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkflowInstanceDetail extends WorkflowInstance {
|
||||||
|
history: StepHistoryEntry[];
|
||||||
|
workflow_name?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InstanceListResponse extends PaginatedResponse<WorkflowInstance> {}
|
||||||
|
|
||||||
|
export interface InstanceCreateInput {
|
||||||
|
context?: Record<string, unknown>;
|
||||||
|
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<WorkflowListResponse>(`/workflows?${params.toString()}`),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useWorkflow(id?: string) {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['workflows', id],
|
||||||
|
queryFn: () => apiGet<Workflow>(`/workflows/${id}`),
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCreateWorkflow() {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (data: WorkflowCreateInput) =>
|
||||||
|
apiPost<Workflow>('/workflows', data),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['workflows'] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUpdateWorkflow() {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
data: WorkflowUpdateInput;
|
||||||
|
}) => apiPatch<Workflow>(`/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<InstanceListResponse>(
|
||||||
|
`/workflows/instances?${params.toString()}`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useWorkflowInstance(id?: string) {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['workflowInstances', id],
|
||||||
|
queryFn: () =>
|
||||||
|
apiGet<WorkflowInstanceDetail>(`/workflows/instances/${id}`),
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCreateWorkflowInstance() {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({
|
||||||
|
workflowId,
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
workflowId: string;
|
||||||
|
data: InstanceCreateInput;
|
||||||
|
}) =>
|
||||||
|
apiPost<WorkflowInstance>(
|
||||||
|
`/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<WorkflowInstance>(
|
||||||
|
`/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<WorkflowInstance>(
|
||||||
|
`/workflows/instances/${instanceId}/cancel`
|
||||||
|
),
|
||||||
|
onSuccess: (_data, instanceId) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['workflowInstances'] });
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ['workflowInstances', instanceId],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user