# 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 ``` ### Frontend Requirements #### Page: Workflow List (`/workflows`) **What the user sees:** - A table or card grid showing all workflows - Each workflow shows: name, status, last activity, progress indicators - General search box (searches across all fields) - Field-specific filter controls (one filter per visible field) - Sorting controls (by name, status, last activity, etc.) - Pagination controls (if many workflows) - Active filter indicators (showing which filters are applied) **What the frontend needs to do:** 1. **Fetch Workflow List** ``` GET /api/workflows/?pagination={"page":1,"pageSize":20,"sort":[],"filters":null} ``` - If user wants all workflows: omit `pagination` parameter - If user wants paginated view: include `pagination` JSON - Include `filters` in pagination object for search and field filtering - Filters structure: `{"search":"query","fieldName":"value",...}` 2. **Fetch Field Definitions** ``` GET /api/attributes/ChatWorkflow ``` - Use this to generate table columns dynamically - Determine which fields are visible, sortable, etc. 3. **Render Dynamic Table** - Generate columns from attribute definitions (filter `visible: true`) - Use `label` for column headers (localized) - Format values based on `type`: - `status` → Show as badge with color coding - `lastActivity` → Format as relative time ("2 hours ago") - `name` → Display as link to detail page - Support sorting: when user clicks column header, update `sort` in pagination and refetch 4. **Handle Pagination** - Display pagination controls using `pagination` metadata from response - Show "Page X of Y" and "Showing 1-20 of 45 workflows" - Handle page changes: update `page` in pagination params and refetch 5. **Handle General Search** - Display search input box (searches across all text fields) - When user types: update `filters.search` in pagination params - Debounce search input (wait 300-500ms after user stops typing) - Reset to page 1 when search changes - Refetch workflows with search filter applied 6. **Handle Field Filtering** - Generate filter controls dynamically from attribute definitions - For each visible field, show appropriate filter UI: - `text` fields → Text input filter - `select` fields → Dropdown with options from metadata - `integer` fields → Number range filter (min/max) - `timestamp` fields → Date range filter - `checkbox` fields → Boolean toggle filter - When user applies filter: update `filters[fieldName]` in pagination params - Support multiple filters simultaneously - Show active filter indicators (chips/badges) - Allow removing individual filters - Reset to page 1 when filters change - Refetch workflows with filters applied 7. **Handle Combined Search + Filter + Sort** - All parameters work together: search + filters + sort + pagination - When any parameter changes, combine all and refetch - Preserve all active filters when sorting or changing pages 8. **Handle Row Clicks** - Navigate to workflow detail page: `/workflows/{workflowId}` ### Backend Routes Used | Route | Purpose | When Used | |-------|---------|-----------| | `GET /api/workflows/` | Get all workflows | Initial page load, pagination changes, sort changes, filter changes, search changes | | `GET /api/attributes/ChatWorkflow` | Get field definitions | Page load (once) - used to generate columns, filters, and search UI | ### Example User Flow ``` 1. User navigates to /workflows 2. Frontend fetches: GET /api/attributes/ChatWorkflow 3. Frontend fetches: GET /api/workflows/?pagination={"page":1,"pageSize":20,"sort":[],"filters":null} 4. Frontend generates table columns from attributes 5. Frontend generates filter controls from attributes (one per visible field) 6. Frontend renders workflows table with search box and filter controls 7. User types "invoice" in search box 8. Frontend debounces (waits 400ms), then updates filters: {"search":"invoice"} 9. Frontend refetches: GET /api/workflows/?pagination={"page":1,"pageSize":20,"sort":[],"filters":{"search":"invoice"}} 10. Frontend displays matching workflows 11. User selects "Status" filter dropdown, chooses "running" 12. Frontend updates filters: {"search":"invoice","status":"running"} 13. Frontend refetches with combined filters 14. Frontend displays filtered results 15. User clicks "Last Activity" column header to sort 16. Frontend updates sort while preserving filters: {"search":"invoice","status":"running"} + sort 17. Frontend refetches with filters + sort 18. User clicks on a workflow row 19. Frontend navigates to /workflows/{workflowId} ``` ### Dynamic UI Generation ```typescript // Fetch attribute definitions const attributes = await fetch('/api/attributes/ChatWorkflow').then(r => r.json()); // Filter visible, sortable columns const tableColumns = attributes.attributes .filter(attr => attr.visible) .sort((a, b) => a.order - b.order) .map(attr => ({ key: attr.name, label: attr.label, // Localized label type: attr.type, sortable: true, // Can be made configurable render: (value) => formatValue(value, attr.type, attr.options) })); // Generate filter controls from attributes const filterControls = attributes.attributes .filter(attr => attr.visible) .map(attr => ({ field: attr.name, label: attr.label, type: attr.type, options: attr.options, // For select fields component: getFilterComponent(attr.type) // Returns appropriate filter UI component })); // Generate table from columns navigate(`/workflows/${workflow.id}`)} /> // Render search and filters handleSearch(query)} placeholder="Search workflows..." /> {filterControls.map(filter => ( handleFilterChange(filter.field, value)} /> ))} ``` --- ## 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 #### Page: Workflow Detail (`/workflows/{workflowId}`) **What the user sees:** - Workflow header: name, status badge, key metrics - Workflow information section: all workflow properties - Messages section: conversation/execution history - Logs section: execution logs with filtering - Action buttons: Edit, Delete (if user has permission) **What the frontend needs to do:** 1. **Fetch Workflow Data** ``` GET /api/workflows/{workflowId} ``` - Get complete workflow object - Includes nested data (messages, logs, stats) if available 2. **Fetch Field Definitions** ``` GET /api/attributes/ChatWorkflow ``` - Use to render workflow information section - Determine which fields are editable vs read-only 3. **Fetch Messages** (if not included in workflow object) ``` GET /api/workflows/{workflowId}/messages ``` - Get all messages for the workflow - Support pagination if many messages 4. **Fetch Logs** (if not included in workflow object) ``` GET /api/workflows/{workflowId}/logs ``` - Get all logs for the workflow - Support filtering by log type 5. **Render Workflow Information** - Display read-only fields as formatted text - Show editable fields with edit indicators (if user has permission) - Format special fields: - `status` → Status badge with color - `lastActivity`, `startedAt` → Formatted timestamps - `workflowMode` → Display mode label (not enum value) - Progress fields → Progress bars 6. **Render Messages Section** - Display messages in chronological order - Show message role (user/assistant/system) - Display attached documents/files - Support threading if `parentMessageId` exists 7. **Render Logs Section** - Display logs in reverse chronological order (newest first) - Color-code by type (info/warning/error) - Show progress indicators if `progress` field exists - Filter by log type (if implemented) ### Backend Routes Used | Route | Purpose | When Used | |-------|---------|-----------| | `GET /api/workflows/{workflowId}` | Get workflow details | Page load | | `GET /api/workflows/{workflowId}/messages` | Get workflow messages | Page load, refresh | | `GET /api/workflows/{workflowId}/logs` | Get workflow logs | Page load, refresh | | `GET /api/attributes/ChatWorkflow` | Get field definitions | Page load (once) | ### User Navigation Flow ```mermaid stateDiagram-v2 [*] --> WorkflowListPage WorkflowListPage --> WorkflowDetailPage: Click workflow WorkflowDetailPage --> WorkflowListPage: Click back WorkflowDetailPage --> WorkflowEditPage: Click Edit WorkflowDetailPage --> WorkflowDetailPage: View messages WorkflowDetailPage --> WorkflowDetailPage: View logs WorkflowDetailPage --> WorkflowDetailPage: Delete workflow WorkflowEditPage --> WorkflowDetailPage: Save or Cancel note right of WorkflowDetailPage Component Structure: - WorkflowHeader (name, status, metrics) - WorkflowInfoSection (all fields) - MessageList (chronological) - LogViewer (reverse chronological) - ActionButtons (edit, delete) end note note right of WorkflowDetailPage: User can scroll through
messages and logs
without leaving page ``` ### Example User Flow ``` 1. User clicks workflow from list → navigates to /workflows/{workflowId} 2. Frontend fetches: GET /api/workflows/{workflowId} 3. Frontend fetches: GET /api/attributes/ChatWorkflow 4. Frontend fetches: GET /api/workflows/{workflowId}/messages 5. Frontend fetches: GET /api/workflows/{workflowId}/logs 6. Frontend renders: - Workflow header with status badge - Information section (read-only fields from attributes) - Messages list (chronological) - Logs viewer (reverse chronological) 7. User sees complete workflow state ``` ### Dynamic UI Generation ```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 ``` ### Frontend Requirements #### Component: Real-Time Workflow Monitor **What the user sees:** - Status indicator that updates automatically - New messages appearing in the messages list - New logs appearing in the logs viewer - Progress indicators updating - Activity indicators showing workflow is active **What the frontend needs to do:** 1. **Initial Load** - Fetch workflow status: `GET /api/workflows/{workflowId}/status` - Fetch messages: `GET /api/workflows/{workflowId}/messages` - Fetch logs: `GET /api/workflows/{workflowId}/logs` 2. **Poll for Status Updates** ``` GET /api/workflows/{workflowId}/status ``` - Poll every 2-5 seconds if workflow status is "running" - Stop polling if workflow status is "completed", "stopped", or "error" - Update status badge and workflow information 3. **Poll for New Messages** (Incremental Loading) ``` GET /api/workflows/{workflowId}/messages?messageId={lastMessageId} ``` - Store ID of last displayed message - Poll every 2-3 seconds for messages after last message ID - Append new messages to list (don't reload all) - Only poll if workflow is "running" 4. **Poll for New Logs** (Incremental Loading) ``` GET /api/workflows/{workflowId}/logs?logId={lastLogId} ``` - Store ID of last displayed log - Poll every 2-3 seconds for logs after last log ID - Prepend new logs to list (newest first) - Only poll if workflow is "running" 5. **Handle Workflow Completion** - Stop all polling when status changes to "completed" or "stopped" - Show completion message - Update UI to indicate workflow is finished ### Backend Routes Used | Route | Purpose | When Used | |-------|---------|-----------| | `GET /api/workflows/{workflowId}/status` | Get current status | Polling (every 2-5s) | | `GET /api/workflows/{workflowId}/messages?messageId={id}` | Get new messages | Polling (every 2-3s) | | `GET /api/workflows/{workflowId}/logs?logId={id}` | Get new logs | Polling (every 2-3s) | ### User Interaction Flow ```mermaid stateDiagram-v2 [*] --> ViewingWorkflow ViewingWorkflow --> MonitoringActive: Workflow is running MonitoringActive --> UpdatingStatus: Poll status (5s) MonitoringActive --> UpdatingMessages: Poll messages (3s) MonitoringActive --> UpdatingLogs: Poll logs (3s) UpdatingStatus --> MonitoringActive: Continue if running UpdatingStatus --> WorkflowCompleted: Status = completed UpdatingMessages --> MonitoringActive: Continue if running UpdatingMessages --> WorkflowCompleted: Status = completed UpdatingLogs --> MonitoringActive: Continue if running UpdatingLogs --> WorkflowCompleted: Status = completed WorkflowCompleted --> ViewingWorkflow: Stop all polling note right of MonitoringActive User sees real-time updates: - Status badge changes - New messages appear - New logs appear - Progress bars update end note note right of WorkflowCompleted All polling stops Completion message shown UI updates to final state end note ``` ### Example User Flow ``` 1. User opens workflow detail page for running workflow 2. Frontend loads initial data (workflow, messages, logs) 3. Frontend starts polling: - Status: every 5 seconds - Messages: every 3 seconds (with lastMessageId) - Logs: every 3 seconds (with lastLogId) 4. User watches as: - Status updates (if changed) - New messages appear at bottom of list - New logs appear at top of list - Progress bars update 5. Workflow completes → status changes to "completed" 6. Frontend stops all polling 7. Frontend shows "Workflow completed" message ``` ### Implementation Pattern ```typescript function useWorkflowMonitoring(workflowId: string) { const [workflow, setWorkflow] = useState(null); const [messages, setMessages] = useState([]); const [logs, setLogs] = useState([]); const [lastMessageId, setLastMessageId] = useState(null); const [lastLogId, setLastLogId] = useState(null); // Status polling useEffect(() => { if (!workflow || workflow.status === 'completed' || workflow.status === 'stopped') { return; // Stop polling if completed } const interval = setInterval(async () => { const updated = await fetch(`/api/workflows/${workflowId}/status`).then(r => r.json()); setWorkflow(updated); }, 5000); return () => clearInterval(interval); }, [workflowId, workflow?.status]); // Message polling (incremental) useEffect(() => { if (!workflow || workflow.status !== 'running') return; const interval = setInterval(async () => { const url = lastMessageId ? `/api/workflows/${workflowId}/messages?messageId=${lastMessageId}` : `/api/workflows/${workflowId}/messages`; const response = await fetch(url).then(r => r.json()); if (response.items.length > 0) { setMessages(prev => [...prev, ...response.items]); setLastMessageId(response.items[response.items.length - 1].id); } }, 3000); return () => clearInterval(interval); }, [workflowId, workflow?.status, lastMessageId]); // Log polling (incremental) useEffect(() => { if (!workflow || workflow.status !== 'running') return; const interval = setInterval(async () => { const url = lastLogId ? `/api/workflows/${workflowId}/logs?logId=${lastLogId}` : `/api/workflows/${workflowId}/logs`; const response = await fetch(url).then(r => r.json()); if (response.items.length > 0) { setLogs(prev => [...response.items, ...prev]); // Prepend (newest first) setLastLogId(response.items[0].id); } }, 3000); return () => clearInterval(interval); }, [workflowId, workflow?.status, lastLogId]); return { workflow, messages, logs }; } ``` --- ## 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->>Backend: PUT /api/workflows/workflowId + form data alt Permission denied (403) Backend-->>Frontend: 403 Forbidden Frontend-->>User: Show permission error else Validation error (400) Backend-->>Frontend: 400 Bad Request + error details Frontend-->>User: Show backend validation errors else Success (200) Backend-->>Frontend: Updated workflow Frontend->>Frontend: Navigate to detail page Frontend-->>User: Show updated workflow end end ``` ### Frontend Requirements #### Page: Workflow Edit (`/workflows/{workflowId}/edit`) **What the user sees:** - Form with editable workflow fields - Only fields the user can edit (based on backend metadata) - Required field indicators - Validation errors (if any) - Save and Cancel buttons **What the frontend needs to do:** 1. **Fetch Workflow Data** ``` GET /api/workflows/{workflowId} ``` - Get current workflow values - Pre-populate form 2. **Fetch Field Definitions** ``` GET /api/attributes/ChatWorkflow ``` - Filter to only editable fields (`editable: true`) - Use to generate form fields dynamically - Get validation rules (required, type, options) 3. **Generate Dynamic Form** - Create form fields from attribute definitions - Use appropriate input types based on `type`: - `text` → Text input - `select` → Dropdown with `options` - `integer` → Number input - `checkbox` → Checkbox - Show required indicators for `required: true` fields - Use `label` for field labels (localized) - Use `placeholder` for placeholder text 4. **Client-Side Validation** - Validate required fields before submit - Validate types (e.g., integer fields must be numbers) - Validate select fields (value must be in options) 5. **Submit Changes** ``` PUT /api/workflows/{workflowId} ``` - Send only changed fields (or all form data) - Handle 403 (permission denied) gracefully - Handle 400 (validation errors) and display errors - On success: navigate back to detail page or show success message ### Backend Routes Used | Route | Purpose | When Used | |-------|---------|-----------| | `GET /api/workflows/{workflowId}` | Get workflow to edit | Page load | | `GET /api/attributes/ChatWorkflow` | Get editable field definitions | Page load | | `PUT /api/workflows/{workflowId}` | Update workflow | Form submit | ### User Navigation Flow ```mermaid stateDiagram-v2 [*] --> WorkflowDetailPage WorkflowDetailPage --> WorkflowEditPage: Click Edit button WorkflowEditPage --> EditingForm: Form loaded EditingForm --> EditingForm: Modify fields EditingForm --> ValidatingForm: Click Save ValidatingForm --> EditingForm: Validation errors ValidatingForm --> SubmittingForm: All valid SubmittingForm --> WorkflowDetailPage: Success SubmittingForm --> EditingForm: Backend errors WorkflowEditPage --> WorkflowDetailPage: Click Cancel note right of EditingForm Components: - DynamicForm (generated from metadata) - FormFields (text, select, number, etc.) - ValidationErrors (inline) end note note right of ValidatingForm Client-side validation: - Required fields - Type checking - Format validation end note note right of SubmittingForm Backend validation: - Permission check - Server-side rules - Data integrity end note ``` ### Example User Flow ``` 1. User clicks "Edit" button on workflow detail page 2. Frontend navigates to /workflows/{workflowId}/edit 3. Frontend fetches workflow and attributes 4. Frontend generates form from editable attributes 5. Frontend pre-populates form with workflow values 6. User changes workflow name and status 7. User clicks "Save" 8. Frontend validates form (required fields, types) 9. Frontend submits: PUT /api/workflows/{workflowId} with form data 10. Backend validates and updates workflow 11. Frontend navigates back to detail page 12. User sees updated workflow ``` ### Dynamic Form Generation ```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()) ]); // Filter editable fields const editableFields = attributes.attributes.filter(attr => attr.visible && attr.editable ); // Generate form fields { try { const response = await fetch(`/api/workflows/${workflowId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); if (response.status === 403) { showError('You do not have permission to edit this workflow'); return; } if (!response.ok) { const error = await response.json(); showError(error.detail); return; } navigate(`/workflows/${workflowId}`); } catch (error) { showError('Failed to update workflow'); } }} /> // DynamicForm component renders fields based on type function DynamicForm({ fields, initialValues, onSubmit }) { const [formData, setFormData] = useState(initialValues); const [errors, setErrors] = useState({}); const handleSubmit = (e) => { e.preventDefault(); // Validate required fields const newErrors = {}; fields.forEach(field => { if (field.required && !formData[field.name]) { newErrors[field.name] = `${field.label} is required`; } }); if (Object.keys(newErrors).length > 0) { setErrors(newErrors); return; } onSubmit(formData); }; return (
{fields.map(field => ( setFormData(prev => ({ ...prev, [field.name]: value }))} error={errors[field.name]} /> ))} ); } ``` --- ## Customer Journey 5: Managing Workflow Messages ### User Goal "I want to view, organize, and clean up messages in a workflow." ### User Story As a user, I want to see all messages in a workflow, view message details including attached files, and delete messages if needed. ### User Story Flow ```mermaid sequenceDiagram participant User participant Frontend participant Backend User->>Frontend: Open workflow detail page Frontend->>Backend: GET /api/workflows/workflowId/messages Backend-->>Frontend: Messages list Frontend->>Frontend: Sort by publishedAt Frontend->>Frontend: Render messages chronologically Frontend-->>User: Display messages with files User->>Frontend: Click delete button on message Frontend->>Frontend: Show confirmation dialog User->>Frontend: Confirm deletion Frontend->>Backend: DELETE /api/workflows/workflowId/messages/messageId Backend-->>Frontend: Success response Frontend->>Frontend: Remove message from UI Frontend-->>User: Updated message list User->>Frontend: Click delete button on file Frontend->>Frontend: Show confirmation dialog User->>Frontend: Confirm deletion Frontend->>Backend: DELETE /api/workflows/workflowId/messages/messageId/files/fileId Backend-->>Frontend: Success response Frontend->>Frontend: Remove file from message Frontend-->>User: Updated message display ``` ### Frontend Requirements #### Component: Message Management **What the user sees:** - List of all messages in chronological order - Each message shows: role, content, timestamp, attached files - Message actions: View details, Delete (if permitted) - Pagination if many messages **What the frontend needs to do:** 1. **Fetch Messages** ``` GET /api/workflows/{workflowId}/messages ``` - Get all messages (or paginated if many) - Support pagination for large message lists 2. **Display Messages** - Show messages in chronological order (`publishedAt`) - Display message role (user/assistant/system) with styling - Show message content or summary - Display attached documents/files if `documents` array has items - Format timestamps as readable dates 3. **Handle Message Deletion** ``` DELETE /api/workflows/{workflowId}/messages/{messageId} ``` - Show delete button/action for each message - Show confirmation dialog before deletion - After deletion: refresh message list or remove from UI 4. **Handle File Deletion** (within messages) ``` DELETE /api/workflows/{workflowId}/messages/{messageId}/files/{fileId} ``` - Show delete button for each file attachment - Show confirmation before deletion - After deletion: update message display (remove file from list) ### Backend Routes Used | Route | Purpose | When Used | |-------|---------|-----------| | `GET /api/workflows/{workflowId}/messages` | Get all messages | Initial load, refresh | | `DELETE /api/workflows/{workflowId}/messages/{messageId}` | Delete message | User action | | `DELETE /api/workflows/{workflowId}/messages/{messageId}/files/{fileId}` | Delete file | User action | ### User Interaction Flow ```mermaid stateDiagram-v2 [*] --> ViewingMessages ViewingMessages --> ViewingMessage: Click message ViewingMessage --> ViewingFile: Click file ViewingMessage --> DeletingMessage: Click delete ViewingFile --> ViewingMessage: Close file view DeletingMessage --> ConfirmDeleteMessage: Show dialog ConfirmDeleteMessage --> ViewingMessages: Cancel ConfirmDeleteMessage --> DeletingMessageAPI: Confirm DeletingMessageAPI --> ViewingMessages: Success ViewingMessage --> DeletingFile: Click delete file DeletingFile --> ConfirmDeleteFile: Show dialog ConfirmDeleteFile --> ViewingMessage: Cancel ConfirmDeleteFile --> DeletingFileAPI: Confirm DeletingFileAPI --> ViewingMessage: File removed note right of ViewingMessages Component: MessageList - Chronological order - Scrollable list - Pagination support end note note right of ViewingMessage Component: MessageCard - Full message content - Attached files - Action buttons end note note right of DeletingMessageAPI Updates UI immediately Optimistic update pattern end note ``` ### Example User Flow ``` 1. User opens workflow detail page 2. Frontend fetches: GET /api/workflows/{workflowId}/messages 3. Frontend displays messages in chronological list 4. User sees a message with attached files 5. User clicks on a file to view it 6. User decides to delete a message 7. User clicks delete button → confirmation dialog appears 8. User confirms deletion 9. Frontend calls: DELETE /api/workflows/{workflowId}/messages/{messageId} 10. Frontend removes message from UI (or refreshes list) 11. User sees updated message list ``` ### Implementation Pattern ```typescript function MessageList({ workflowId }) { const [messages, setMessages] = useState([]); useEffect(() => { fetch(`/api/workflows/${workflowId}/messages`) .then(r => r.json()) .then(data => setMessages(data.items || data)); }, [workflowId]); const handleDeleteMessage = async (messageId) => { if (!confirm('Are you sure you want to delete this message?')) { return; } try { await fetch(`/api/workflows/${workflowId}/messages/${messageId}`, { method: 'DELETE' }); // Remove from UI setMessages(prev => prev.filter(msg => msg.id !== messageId)); } catch (error) { showError('Failed to delete message'); } }; const handleDeleteFile = async (messageId, fileId) => { if (!confirm('Are you sure you want to delete this file?')) { return; } try { await fetch(`/api/workflows/${workflowId}/messages/${messageId}/files/${fileId}`, { method: 'DELETE' }); // Update message in UI (remove file from documents array) setMessages(prev => prev.map(msg => msg.id === messageId ? { ...msg, documents: msg.documents.filter(doc => doc.fileId !== fileId) } : msg )); } catch (error) { showError('Failed to delete file'); } }; return (
{messages.map(message => ( {message.documents?.map(doc => ( handleDeleteFile(message.id, doc.fileId)} /> ))} ))}
); } ``` --- ## Customer Journey 6: 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->>Backend: DELETE /api/workflows/workflowId alt Permission denied (403) Backend-->>Frontend: 403 Forbidden Frontend-->>User: Show permission error else Not found (404) Backend-->>Frontend: 404 Not Found Frontend-->>User: Show not found error else Success (200) Backend-->>Frontend: Success response Frontend->>Frontend: Show success message Frontend->>Frontend: Navigate to /workflows Frontend-->>User: Display workflow list (without deleted workflow) end ``` ### Frontend Requirements #### Action: Delete Workflow **What the user sees:** - Delete button/action on workflow detail page or list - Confirmation dialog before deletion - Success message after deletion - Redirect to workflow list after deletion **What the frontend needs to do:** 1. **Show Delete Action** - Display delete button (only if user has permission—backend will enforce) - Can be on detail page or in list row actions 2. **Handle Delete Confirmation** - Show confirmation dialog when user clicks delete - Display workflow name in confirmation - Warn about permanent deletion 3. **Execute Deletion** ``` DELETE /api/workflows/{workflowId} ``` - Call delete endpoint - Handle 403 (permission denied) gracefully - Handle 404 (not found) gracefully 4. **Handle Success** - Show success message - If on detail page: redirect to workflow list - If on list page: remove workflow from list (or refresh) ### 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