Skip to main content

Template Statuses & Workflow Builder

Overview

This feature adds two layers on top of Application Templates:

  1. Statuses — each template defines its own custom statuses (replacing the old hardcoded enum). Statuses have a state-machine of allowed transitions between them.
  2. Workflow — visual process graph (ReactFlow canvas) of workflow steps. Each step references a status and optionally runs an automated system handler. Steps are connected by transitions.

When an application moves through the workflow, its currentWorkflowStepId changes and its internal/external status is updated to match the step's linked TemplateStatus.


Architecture: Two Layers

Layer 1 — Status Machine (TemplateStatus + TemplateStatusTransition)
Defines what statuses exist and which transitions are allowed (USER/SYSTEM/BOTH).

Layer 2 — Workflow Steps (WorkflowStep + WorkflowStepTransition)
Visual process graph drawn on ReactFlow canvas. Each step references a TemplateStatus
and optionally has a system handler (automated logic). Steps are connected by edges.

Backend is authoritative for all workflow advancement. The frontend only:

  • Sends advanceWorkflowStep for USER-type transitions (officer decision)
  • Sends triggerWorkflowStepCheck to manually re-run a SYSTEM step's handler

System transitions fire automatically via WorkflowEventListener (domain events) → WorkflowEngine.


File Structure

Frontend

src/features/application-templates/
├── data/
│ ├── use-template-statuses.ts # All status + transition GQL hooks
│ └── use-workflow.ts # All workflow step + transition + definition GQL hooks
└── detail/
└── components/
├── application-template-statuses.tsx # Statuses tab: CRUD table + transitions matrix
├── application-template-workflow.tsx # Workflow tab: ReactFlow canvas builder
└── workflow/
├── workflow-step-node.tsx # Custom ReactFlow node component
├── workflow-step-panel.tsx # Right-side sheet for step config (inc. stepId field)
├── workflow-transition-panel.tsx # Right-side sheet for create/edit transitions
├── workflow-json-dialog.tsx # Wide dialog: "Edit as JSON" bulk editor
└── workflow-canvas.css # Dark-mode overrides for ReactFlow controls

src/features/applications/
├── data/
│ └── use-application-workflow.ts # Hooks: status history, workflow info, advance, triggerCheck
└── detail/
└── components/
├── application-workflow-flow.tsx # Read-only ReactFlow on application overview
└── application-workflow-history.tsx # Status transition history table

src/routes/_authenticated/o/$orgSlug/application-setup/templates/$templateId/
├── statuses/index.tsx # Route → ApplicationTemplateStatuses
└── workflow/index.tsx # Route → ApplicationTemplateWorkflow

Deleted: workflow-transition-dialog.tsx — replaced entirely by workflow-transition-panel.tsx

Backend

core/src/main/java/com/axvero/ams/core/
├── document/domain/
│ ├── TemplateStatus.java # Custom status per template (inc. manuallySettable, sortOrder)
│ ├── TemplateStatusTransition.java # Allowed from→to with USER/SYSTEM/BOTH
│ └── TemplateTransitionType.java # Enum: USER | SYSTEM | BOTH
├── workflow/
│ ├── domain/
│ │ ├── WorkflowStep.java # Inc. stepId slug (unique per template)
│ │ ├── WorkflowStepRepository.java # Inc. findByApplicationTemplateIdAndStepId etc.
│ │ ├── WorkflowStepTransition.java
│ │ └── WorkflowStepTransitionRepository.java
│ ├── handlers/
│ │ ├── WorkflowStepHandler.java # Interface: getHandlerId, getDisplayName, execute
│ │ ├── WorkflowHandlerRegistry.java
│ │ └── DocumentCompletenessCheckHandler.java
│ ├── WorkflowDefinitionResult.java # Record: List<WorkflowStep> steps, List<WorkflowStepTransition> transitions
│ ├── WorkflowEngine.java
│ ├── WorkflowEventListener.java
│ ├── WorkflowService.java # CRUD + replaceWorkflow + stepId auto-gen + lazy backfill
│ └── web/
│ ├── WorkflowController.java # Inc. replaceWorkflow mutation
│ ├── WorkflowStepMapper.java # Inc. toDefinitionStepDto, toDefinitionTransitionDto
│ └── dto/
│ ├── WorkflowStepDto.java # Inc. stepId
│ ├── WorkflowStepInput.java # Inc. stepId
│ ├── WorkflowStepTransitionDto.java
│ ├── WorkflowStepTransitionInput.java
│ ├── WorkflowDefinitionDto.java # { steps, transitions } for JSON editor response
│ ├── WorkflowDefinitionStepDto.java # stepId(slug), name, statusId(slug), ...
│ ├── WorkflowDefinitionTransitionDto.java # fromStepId(slug), toStepId(slug), ...
│ ├── WorkflowDefinitionInput.java # top-level input for replaceWorkflow
│ ├── WorkflowStepDefinitionInput.java # stepId!(slug), name!, statusId(slug), ...
│ ├── WorkflowTransitionDefinitionInput.java # fromStepId(slug), toStepId(slug)
│ ├── WorkflowHandlerInfoDto.java
│ └── WorkflowCheckResultDto.java

