Skip to main content

UX Standards — 23: Visual Workflow Builder

Governs: The @xyflow/react (ReactFlow)-based canvas builder at /settings/workflows. Parent rules: See 00-OVERVIEW-AND-CSS-RULES.md first.


Overview

The Workflow section at /settings/workflows provides a visual interface for creating and managing two types of workflows:

TypeDescriptionBuilder
DocType WorkflowsERPNext document state machine — defines document states, allowed actions, and transitions between statesReactFlow canvas (DocType Workflow Builder)
Process OrchestrationsN8N-powered automation flows — trigger-action-gateway pipelines with AI nodesPlanned (JSON/N8N integration)

The system uses @xyflow/react for the visual canvas. N8N handles the underlying orchestration execution.


Route Structure

/settings/workflows → All Workflows list (tabs: All / Getting Started / Monitor / History / Library)
/settings/workflows/getting-started → Getting Started guide
/settings/workflows/monitor → Process Monitor
/settings/workflows/history → History log
/settings/workflows/library → Workflow Template Library
/settings/workflows/new → Create new workflow
/settings/workflows/[name] → Workflow detail (states/transitions tables)
/settings/workflows/[name]/builder → Visual canvas builder

Workflows List Page

The main /settings/workflows page has 5 tabs across the top and stat cards:

Tabs

TabRoute
All Workflows/settings/workflows
Getting Started/settings/workflows/getting-started
Monitor/settings/workflows/monitor
History/settings/workflows/history
Library/settings/workflows/library

Stat Cards

  • DocType Workflows — count of ERPNext workflow definitions
  • Orchestrations — count of N8N process orchestrations

Table Columns

Name | Type | Status | DocType / Category | Details

Row actions include "Open Builder" which navigates to /settings/workflows/[name]/builder.


Getting Started Page

Designed for first-time users:

Hero section:

  • Title: "Welcome to Workflows"
  • CTAs: "Create Your First Workflow" + "Browse Templates"
  • Stats bar: Active Workflows | Running Processes | Completed Today | Avg Duration

How It Works (4-step stepper):

  1. Choose a workflow type
  2. Design your flow visually
  3. Set triggers and conditions
  4. Activate and monitor

Choose Your Workflow Type (cards):

  • DocType Workflow — ERPNext document state machine
  • Process Orchestration — N8N automation pipeline

Quick Links section:

  • Links to documentation, templates, monitor

Process Monitor Page

/settings/workflows/monitor

Stat cards: Total | Running | Waiting | Completed | Failed

Table columns: Process | Status | Current Step | Progress | Trigger | Started

Progress shown as a progress bar or percentage.


Workflow Library Page

/settings/workflows/library

Browse pre-built workflow templates:

Table columns: Name | Type | Target / Category | Status | Version | Usage

Row actions: eye (preview) + edit (clone/use template) icons.


DocType Workflow Builder Canvas

The WorkflowBuilderCanvas component renders a full-page ReactFlow canvas for designing ERPNext DocType workflows.

packages/shared → used via:
src/app/(dashboard)/settings/workflows/[name]/builder/components/workflow-builder-canvas.tsx
src/app/(dashboard)/settings/workflows/[name]/builder/components/workflow-canvas-nodes.tsx
src/app/(dashboard)/settings/workflows/[name]/builder/components/workflow-canvas-sidebar.tsx

Full-Page Layout

┌───────────────────────────────────────────────────────────────────┐
│ ← [WorkflowName] [DocType] • N states • M transitions [Unsaved]│ ← Builder Header
│ [💾][↩][↪] | [+][🗑] | [⧉][📋] | [+][−][⛶] | [↓][↑] | [✓][⚙][?] │
├─────────────────────────────────────────────────┬─────────────────┤
│ │ │
│ ReactFlow Canvas │ Properties │
│ (dot grid background) │ Sidebar │
│ │ (w-72) │
│ ┌────┐ [Action] ┌────────┐ │ │
│ │ ●→ │ ────────────────▶ │ State │ │ (no selection)│
│ └────┘ └────────┘ │ Workflow Props │
│ │ + shortcuts │
│ ┌ Element Toolbox ┐ (top-right overlay) │ │
│ │ [State] [Start] │ │ (node select) │
│ │ Drag to canvas │ │ State Props │
│ └─────────────────┘ │ + delete btn │
│ │ (edge select) │
│ [MiniMap - bottom right] │ Transition │
│ [Controls - bottom left] │ Props │
│ [Validation panel - bottom right if errors] │ + delete btn │
└─────────────────────────────────────────────────┴─────────────────┘

Builder Header Bar

Fixed strip across the top with workflow info and toolbar:

Left Zone

  • Back button (ArrowLeft h-4 w-4) — prompts if unsaved changes
  • Workflow name (text-lg font-semibold)
  • Subtitle{DocType} • {N} states • {M} transitions (text-xs text-muted-foreground)
  • Unsaved badgeBadge variant="outline" with border-amber-600 text-amber-600 when dirty

