Skip to main content

Workflow Hooks

React hooks for executing and managing Ductape Workflows.

useWorkflowExecution

Execute a workflow and track its progress.

Basic Workflow Execution

import { useWorkflowExecution } from '@ductape/react';

function OrderProcessor() {
const { mutate, isLoading, data, error } = useWorkflowExecution({
onSuccess: (result) => {
console.log('Workflow completed:', result);
alert('Order processed successfully!');
},
onError: (err) => {
console.error('Workflow failed:', err);
}
});

const handleProcessOrder = (orderId: string) => {
mutate({
workflow: 'process-order',
input: {
orderId,
priority: 'high'
}
});
};

return (
<div>
<button onClick={() => handleProcessOrder('order-123')} disabled={isLoading}>
{isLoading ? 'Processing...' : 'Process Order'}
</button>
{error && <p className="error">{error.message}</p>}
{data && <p>Result: {JSON.stringify(data.output)}</p>}
</div>
);
}

Workflow with Real-time Status

function WorkflowRunner() {
const [executionId, setExecutionId] = useState<string | null>(null);

const { mutate, isLoading } = useWorkflowExecution({
onSuccess: (result) => {
setExecutionId(result.executionId);
}
});

const handleStart = () => {
mutate({
workflow: 'data-pipeline',
input: {
source: 'database',
destination: 's3'
}
});
};

return (
<div>
<button onClick={handleStart} disabled={isLoading}>
Start Pipeline
</button>
{executionId && <WorkflowStatus executionId={executionId} />}
</div>
);
}

useWorkflowStatus

Monitor the status of a running workflow.

import { useWorkflowStatus } from '@ductape/react';

function WorkflowStatus({ executionId }: { executionId: string }) {
const { data: status, isLoading } = useWorkflowStatus(executionId, {
refetchInterval: 2000 // Poll every 2 seconds
});

if (isLoading) return <div>Loading status...</div>;

return (
<div className="workflow-status">
<h3>Workflow Status</h3>
<div>
<strong>Status:</strong> {status?.status}
</div>
<div>
<strong>Current Step:</strong> {status?.currentStep}
</div>
<div>
<strong>Progress:</strong> {status?.progress}%
</div>

{status?.status === 'running' && (
<div className="progress-bar">
<div style={{ width: `${status.progress}%` }} />
</div>
)}

{status?.status === 'completed' && (
<div className="success">
Workflow completed successfully!
</div>
)}

{status?.status === 'failed' && (
<div className="error">
Error: {status.error}
</div>
)}
</div>
);
}

useWorkflowSubscription

Subscribe to real-time workflow events.

import { useWorkflowSubscription } from '@ductape/react';

function LiveWorkflowMonitor({ executionId }: { executionId: string }) {
const [events, setEvents] = useState<any[]>([]);

useWorkflowSubscription({
executionId,
onEvent: (event) => {
setEvents(prev => [...prev, event]);

if (event.type === 'step_completed') {
console.log(`Step ${event.step} completed`);
} else if (event.type === 'workflow_completed') {
console.log('Workflow finished!');
}
}
});

return (
<div>
<h3>Workflow Events</h3>
<ul>
{events.map((event, idx) => (
<li key={idx}>
<strong>{event.type}:</strong> {event.message}
<span className="timestamp">
{new Date(event.timestamp).toLocaleTimeString()}
</span>
</li>
))}
</ul>
</div>
);
}

useWorkflowSignal

Send signals to a running workflow.

import { useWorkflowSignal } from '@ductape/react';

function WorkflowController({ executionId }: { executionId: string }) {
const { mutate: sendSignal, isLoading } = useWorkflowSignal({
onSuccess: () => {
alert('Signal sent successfully');
}
});

const handleApprove = () => {
sendSignal({
executionId,
signal: 'approve',
data: {
approvedBy: 'user-123',
timestamp: new Date().toISOString()
}
});
};

const handleReject = () => {
sendSignal({
executionId,
signal: 'reject',
data: { reason: 'Does not meet criteria' }
});
};

return (
<div>
<button onClick={handleApprove} disabled={isLoading}>
Approve
</button>
<button onClick={handleReject} disabled={isLoading}>
Reject
</button>
</div>
);
}

