gateway/docs/frontend-documentation/workflow-page-customer-journeys.md

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

  1. Overview
  2. Customer Journey 1: Discovering and Browsing Workflows
  3. Customer Journey 2: Viewing Workflow Details
  4. Customer Journey 3: Monitoring Workflow Execution
  5. Customer Journey 4: Editing Workflow Properties
  6. Customer Journey 5: Managing Workflow Messages
  7. Customer Journey 6: Cleaning Up Workflows
  8. Backend Metadata System
  9. 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:

  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

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:

  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