# 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 1. [Overview](#overview) 2. [Customer Journey 1: Discovering and Browsing Workflows](#customer-journey-1-discovering-and-browsing-workflows) 3. [Customer Journey 2: Viewing Workflow Details](#customer-journey-2-viewing-workflow-details) 4. [Customer Journey 3: Monitoring Workflow Execution](#customer-journey-3-monitoring-workflow-execution) 5. [Customer Journey 4: Editing Workflow Properties](#customer-journey-4-editing-workflow-properties) 6. [Customer Journey 5: Managing Workflow Messages](#customer-journey-5-managing-workflow-messages) 7. [Customer Journey 6: Cleaning Up Workflows](#customer-journey-6-cleaning-up-workflows) 8. [Backend Metadata System](#backend-metadata-system) 9. [Implementation Patterns](#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 ```mermaid 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 ```mermaid 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](./workflow-page-requirements.md#customer-journey-2-viewing-workflow-details)** 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. ```typescript // 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 {readOnlyFields.map(attr => ( ))} // Show edit button if user has editable fields and permission {editableFields.length > 0 && ( )} ``` --- ## 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 ```mermaid 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 ```mermaid 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 ```mermaid 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
"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](./workflow-page-requirements.md#customer-journey-6-cleaning-up-workflows)** 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 ```mermaid 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 ```typescript 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 (
{/* Workflow content */}
); } ``` --- ## 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 ```typescript 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 ```typescript // 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 ```typescript // 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 ```typescript // 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: 1. Use `label` directly (already localized) 2. For select options, use `option.label[userLanguage]` or fallback to `option.value` --- ## Implementation Patterns ### Pattern 1: Dynamic Table Component ```typescript 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 ( {columns.map(col => ( ))} {data.map(row => ( onRowClick?.(row)}> {columns.map(col => ( ))} ))}
handleSort(col.key)}> {col.label}
{col.render(row[col.key])}
); } ``` ### Pattern 2: Dynamic Form Component ```typescript 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 (
{attributes .sort((a, b) => a.order - b.order) .map(attr => ( setFormData(prev => ({ ...prev, [attr.name]: value }))} error={errors[attr.name]} /> ))} {onCancel && } ); } ``` ### Pattern 3: Polling Hook ```typescript 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 ```typescript 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: 1. **User Goal**: What the user is trying to achieve 2. **User Story**: The scenario from the user's perspective 3. **Frontend Requirements**: What pages/components are needed 4. **Backend Routes**: Which API endpoints support the journey 5. **Example Flows**: Step-by-step user interactions 6. **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