core/src/main/resources/graphql/
├── workflow.graphqls # WorkflowStep (inc. stepId), WorkflowDefinition types, queries, mutations
├── workflow-input.graphqls # WorkflowStepInput (inc. stepId), WorkflowDefinitionInput types
└── documents.graphqls # TemplateStatus (inc. manuallySettable, sortOrder), transitions

Data Hooks

use-template-statuses.ts

// Types
TemplateStatus // id, statusId(slug), numericCode, internalName, externalName,
// useInternalAsExternal, description, color, isInitial, isFinal,
// manuallySettable, sortOrder
TemplateStatusTransition // id, fromStatus, toStatus, transitionType, label

// Query hooks
useTemplateStatuses(templateId) // → TemplateStatus[]
useTemplateStatusTransitions(templateId) // → TemplateStatusTransition[]

// Mutation hooks
useCreateTemplateStatus(templateId)
useUpdateTemplateStatus(templateId) // takes { id, input } — statusId CAN be renamed
useDeleteTemplateStatus(templateId)
useCreateTemplateStatusTransition(templateId)
useDeleteTemplateStatusTransition(templateId)
useReorderTemplateStatuses() // takes { templateId, statusIds: string[] }

use-workflow.ts

// Types
WorkflowStep // id, applicationTemplateId, stepId(slug), name, description,
// stepType (MANUAL|SYSTEM), systemHandler, templateStatus, positionX, positionY, sortOrder
WorkflowStepTransition // id, fromStep, toStep, transitionType, label
WorkflowHandlerInfo // handlerId, displayName
WorkflowStepType // 'MANUAL' | 'SYSTEM'
TransitionType // 'USER' | 'SYSTEM' | 'BOTH'

// JSON definition types (used by WorkflowJsonDialog)
WorkflowDefinitionStep // stepId(slug), name, description, stepType, systemHandler,
// statusId(slug), positionX, positionY, sortOrder
WorkflowDefinitionTransition // fromStepId(slug), toStepId(slug), transitionType, label
WorkflowDefinition // { steps: WorkflowDefinitionStep[], transitions: WorkflowDefinitionTransition[] }

// Query hooks
useWorkflowSteps(templateId)
useWorkflowStepTransitions(templateId)
useAvailableWorkflowHandlers()

// Mutation hooks
useCreateWorkflowStep(templateId)
useUpdateWorkflowStep(templateId)
useDeleteWorkflowStep(templateId)
useUpdateWorkflowStepPosition(templateId) // debounced drag save
useCreateWorkflowStepTransition(templateId)
useUpdateWorkflowStepTransition(templateId) // can move from/to step
useDeleteWorkflowStepTransition(templateId)
useReplaceWorkflow(templateId) // atomic bulk replace via JSON definition

use-application-workflow.ts

useApplicationStatusHistory(applicationId)   // → ApplicationStatusTransition[]
useApplicationWorkflow(applicationId, enabled?) // → ApplicationWorkflowInfo | null
useAdvanceWorkflowStep(applicationId) // { applicationId, toStepId }
useTriggerWorkflowStepCheck(applicationId) // { applicationId }

Query Keys

queryKeys.templateStatuses(templateId)
queryKeys.templateStatusTransitions(templateId)
queryKeys.workflowSteps(templateId)
queryKeys.workflowStepTransitions(templateId)
queryKeys.applicationStatusHistory(applicationId)

Template Statuses Page (application-template-statuses.tsx)

Route: /o/$orgSlug/application-setup/templates/$templateId/statuses/

Layout

  • Page header: flex flex-wrap items-end justify-between gap-2 with h2 title + muted description on left, "Add Status" button on right
  • Table wrapped in overflow-x-auto rounded-md border for horizontal scroll on small screens