Right Zone — Toolbar (all Button variant="ghost" size="icon")

ButtonIconNotes
SaveSave h-4 w-4Disabled when not dirty
(separator)
UndoUndo2 h-4 w-4Disabled when stack empty
RedoRedo2 h-4 w-4Disabled when stack empty
(separator)
Add StatePlus h-4 w-4Adds new state at center
DeleteTrash2 h-4 w-4Disabled when nothing selected
(separator)
CopyCopy h-4 w-4Currently disabled
PasteClipboardPaste h-4 w-4Currently disabled
(separator)
Zoom InZoomIn h-4 w-4reactFlowInstance.zoomIn()
Zoom OutZoomOut h-4 w-4reactFlowInstance.zoomOut()
Fit ViewMaximize2 h-4 w-4fitView({ padding: 0.2 })
(separator)
ExportDownload h-4 w-4Export workflow JSON
ImportUpload h-4 w-4Import workflow JSON
(separator)
ValidateCheckCircle or AlertCircle h-4 w-4Red when errors exist
(separator)
Toggle SidebarSettings h-4 w-4Show/hide properties sidebar
HelpHelpCircle h-4 w-4

Canvas Configuration

<ReactFlow
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
fitView
fitViewOptions={{ padding: 0.2 }}
connectionLineStyle={{ stroke: '#9ca3af', strokeWidth: 2 }}
>
<Background variant={BackgroundVariant.Dots} gap={20} size={1} />
<MiniMap nodeColor={(node) => ...} className='!bg-background/80' />
<Controls position='bottom-left' showZoom={false} showFitView={false} showInteractive={false} />
</ReactFlow>
FeatureValue
BackgroundDots variant, gap={20}, size={1}
MiniMapBottom-right, color-coded by node state style
ControlsBottom-left (custom — built-in zoom/fit controls hidden)
Default edgetransitionEdge type, ArrowClosed marker, #9ca3af
Fit on loadfitView with padding: 0.2

Node Types

Start Node (startNode)

Green filled circle (32×32px):

h-8 w-8 rounded-full bg-green-500
  • Contains a play (▶) icon in white
  • Has source handles on Right and Bottom
  • Selection ring: ring-2 ring-blue-500 ring-offset-2

State Node (stateNode)

Rounded rectangle with label:

StyleBackgroundTextBorderWhen
Defaultbg-white dark:bg-gray-800text-gray-900border-gray-300Normal states
Successbg-green-500text-whiteborder-green-600doc_status === '1'
Dangerbg-red-500text-whiteborder-red-600doc_status === '2'
Primary(default)New, Received
Warning(default + amber ring)Pending Review states
Info(default + cyan ring)In Progress states

Content:

  • Label: text-xs font-medium
  • Role badge (below label): user icon + allow_edit role name in text-[10px]
  • Handles on all 4 sides (top/bottom: source+target, left/right: source+target)
  • Selection ring: ring-1 ring-blue-500 ring-offset-1

Auto-Style Inference

State nodes are automatically styled based on their state name and doc_status:

ConditionStyle
doc_status === '2'Danger
doc_status === '1'Success
State name in ['New', 'Received']Primary
State name contains Confirmed, Completed, High Confidence, Human Approved, Processed, RoutedSuccess
State name contains Cancelled, Declined, Failed, Lost, SpamDanger
State name contains Pending Review, Awaiting, Human CorrectedWarning
State name contains Checking, Classifying, Extracting, In ProgressInfo

Edge Types

Transition Edge (transitionEdge)

Dashed line with arrowhead and label:

  • Line style: strokeDasharray="5 3", color #b0b0b0 (selected: #3b82f6)
  • Stroke width: 1 normal, 1.5 selected
  • Arrowhead: ArrowClosed via SVG marker
  • Label: Action name displayed in a foreignObject at the midpoint
    • Label box: bg-white dark:bg-gray-800, border-gray-200 dark:border-gray-600
    • Label text: text-[10px] font-medium text-gray-600
    • Selected label: border-blue-400 text-blue-600

Element Toolbox (Overlay)

Positioned absolute right-4 top-4 z-10 on the canvas:

┌─────────────────────┐
│ Elements │ ← title (text-xs text-gray-500)
├─────────────────────┤
│ [State] State │ ← draggable item (stateNode)
│ [●] Start │ ← draggable item (startNode)
├─────────────────────┤
│ Drag onto canvas │ ← hint text
└─────────────────────┘
  • Width: w-40
  • Card: rounded-lg border bg-white dark:bg-gray-900 p-3 shadow-lg
  • Draggable items: cursor-grab, hover highlights (blue for state, green for start)
  • Drag behavior: onDragStart sets dataTransfer.setData('application/reactflow', nodeType)
  • Drop target: onDrop on the canvas wrapper converts screen coords to flow coords via reactFlowInstance.screenToFlowPosition()

Properties Sidebar