useWorkflowCancel

Cancel a running workflow.

import { useWorkflowCancel } from '@ductape/react';

function CancelButton({ executionId }: { executionId: string }) {
const { mutate: cancel, isLoading } = useWorkflowCancel({
onSuccess: () => {
alert('Workflow cancelled');
}
});

const handleCancel = () => {
if (confirm('Are you sure you want to cancel this workflow?')) {
cancel({ executionId });
}
};

return (
<button onClick={handleCancel} disabled={isLoading} className="danger">
{isLoading ? 'Cancelling...' : 'Cancel Workflow'}
</button>
);
}

useWorkflowHistory

View workflow execution history.

import { useWorkflowHistory } from '@ductape/react';

function WorkflowHistory() {
const { data, isLoading } = useWorkflowHistory({
workflow: 'process-order',
limit: 20
});

if (isLoading) return <div>Loading history...</div>;

return (
<div>
<h2>Workflow History</h2>
<table>
<thead>
<tr>
<th>Execution ID</th>
<th>Status</th>
<th>Started</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
{data?.executions.map(execution => (
<tr key={execution.id}>
<td>{execution.id}</td>
<td>
<span className={`status-${execution.status}`}>
{execution.status}
</span>
</td>
<td>{new Date(execution.startedAt).toLocaleString()}</td>
<td>{execution.duration}ms</td>
</tr>
))}
</tbody>
</table>
</div>
);
}

Complete Approval Workflow Example

interface ApprovalRequest {
id: string;
title: string;
description: string;
requestedBy: string;
status: 'pending' | 'approved' | 'rejected';
}

function ApprovalWorkflow({ request }: { request: ApprovalRequest }) {
const [executionId, setExecutionId] = useState<string | null>(null);
const [workflowStatus, setWorkflowStatus] = useState<string>('idle');

// Start workflow
const { mutate: startWorkflow, isLoading: isStarting } = useWorkflowExecution({
onSuccess: (result) => {
setExecutionId(result.executionId);
setWorkflowStatus('running');
}
});

// Send approval signal
const { mutate: sendSignal } = useWorkflowSignal({
onSuccess: () => {
setWorkflowStatus('completed');
}
});

// Monitor status
useWorkflowSubscription({
executionId: executionId || '',
enabled: !!executionId,
onEvent: (event) => {
if (event.type === 'workflow_completed') {
setWorkflowStatus('completed');
} else if (event.type === 'workflow_failed') {
setWorkflowStatus('failed');
}
}
});

const handleStart = () => {
startWorkflow({
workflow: 'approval-process',
input: {
requestId: request.id,
requestData: request
}
});
};

const handleApprove = () => {
if (!executionId) return;
sendSignal({
executionId,
signal: 'approve',
data: {
comments: 'Approved by manager',
timestamp: new Date().toISOString()
}
});
};

const handleReject = () => {
if (!executionId) return;
sendSignal({
executionId,
signal: 'reject',
data: {
reason: 'Does not meet requirements'
}
});
};

return (
<div className="approval-workflow">
<div className="request-details">
<h3>{request.title}</h3>
<p>{request.description}</p>
<p>Requested by: {request.requestedBy}</p>
</div>

{workflowStatus === 'idle' && (
<button onClick={handleStart} disabled={isStarting}>
{isStarting ? 'Starting...' : 'Start Approval Process'}
</button>
)}

{workflowStatus === 'running' && executionId && (
<div className="workflow-controls">
<WorkflowStatus executionId={executionId} />
<div className="actions">
<button onClick={handleApprove} className="approve">
Approve
</button>
<button onClick={handleReject} className="reject">
Reject
</button>
</div>
</div>
)}

{workflowStatus === 'completed' && (
<div className="success">
Approval process completed!
</div>
)}

{workflowStatus === 'failed' && (
<div className="error">
Approval process failed. Please try again.
</div>
)}
</div>
);
}

Next Steps