25 KiB
Workflow Routes Frontend Documentation
This document describes customer journeys for managing workflows through the frontend, focusing on how users interact with workflows and how the backend routes support these experiences. All UI components are dynamically generated from backend metadata—no hardcoding required.
Table of Contents
- Overview
- Customer Journey 1: Discovering and Browsing Workflows
- Customer Journey 2: Viewing Workflow Details
- Customer Journey 3: Monitoring Workflow Execution
- Customer Journey 4: Editing Workflow Properties
- Customer Journey 5: Managing Workflow Messages
- Customer Journey 6: Cleaning Up Workflows
- Backend Metadata System
- Implementation Patterns
Overview
The workflow routes (/api/workflows) enable users to manage and monitor workflows that are already running or completed. These routes focus on management and monitoring rather than creation (workflows are created through the chat playground routes).
Key Principles:
- User-Centric: Documentation organized around what users want to accomplish
- Backend-Driven: All forms, tables, and UI components generated from backend metadata
- No Hardcoding: Field definitions, labels, validation rules, and options come from the backend
- Real-Time Updates: Support for polling and incremental data loading
- Permission-Aware: Backend enforces permissions; frontend handles gracefully
Customer Journey 1: Discovering and Browsing Workflows
User Goal
"I want to see all my workflows and find the one I'm looking for."
User Story
As a user, I want to browse my workflows, search for specific workflows, filter by any field, sort them by different criteria, and quickly identify which ones are running, completed, or have errors.
User Story Flow
sequenceDiagram
participant User
participant Frontend
participant Backend
User->>Frontend: Navigate to /workflows
Frontend->>Backend: GET /api/attributes/ChatWorkflow
Backend-->>Frontend: Attribute definitions (fields, labels, types)
Frontend->>Backend: GET /api/workflows/?pagination=...
Backend-->>Frontend: Paginated workflows list
Frontend->>Frontend: Generate table columns from attributes
Frontend->>Frontend: Generate filter controls from attributes
Frontend->>Frontend: Render workflows table + search + filters
Frontend-->>User: Display workflow list with search/filter UI
alt User performs general search
User->>Frontend: Type in search box (e.g., "invoice")
Frontend->>Frontend: Update search query
Frontend->>Backend: GET /api/workflows/?pagination=...filters:{"search":"invoice"}
Backend-->>Frontend: Filtered workflows list
Frontend-->>User: Display matching workflows
end
alt User applies field filter
User->>Frontend: Select filter field (e.g., "Status")
Frontend->>Frontend: Show filter options from attribute metadata
User->>Frontend: Select filter value (e.g., "running")
Frontend->>Frontend: Update filter parameters
Frontend->>Backend: GET /api/workflows/?pagination=...filters:{"status":"running"}
Backend-->>Frontend: Filtered workflows list
Frontend-->>User: Display filtered workflows
end
alt User combines search + filter + sort
User->>Frontend: Apply search + filter + sort
Frontend->>Frontend: Combine all parameters
Frontend->>Backend: GET /api/workflows/?pagination=...filters:{"search":"invoice","status":"running"}...sort:desc
Backend-->>Frontend: Filtered, sorted workflows list
Frontend-->>User: Display results
end
User->>Frontend: Click column header (e.g., "Last Activity")
Frontend->>Frontend: Update sort parameters
Frontend->>Backend: GET /api/workflows/?pagination=...sort:desc
Backend-->>Frontend: Sorted workflows list
Frontend-->>User: Display sorted workflows
User->>Frontend: Click workflow row
Frontend->>Frontend: Navigate to /workflows/workflowId
Frontend-->>User: Show workflow detail page
Customer Journey 2: Viewing Workflow Details
User Goal
"I want to see everything about a specific workflow—its current state, what it's doing, and what it has accomplished."
User Story
As a user, I want to open a workflow and see its complete information, including its configuration, current status, messages, and logs, all in one place.
User Story Flow
sequenceDiagram
participant User
participant Frontend
participant Backend
User->>Frontend: Click workflow from list
Frontend->>Backend: GET /api/workflows/workflowId
Backend-->>Frontend: Workflow object
Frontend->>Backend: GET /api/attributes/ChatWorkflow
Backend-->>Frontend: Field definitions
Frontend->>Backend: GET /api/workflows/workflowId/messages
Backend-->>Frontend: Messages list
Frontend->>Backend: GET /api/workflows/workflowId/logs
Backend-->>Frontend: Logs list
Frontend->>Frontend: Generate UI from metadata
Frontend->>Frontend: Render workflow info section
Frontend->>Frontend: Render messages section
Frontend->>Frontend: Render logs section
Frontend-->>User: Display complete workflow view
Frontend Requirements
📋 Complete frontend requirements for this journey are documented in Workflow Page Requirements
The frontend must implement a workflow detail page that displays workflow information, messages, and logs. All UI components are generated from backend metadata—see the requirements document for complete details.
// Fetch workflow and attributes
const [workflow, attributes] = await Promise.all([
fetch(`/api/workflows/${workflowId}`).then(r => r.json()),
fetch('/api/attributes/ChatWorkflow').then(r => r.json())
]);
// Separate editable vs read-only fields
const readOnlyFields = attributes.attributes.filter(attr =>
attr.visible && !attr.editable
);
const editableFields = attributes.attributes.filter(attr =>
attr.visible && attr.editable
);
// Render information section
<WorkflowInfoSection>
{readOnlyFields.map(attr => (
<InfoField
key={attr.name}
label={attr.label}
value={formatValue(workflow[attr.name], attr.type, attr.options)}
/>
))}
</WorkflowInfoSection>
// Show edit button if user has editable fields and permission
{editableFields.length > 0 && (
<Button onClick={() => navigate(`/workflows/${workflowId}/edit`)}>
Edit Workflow
</Button>
)}
Customer Journey 3: Monitoring Workflow Execution
User Goal
"I want to watch my workflow as it runs and see updates in real-time."
User Story
As a user, I want to monitor a running workflow and see new messages and logs appear automatically without refreshing the page.
User Story Flow
sequenceDiagram
participant User
participant Frontend
participant Backend
User->>Frontend: Open workflow detail page
Frontend->>Backend: GET /api/workflows/id/status
Backend-->>Frontend: Current workflow status
Frontend->>Backend: GET /api/workflows/id/messages
Backend-->>Frontend: Initial messages
Frontend->>Backend: GET /api/workflows/id/logs
Backend-->>Frontend: Initial logs
Frontend-->>User: Display initial state
loop Every 5 seconds (if running)
Frontend->>Backend: GET /api/workflows/id/status
Backend-->>Frontend: Updated status
Frontend->>Frontend: Update status badge
Frontend-->>User: Show status update
end
loop Every 3 seconds (if running)
Frontend->>Backend: GET /api/workflows/id/messages?messageId=lastId
Backend-->>Frontend: New messages (if any)
alt New messages exist
Frontend->>Frontend: Append to messages list
Frontend-->>User: Show new messages
end
end
loop Every 3 seconds (if running)
Frontend->>Backend: GET /api/workflows/id/logs?logId=lastId
Backend-->>Frontend: New logs (if any)
alt New logs exist
Frontend->>Frontend: Prepend to logs list
Frontend-->>User: Show new logs
end
end
Backend->>Backend: Workflow completes
Frontend->>Backend: GET /api/workflows/id/status
Backend-->>Frontend: Status: "completed"
Frontend->>Frontend: Stop all polling
Frontend-->>User: Show completion message
Customer Journey 4: Editing Workflow Properties
User Goal
"I want to change workflow settings like its name, status, or mode."
User Story
As a user, I want to edit a workflow's properties through a form that only shows fields I'm allowed to edit, with validation and clear error messages.
User Story Flow
sequenceDiagram
participant User
participant Frontend
participant Backend
User->>Frontend: Click "Edit" button
Frontend->>Frontend: Navigate to /workflows/id/edit
Frontend->>Backend: GET /api/workflows/workflowId
Backend-->>Frontend: Current workflow data
Frontend->>Backend: GET /api/attributes/ChatWorkflow
Backend-->>Frontend: Field definitions (editable fields)
Frontend->>Frontend: Filter editable fields
Frontend->>Frontend: Generate form from attributes
Frontend->>Frontend: Pre-populate form with workflow data
Frontend-->>User: Display edit form
User->>Frontend: Modify form fields
User->>Frontend: Click "Save"
Frontend->>Frontend: Validate form (required fields, types)
alt Validation fails
Frontend-->>User: Show validation errors
else Validation passes
Frontend->>Frontend: Optimistic update: Apply changes to UI immediately
Frontend->>Backend: PUT /api/workflows/workflowId + form data
alt Permission denied (403)
Backend-->>Frontend: 403 Forbidden
Frontend->>Frontend: Revert optimistic update
Frontend-->>User: Show permission error
else Validation error (400)
Backend-->>Frontend: 400 Bad Request + error details
Frontend->>Frontend: Revert optimistic update
Frontend-->>User: Show backend validation errors
else Success (200)
Backend-->>Frontend: Updated workflow
Frontend->>Frontend: Keep optimistic update (or refresh from response)
Frontend->>Frontend: Navigate to detail page
Frontend-->>User: Show updated workflow
end
end
Customer Journey 5: Cleaning Up Workflows
User Goal
"I want to delete workflows I no longer need."
User Story
As a user, I want to delete workflows that are completed or no longer useful, with a clear confirmation to prevent accidental deletion.
User Story Flow
sequenceDiagram
participant User
participant Frontend
participant Backend
User->>Frontend: Click "Delete" button
Frontend->>Backend: GET /api/workflows/workflowId
Backend-->>Frontend: Workflow data (for name)
Frontend->>Frontend: Show confirmation dialog<br/>"Delete 'Workflow Name'?"
User->>Frontend: Cancel deletion
Frontend-->>User: Dialog closed, no action
User->>Frontend: Click "Delete" button again
Frontend->>Backend: GET /api/workflows/workflowId
Backend-->>Frontend: Workflow data
Frontend->>Frontend: Show confirmation dialog
User->>Frontend: Confirm deletion
Frontend->>Frontend: Optimistic update: Remove workflow from UI immediately
Frontend->>Backend: DELETE /api/workflows/workflowId
alt Permission denied (403)
Backend-->>Frontend: 403 Forbidden
Frontend->>Frontend: Revert optimistic update: Restore workflow in UI
Frontend-->>User: Show permission error
else Not found (404)
Backend-->>Frontend: 404 Not Found
Frontend->>Frontend: Revert optimistic update: Restore workflow in UI
Frontend-->>User: Show not found error
else Success (200)
Backend-->>Frontend: Success response
Frontend->>Frontend: Keep optimistic update (workflow already removed)
Frontend->>Frontend: Show success message
Frontend->>Frontend: Navigate to /workflows (if on detail page)
Frontend-->>User: Display workflow list (without deleted workflow)
end
Frontend Requirements
📋 Complete frontend requirements for this journey are documented in Workflow Page Requirements
The frontend must implement workflow deletion with confirmation dialogs and proper error handling. See the requirements document for complete details.
Backend Routes Used
| Route | Purpose | When Used |
|---|---|---|
DELETE /api/workflows/{workflowId} |
Delete workflow | User confirmation |
User Navigation Flow
stateDiagram-v2
[*] --> WorkflowDetailPage
WorkflowDetailPage --> ViewingDeleteButton: Hover delete button
ViewingDeleteButton --> ClickingDelete: Click delete
ClickingDelete --> FetchingWorkflowName: Fetch workflow name
FetchingWorkflowName --> ShowingConfirmation: Show dialog
ShowingConfirmation --> WorkflowDetailPage: Cancel
ShowingConfirmation --> ConfirmingDeletion: Confirm
ConfirmingDeletion --> DeletingWorkflow: API call
DeletingWorkflow --> WorkflowListPage: Success
DeletingWorkflow --> ShowingError: Error (403/404)
ShowingError --> WorkflowDetailPage: Dismiss error
note right of ShowingConfirmation
ConfirmationDialog Component:
- Workflow name
- Warning message
- Cancel/Confirm buttons
end note
note right of DeletingWorkflow
DELETE /api/workflows/{id}
- Backend deletes workflow
- Deletes all associated data
- Returns success response
end note
note right of WorkflowListPage
List automatically refreshes
Deleted workflow removed
Success message shown
end note
Example User Flow
1. User is on workflow detail page
2. User clicks "Delete" button
3. Frontend shows confirmation dialog: "Are you sure you want to delete 'Workflow Name'? This action cannot be undone."
4. User confirms deletion
5. Frontend calls: DELETE /api/workflows/{workflowId}
6. Backend deletes workflow and all associated data
7. Frontend receives success response
8. Frontend shows success message: "Workflow deleted successfully"
9. Frontend redirects to /workflows (list page)
10. User sees updated workflow list (without deleted workflow)
Implementation Pattern
function WorkflowDetailPage({ workflowId }) {
const navigate = useNavigate();
const handleDelete = async () => {
// Fetch workflow name for confirmation
const workflow = await fetch(`/api/workflows/${workflowId}`).then(r => r.json());
if (!confirm(`Are you sure you want to delete "${workflow.name || 'this workflow'}"? This action cannot be undone.`)) {
return;
}
try {
const response = await fetch(`/api/workflows/${workflowId}`, {
method: 'DELETE'
});
if (response.status === 403) {
showError('You do not have permission to delete this workflow');
return;
}
if (response.status === 404) {
showError('Workflow not found');
return;
}
if (!response.ok) {
const error = await response.json();
showError(error.detail || 'Failed to delete workflow');
return;
}
showSuccess('Workflow deleted successfully');
navigate('/workflows');
} catch (error) {
showError('Failed to delete workflow');
}
};
return (
<div>
{/* Workflow content */}
<Button onClick={handleDelete} variant="danger">
Delete Workflow
</Button>
</div>
);
}
Backend Metadata System
How Metadata Works
The backend serves all UI metadata through the /api/attributes/{entityType} endpoint. This enables completely dynamic frontend generation with no hardcoding.
Attribute Definition Structure
interface AttributeDefinition {
name: string; // Field name (e.g., "status", "name")
type: string; // Frontend type: "text", "select", "integer", "timestamp", "checkbox"
label: string; // Display label (localized to user's language)
description: string; // Field description
required: boolean; // Whether field is required
readonly: boolean; // Whether field is read-only
editable: boolean; // Whether field can be edited (inverse of readonly)
visible: boolean; // Whether field should be displayed
order: number; // Display order (lower = earlier)
placeholder: string; // Placeholder text for inputs
options?: Option[]; // Options for select fields (with localized labels)
default?: any; // Default value
}
interface Option {
value: string; // Option value (e.g., "running")
label: { // Localized labels
[language: string]: string; // e.g., {"en": "Running", "fr": "En cours"}
};
}
Fetching Metadata
// Fetch attribute definitions for ChatWorkflow
const response = await fetch('/api/attributes/ChatWorkflow');
const data = await response.json();
const attributes = data.attributes; // AttributeDefinition[]
Using Metadata for Forms
// Generate form fields from attributes
const formFields = attributes
.filter(attr => attr.visible && attr.editable) // Only editable, visible fields
.sort((a, b) => a.order - b.order) // Sort by order
.map(attr => ({
name: attr.name,
label: attr.label,
type: attr.type,
required: attr.required,
options: attr.options,
placeholder: attr.placeholder,
default: attr.default
}));
Using Metadata for Tables
// Generate table columns from attributes
const tableColumns = attributes
.filter(attr => attr.visible) // Only visible fields
.sort((a, b) => a.order - b.order) // Sort by order
.map(attr => ({
key: attr.name,
label: attr.label,
type: attr.type,
sortable: true, // Can be made configurable
render: (value) => formatValue(value, attr.type, attr.options)
}));
Field Type Handling
Different field types require different UI components:
| Type | UI Component | Example Values |
|---|---|---|
text |
Text input | "My Workflow" |
textarea |
Textarea | Long descriptions |
integer |
Number input | 5, 10, 100 |
select |
Dropdown | Options from options array |
checkbox |
Checkbox | true/false |
timestamp |
Date/time picker | Unix timestamp |
Localization
All labels and option labels support multiple languages. The backend serves labels in the user's language (determined by authentication context). The frontend should:
- Use
labeldirectly (already localized) - For select options, use
option.label[userLanguage]or fallback tooption.value
Implementation Patterns
Pattern 1: Dynamic Table Component
function DynamicTable({ entityType, data, pagination, onSort, onPageChange, onRowClick }) {
const [attributes, setAttributes] = useState([]);
useEffect(() => {
fetch(`/api/attributes/${entityType}`)
.then(r => r.json())
.then(data => setAttributes(data.attributes.filter(attr => attr.visible)));
}, [entityType]);
const columns = attributes
.sort((a, b) => a.order - b.order)
.map(attr => ({
key: attr.name,
label: attr.label,
type: attr.type,
render: (value) => formatValue(value, attr.type, attr.options)
}));
return (
<Table>
<thead>
<tr>
{columns.map(col => (
<th key={col.key} onClick={() => handleSort(col.key)}>
{col.label}
</th>
))}
</tr>
</thead>
<tbody>
{data.map(row => (
<tr key={row.id} onClick={() => onRowClick?.(row)}>
{columns.map(col => (
<td key={col.key}>{col.render(row[col.key])}</td>
))}
</tr>
))}
</tbody>
</Table>
);
}
Pattern 2: Dynamic Form Component
function DynamicForm({ entityType, initialValues, onSubmit, onCancel }) {
const [attributes, setAttributes] = useState([]);
const [formData, setFormData] = useState(initialValues || {});
const [errors, setErrors] = useState({});
useEffect(() => {
fetch(`/api/attributes/${entityType}`)
.then(r => r.json())
.then(data => {
const editable = data.attributes.filter(attr => attr.visible && attr.editable);
setAttributes(editable);
});
}, [entityType]);
const handleSubmit = (e) => {
e.preventDefault();
// Validate
const newErrors = {};
attributes.forEach(attr => {
if (attr.required && !formData[attr.name]) {
newErrors[attr.name] = `${attr.label} is required`;
}
});
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
onSubmit(formData);
};
return (
<form onSubmit={handleSubmit}>
{attributes
.sort((a, b) => a.order - b.order)
.map(attr => (
<FormField
key={attr.name}
field={attr}
value={formData[attr.name] ?? attr.default}
onChange={(value) => setFormData(prev => ({ ...prev, [attr.name]: value }))}
error={errors[attr.name]}
/>
))}
<button type="submit">Save</button>
{onCancel && <button type="button" onClick={onCancel}>Cancel</button>}
</form>
);
}
Pattern 3: Polling Hook
function usePolling(url, interval, condition = true) {
const [data, setData] = useState(null);
useEffect(() => {
if (!condition) return;
const fetchData = async () => {
try {
const response = await fetch(url);
if (response.ok) {
const result = await response.json();
setData(result);
}
} catch (error) {
console.error('Polling error:', error);
}
};
fetchData(); // Initial fetch
const intervalId = setInterval(fetchData, interval);
return () => clearInterval(intervalId);
}, [url, interval, condition]);
return data;
}
// Usage
const workflow = usePolling(
`/api/workflows/${workflowId}/status`,
5000,
workflow?.status === 'running'
);
Pattern 4: Incremental Data Loading
function useIncrementalData(baseUrl, lastIdKey) {
const [items, setItems] = useState([]);
const [lastId, setLastId] = useState(null);
const loadInitial = async () => {
const response = await fetch(baseUrl).then(r => r.json());
const newItems = response.items || response;
setItems(newItems);
if (newItems.length > 0) {
setLastId(newItems[newItems.length - 1][lastIdKey]);
}
};
const loadNew = async () => {
if (!lastId) return;
const url = `${baseUrl}?${lastIdKey}=${lastId}`;
const response = await fetch(url).then(r => r.json());
const newItems = response.items || response;
if (newItems.length > 0) {
setItems(prev => [...prev, ...newItems]);
setLastId(newItems[newItems.length - 1][lastIdKey]);
}
};
useEffect(() => {
loadInitial();
}, [baseUrl]);
return { items, loadNew };
}
// Usage for messages
const { items: messages, loadNew: loadNewMessages } = useIncrementalData(
`/api/workflows/${workflowId}/messages`,
'messageId'
);
// Poll for new messages
useEffect(() => {
if (workflow?.status !== 'running') return;
const interval = setInterval(loadNewMessages, 3000);
return () => clearInterval(interval);
}, [workflow?.status, loadNewMessages]);
Summary
This documentation focuses on customer journeys—what users want to accomplish with workflows. Each journey describes:
- User Goal: What the user is trying to achieve
- User Story: The scenario from the user's perspective
- Frontend Requirements: What pages/components are needed
- Backend Routes: Which API endpoints support the journey
- Example Flows: Step-by-step user interactions
- Implementation Patterns: Code examples for common patterns
The key principle throughout: Everything is dynamic and driven by backend metadata. The frontend should never hardcode field definitions, validation rules, labels, or UI structure. All of this comes from the backend's attribute definition system.
By following these customer journeys and using the backend metadata system, you can build a frontend that:
- Adapts automatically to backend changes
- Supports multiple languages
- Enforces permissions correctly
- Provides real-time updates
- Requires minimal maintenance