Right-side panel, w-72, with overflow-y-auto border-l bg-card:

No Selection — Workflow Properties

  • Document Type (read-only label)
  • Is Active (Checkbox)
  • Don't Override Status (Checkbox)
  • Send Email Alert (Checkbox)
  • Keyboard shortcuts reminder

Node Selected — State Properties

FieldComponent
State NameInput (read-only)
Doc StatusSelect (Draft/Submitted/Cancelled)
Only Allow Edit ForInput (read-only)
Update FieldInput (read-only, optional)
Update ValueInput (read-only, optional)
Delete StateButton variant="destructive" full-width

Edge Selected — Transition Properties

FieldComponent
ActionInput (read-only)
Allowed RoleInput (read-only)
Allow Self ApprovalCheckbox
ConditionTextarea rows={4} (monospace, read-only)
Delete TransitionButton variant="destructive" full-width

Validation Panel

Shown in bottom-right Panel when validationErrors.length > 0:

<div className='max-w-sm rounded-lg border border-red-200 bg-red-50 p-3 dark:border-red-800 dark:bg-red-950'>
<div className='mb-2 flex items-center gap-2 text-sm font-medium text-red-800 dark:text-red-200'>
<AlertCircle className='h-4 w-4' />
{validationErrors.length} validation issue(s)
</div>
<ul className='space-y-1 text-xs text-red-700 dark:text-red-300'>
{validationErrors.slice(0, 5).map((error) => (
<li>{error}</li>
))}
{validationErrors.length > 5 && <li>...and N more</li>}
</ul>
</div>

Validation checks:

  • States with no transitions (isolated nodes)
  • Workflow should have an initial state (no incoming edges)

Keyboard Shortcuts

ShortcutAction
Ctrl/⌘ + SSave workflow
Delete / BackspaceDelete selected node or edge
NAdd new state at canvas center
EscapeDeselect current selection

Drag & Drop

Nodes can be dragged from the Element Toolbox onto the canvas:

  1. draggable attribute on toolbox items
  2. onDragStart sets node type in dataTransfer
  3. Canvas onDrop converts cursor position to flow coordinates
  4. New node created at drop position with default data

Data Model

// ERPNext Workflow
interface Workflow {
workflow_name: string;
document_type: string;
is_active: 0 | 1;
override_status: 0 | 1;
send_email_alert: 0 | 1;
workflow_data?: string; // JSON string of node positions
states: WorkflowState[];
transitions: WorkflowTransition[];
}

// State Node Data
interface StateNodeData {
label: string;
state: WorkflowState;
style: '' | 'Primary' | 'Success' | 'Danger' | 'Warning' | 'Info';
hasError?: boolean;
hasWarning?: boolean;
}

// Transition Edge Data
interface TransitionEdgeData {
transition: WorkflowTransition;
actionLabel: string;
roleLabel: string;
hasCondition: boolean;
allowSelfApproval: boolean;
}

MiniMap Color Coding

StyleColor
Success#22c55e (green-500)
Danger#ef4444 (red-500)
Warning#f59e0b (amber-500)
Primary#3b82f6 (blue-500)
Info#06b6d4 (cyan-500)
Default#9ca3af (gray-400)

CSS Tokens for Workflow Nodes

Workflow node colors are defined in _variables.css under --node-* tokens:

/* Start Node - Green */
--node-start: 142 76% 36%;
--node-start-bg: 142 76% 95%;
--node-start-border: 142 76% 70%;

/* Gateway Node - Amber */
--node-gateway: 38 92% 50%;
--node-gateway-bg: 38 92% 95%;
--node-gateway-border: 38 92% 75%;

/* Action Node - Emerald */
--node-action: 160 84% 39%;

/* Timer Node - Indigo */
--node-timer: 239 84% 67%;

/* Signal Node - Pink */
--node-signal: 330 81% 60%;
/* ... etc */

See 24-GLOBAL-CSS-AND-DESIGN-TOKENS.md for the complete token list.


Violation Checklist

  • Uses @xyflow/react (NOT reactflow v10 or older) — import from '@xyflow/react'
  • Canvas background: BackgroundVariant.Dots with gap={20} size={1}
  • MiniMap: className='!bg-background/80'
  • Controls: position='bottom-left' with all built-in buttons hidden
  • Element Toolbox: absolute right-4 top-4 z-10 w-40
  • Start node: h-8 w-8 rounded-full bg-green-500
  • State node: rounded rectangle, min-width 100px, auto-styled by inferStyle()
  • Transition edge: strokeDasharray="5 3", label at midpoint in foreignObject
  • Properties sidebar: w-72 border-l bg-card
  • Save shortcut: Ctrl/⌘+S
  • Delete shortcut: Delete/Backspace on selected element
  • "Unsaved" badge: border-amber-600 text-amber-600
  • Validation panel: bg-red-50 dark:bg-red-950 border-red-200
  • Back button prompts if dirty: confirm(t('popup.workflow.unsavedWarning'))