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: workflowStep → WorkflowStepNode
  • 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/