Status Table

Columns: drag handle, # (sort order), statusId badge, numericCode, internalName, externalName (actual value — not "(same)"), flags (Initial/Final/"manual allowed" badges), color swatch, row actions kebab menu

Drag-and-drop reordering:

  • Native HTML5 drag events: draggable, onDragStart, onDragOver, onDrop, onDragEnd
  • Local orderedStatuses state for optimistic UI; rolls back on error
  • dragFromIndex / dragOverIndex are useState (NOT useRef) — ref access during render is a lint error
  • On drop: reorders locally, calls useReorderTemplateStatuses with new ID order

Row actions (kebab menu):

  • Edit (pencil icon) — opens sheet pre-filled
  • Delete (trash icon) — rare action, moved to kebab to reduce clutter

Create/Edit Sheet (Shadcn Sheet pattern)

<SheetContent className="flex flex-col">
<SheetHeader className="text-start">...</SheetHeader>
<div className="flex-1 space-y-5 overflow-y-auto px-4 py-2">
{/* form fields */}
</div>
<SheetFooter className="gap-2">
{/* Delete (mr-auto destructive) | Cancel (SheetClose) | Save */}
</SheetFooter>
</SheetContent>
  • statusId field: editable in both create and edit mode (uniqueness enforced by backend)
  • Hint: "Used as identifier in JSON definitions"
  • useInternalAsExternal toggle hides the externalName field when on
  • Description: <Textarea rows={3}>
  • manuallySettable toggle: "Can officers manually set this status?" — displayed as "manual allowed" badge in table

Transitions Matrix

  • Shown only when statuses.length >= 2
  • style={{ minWidth: \${120 + orderedStatuses.length * 108}px` }}` — ensures proper width
  • Numbered column/row headers (#1, #2 …)
  • Each cell: cycles null → USER → SYSTEM → BOTH → null on click; colored by type
  • TRANSITION_COLORS record maps type to Tailwind classes

EMPTY ARRAY PATTERN — CRITICAL

const EMPTY_STATUSES: TemplateStatus[] = []
const { data: statuses = EMPTY_STATUSES } = useTemplateStatuses(templateId)

Never write const { data: statuses = [] } — the inline [] is a new reference every render, causing any useEffect that includes statuses in its dependency array to loop infinitely.


Workflow Builder (application-template-workflow.tsx)

Route: /o/$orgSlug/application-setup/templates/$templateId/workflow/

Layout

  • Page header: same pattern as statuses — flex flex-wrap items-end justify-between gap-2 border-b px-6 py-4 with h2 title + description on left, button group on right
  • Button group (right-aligned): [Edit as JSON] (outline, Braces icon) · [Add Transition] (outline, Plus icon, disabled when steps.length < 2 || isLocked) · [Add Step] (primary, Plus icon)
  • Minimap toggle: rendered as <Panel position="bottom-right"> inside <ReactFlow> — not in the toolbar

Canvas

  • @xyflow/react ReactFlow v12 (useNodesState<Node>, useEdgesState<Edge> — explicit type params required)
  • Node type: workflowStepWorkflowStepNode
  • proOptions={{ hideAttribution: true }}

Lock / Unlock

  • isLocked state controls nodesDraggable, nodesConnectable, elementsSelectable, and all handlers
  • <WorkflowControls> renders <Controls showInteractive={false}> + <ControlButton> for lock (amber Lock icon when locked)
  • Must be rendered inside <ReactFlow> (requires ReactFlow context)
  • When locked, WorkflowStepPanel receives readOnly={true}; onNodeClick/onConnect/onNodeDragStop are set to undefined

Node Interactions

  • Node click → opens WorkflowStepPanel (right Sheet)
  • Edge click → opens WorkflowTransitionPanel in edit mode (pre-filled with existing transition)
  • Draw connection (onConnect) → opens WorkflowTransitionPanel in create mode with initialFromStepId/initialToStepId pre-filled
  • "Add Transition" button → opens WorkflowTransitionPanel in create mode with empty selects
  • Auto-save position: handleNodeDragStop debounces 800ms → useUpdateWorkflowStepPosition

transitionMap

const transitionMap = useMemo(() => {
const m = new Map<string, WorkflowStepTransition>()
for (const t of transitions) m.set(t.id, t)
return m
}, [transitions])

Used in handleEdgeClick to look up the full transition object from the ReactFlow edge id.

Panels and Dialogs

All rendered at the bottom of the component (outside the canvas div):

<WorkflowTransitionPanel transition={selectedTransition} ... open={transitionPanelOpen} />
<WorkflowStepPanel step={selectedStep} ... open={panelOpen} readOnly={isLocked} />
<WorkflowTransitionPanel initialFromStepId={...} initialToStepId={...} ... open={newTransitionPanelOpen} />
<WorkflowJsonDialog steps={steps} transitions={transitions} ... open={jsonDialogOpen} />

Workflow Step Node (workflow-step-node.tsx)

Custom ReactFlow node:

  • Blue border = MANUAL, Purple border = SYSTEM
  • Green dot (top-left) = initial status, Red dot (top-right) = final status
  • Shows: step name, stepType badge, linked status pill (colored by templateStatus.color)
  • Handle for source (right) and target (left)

Workflow Step Panel (workflow-step-panel.tsx)

Right-side Sheet for configuring a step. Uses the standard Sheet pattern:

<SheetContent className="flex flex-col overflow-x-hidden">
<SheetHeader className="text-start">...</SheetHeader>
<div className="flex-1 space-y-5 overflow-y-auto px-4 py-2">
{/* Step Name, Step ID, Description, Step Type, System Handler, Linked Status */}
</div>
<SheetFooter className="gap-2">
{/* Delete (mr-auto) | Cancel (SheetClose) | Save */}
</SheetFooter>
</SheetContent>

overflow-x-hidden on SheetContent is required — long Select option text can overflow the sheet width.

Fields

FieldNotes
Step NameInput, required
Step IDInput, editable slug (e.g. "initial-review"), auto-sanitised to lowercase-kebab-case on input, hint: "Used as identifier in JSON definitions and transitions."
DescriptionTextarea rows={3}
Step TypeSelect MANUAL / SYSTEM
System HandlerSelect, shown only when stepType === 'SYSTEM'
Linked StatusSelect with '__none__' sentinel — Radix forbids value=""

Effect Dependency

const stepId = step?.id   // use the UUID, not the full object
useEffect(() => {
if (!step) return
setStepSlug(step.stepId ?? '')
setName(step.name)
// ...
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [stepId])

Workflow Transition Panel (workflow-transition-panel.tsx)

Handles both create and edit mode in a single Sheet component:

type Props = {
transition?: WorkflowStepTransition | null // present → edit mode
initialFromStepId?: string // pre-fill for draw/button create
initialToStepId?: string
steps: WorkflowStep[]
templateId: string
open: boolean
onClose: () => void
}
  • isEditMode = !!transition
  • useEffect keyed on open to repopulate form for both modes
  • "To" step select filters out the selected "From" step
  • Edit mode: Delete button left of footer (destructive, mr-auto)
  • disabled={!fromStepId || !toStepId || fromStepId === toStepId || isPending}

Workflow JSON Editor (workflow-json-dialog.tsx)

"Edit as JSON" button opens a wide Dialog (max-w-3xl) for bulk editing the entire workflow.

JSON Format

{
"steps": [
{
"stepId": "initial-review",
"name": "Initial Review",
"description": null,
"stepType": "MANUAL",
"systemHandler": null,
"statusId": "pending",
"positionX": 100,
"positionY": 200,
"sortOrder": 0
}
],
"transitions": [
{
"fromStepId": "initial-review",
"toStepId": "document-collection",
"transitionType": "USER",
"label": null
}
]
}

No UUIDs. Both steps and transitions use the stepId slug as identifier. statusId is also a slug.

Save Rules (backend replaceWorkflow)

  • stepId matches existing step → update that step
  • stepId not found → create new step with that slug
  • Step absent from list → delete (cascades its transitions)
  • All existing transitions are deleted and recreated from the transitions array
  • fromStepId/toStepId must match a stepId in the steps array of the same payload

Frontend Dialog Behaviour

  • buildDefinition(steps, transitions) called on open — falls back to UUID if stepId is null (legacy steps)
  • "Format JSON" button re-pretty-prints
  • Inline error displayed below textarea on invalid JSON or missing arrays
  • On save: JSON.parse → validate shape → replaceWorkflow.mutateAsync → invalidates both query keys

stepId Slug on WorkflowStep

WorkflowStep.stepId is a human-readable slug unique per template (e.g. "initial-review").

ScenarioBehaviour
createStep with stepId providedUse as-is; error if duplicate within template
createStep without stepIdAuto-generated from name via toSlug() + numeric suffix for conflicts
updateStep with new stepIdUniqueness check excluding self; error if duplicate
Existing step with null stepId (pre-feature)Lazy backfill: findStepsByTemplateId auto-generates and saves slugs on first call

toSlug(name): lowercase, strip non-[a-z0-9 -], trim, spaces→hyphens, collapse --.


manuallySettable on TemplateStatus

TemplateStatus.manuallySettable: boolean = true (default true).

  • true — officers can manually set the application to this status
  • false — status is set only by the workflow engine (system-only)
  • Displayed as "manual allowed" badge in the flags column of the status table (shown only when true)
  • Toggle in create/edit sheet: "Can officers manually set this status?"

statusId Slug on TemplateStatus

statusId is editable in both create and edit mode. The backend enforces uniqueness per template (excluding self on update). No side effects from renaming: the workflow engine uses UUID + copies name strings internally.


Backend: replaceWorkflow Mutation

replaceWorkflow(templateId: ID!, input: WorkflowDefinitionInput!): WorkflowDefinition!

Atomic @Transactional service method in WorkflowService.replaceWorkflow():

  1. Load existing steps → build slug → WorkflowStep map
  2. Delete all existing transitions
  3. For each step in input: matched by stepId slug → update; unknown slug → create
  4. Delete steps not present in input
  5. Recreate transitions, resolving fromStepId/toStepId slugs from the saved step map
  6. statusId slug resolved via TemplateStatusRepository.findByApplicationTemplateIdAndStatusId()
  7. Returns fresh WorkflowDefinitionResult (reloaded from DB)

Backend: reorderTemplateStatuses Mutation

reorderTemplateStatuses(templateId: ID!, statusIds: [ID!]!): [TemplateStatus!]!

Assigns sortOrder = index for each status in the provided order. Frontend sends optimistic reorder and rolls back on error.


GraphQL Mutations Reference

# Status CRUD
createTemplateStatus(templateId: ID!, input: TemplateStatusInput!): TemplateStatus!
updateTemplateStatus(id: ID!, input: TemplateStatusInput!): TemplateStatus!
deleteTemplateStatus(id: ID!): Boolean!
reorderTemplateStatuses(templateId: ID!, statusIds: [ID!]!): [TemplateStatus!]!
createTemplateStatusTransition(templateId: ID!, input: TemplateStatusTransitionInput!): TemplateStatusTransition!
deleteTemplateStatusTransition(id: ID!): Boolean!

# Workflow step CRUD
createWorkflowStep(templateId: ID!, input: WorkflowStepInput!): WorkflowStep!
updateWorkflowStep(id: ID!, input: WorkflowStepInput!): WorkflowStep!
deleteWorkflowStep(id: ID!): Boolean!
updateWorkflowStepPosition(id: ID!, x: Float!, y: Float!): WorkflowStep!

# Workflow transition CRUD
createWorkflowStepTransition(input: WorkflowStepTransitionInput!): WorkflowStepTransition!
updateWorkflowStepTransition(id: ID!, input: WorkflowStepTransitionInput!): WorkflowStepTransition!
deleteWorkflowStepTransition(id: ID!): Boolean!

# Bulk JSON replace
replaceWorkflow(templateId: ID!, input: WorkflowDefinitionInput!): WorkflowDefinition!

# Application workflow advancement (in ApplicationController)
advanceWorkflowStep(applicationId: ID!, toStepId: ID!): Application!
triggerWorkflowStepCheck(applicationId: ID!): WorkflowCheckResult!

Application Detail Workflow Display

Added to src/features/applications/detail/overview/index.tsx:

  • Workflow card hidden when applicationTemplateId is null
  • Two tabs: Flow (read-only ReactFlow) and History (transition table)

Flow Tab (application-workflow-flow.tsx)

Visual states:

  • Past: greyed (opacity: 0.6), checkmark overlay — step in visitedStepIds
  • Current: indigo border + background, pulsing dot
  • Future: default styling

Action bar (when canDecide):

  • USER-type outgoing transitions: [→ Advance to X] buttons
  • SYSTEM step: [Run Check Now] button + result badge
  • Final step: completion badge, no buttons

History Tab (application-workflow-history.tsx)

ApplicationStatusTransition table (newest first): #, From, To, Triggered By, Step, Date (relative)


Backend Workflow Engine

workflowEngine.advanceByUser(applicationId, toStepId, currentUserId)
workflowEngine.triggerSystemCheck(applicationId) // → WorkflowHandlerResult
workflowEngine.onDomainEvent(applicationId) // called by WorkflowEventListener

advanceByUser throws if the transition is SYSTEM-only.

System Handlers

Implement WorkflowStepHandler + @Component → auto-registers via WorkflowHandlerRegistry.

Current: document-completeness-check — all required doc slots have ≥1 APPROVED document.

Event Flow

reviewDocument() → ApplicationDocumentStatusChangedEvent
→ WorkflowEventListener → workflowEngine.onDomainEvent()
→ handler.execute() → if satisfied → enterStep(nextStepId, "SYSTEM")

Known Patterns & Pitfalls

1. Empty array defaults cause infinite loops

// ❌ WRONG — new [] reference every render
const { data: steps = [] } = useWorkflowSteps(templateId)

// ✅ CORRECT — stable module-level constant
const EMPTY_STEPS: WorkflowStep[] = []
const { data: steps = EMPTY_STEPS } = useWorkflowSteps(templateId)

Required for all hooks whose data feeds into a useEffect dependency array.

2. useNodesState/useEdgesState require explicit type params

const [nodes, setNodes, onNodesChange] = useNodesState<Node>([])
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([])

3. Radix SelectItem forbids empty string value

<SelectItem value="__none__">None</SelectItem>
// convert: templateStatusId === '__none__' ? null : templateStatusId

4. Lucide Map shadows built-in Map

import { Map as MapIcon, Plus } from 'lucide-react'

5. WorkflowControls must be inside <ReactFlow>

<Controls> and <ControlButton> require the ReactFlow context.

6. WorkflowStepPanel useEffect dependency

Use step?.id (UUID), not the full step object:

const stepId = step?.id
useEffect(() => { ... }, [stepId])

7. Drag state must be useState, not useRef

// ❌ ref access during render — lint error
const dragFromIndex = useRef<number | null>(null)
// ... className={dragFromIndex.current === i ? 'opacity-50' : ''}

// ✅ state
const [dragFromIndex, setDragFromIndex] = useState<number | null>(null)

8. Sheet overflow-x-hidden for step panel

Without overflow-x-hidden on SheetContent, long Select option text (e.g. status names) causes a horizontal scrollbar inside the sheet.

9. MapStruct stale mapper during mvn spring-boot:run

pom.xml has <useIncrementalCompilation>false</useIncrementalCompilation> in the maven-compiler-plugin config. This forces full recompile and ensures MapStruct always regenerates *MapperImpl classes. Do not remove it.

10. stepId lazy backfill

WorkflowService.findStepsByTemplateId() is @Transactional and auto-assigns stepId slugs to any step that doesn't have one. This is a one-time migration on first load after the feature was added.


Translations

All keys in src/lib/i18n/translations/{en,de,ja}/applicationTemplates.ts.

Key groups:

applicationTemplates.sidebar.statuses / workflow

applicationTemplates.statuses.title / description / addStatus / noStatuses / initial / final
applicationTemplates.statuses.columns.statusId / numericCode / internalName / externalName / flags / color
applicationTemplates.statuses.sheet.createTitle / editTitle / statusId / statusIdHint / numericCode /
internalName / sameAsInternal / externalName / description / color / isInitial / isFinal / manuallySettable

applicationTemplates.statuses.transitions.title / description

applicationTemplates.workflow.title / description
applicationTemplates.workflow.addStep / editJson / minimap / noSteps
applicationTemplates.workflow.step.create.success/error
applicationTemplates.workflow.step.update.success/error
applicationTemplates.workflow.step.delete.success/error
applicationTemplates.workflow.transition.add
applicationTemplates.workflow.transition.create.success/error
applicationTemplates.workflow.transition.update.success/error
applicationTemplates.workflow.transition.delete.success/error
applicationTemplates.workflow.replace.success/error
applicationTemplates.workflow.stepType.manual / system
applicationTemplates.workflow.panel.title / stepName / stepId / description / stepType /
systemHandler / linkedStatus / delete / save

Registered in src/components/layout/data/sidebar-data.tsx via getApplicationTemplateDetailNavItems():

  • Statuses — icon: Tag, route: .../statuses/
  • Workflow — icon: GitBranch, route: .../workflow/