Parent epic: #4848 AI-First Starting UX
Depends on: #4856
What this issue builds
Three concrete pieces:
- A portable template modal component — accepts a templates array as a prop and an
onSelect callback; knows nothing about workflow creation logic
- Template data wiring — fetches templates (base + user-created) via the existing Phoenix
Channel and passes them into the modal component
- Workflow creation on card click — selecting a template applies it to the canvas and
saves the workflow to the DB, ending pink toolbar state
What is not changing: the template data source (list_templates channel message +
WorkflowTemplates.list_templates/0), the importWorkflow function in createWorkflowStore.ts,
the validate_workflow_name channel message, or the template YAML content itself.
TBC: The designs for the modal may change
User flow
- User clicks "Browse templates" on the landing screen
- A modal opens: "Browse Workflow Templates" — search bar, flat list of template cards
- User can search/filter the list
- Clicking a card:
- Modal closes
- Canvas is populated with the template's structure
- Workflow is saved to the DB (first DB write, same as AI path auto-save)
- Pink toolbar ends: URL updates to
/projects/{id}/w/{workflow_id}, Save becomes enabled
- Workflow is named after the template with uniqueness enforced (existing behaviour)
- Closing the modal via the X button returns the user to the landing screen — no navigation
- Pressing browser back while the modal is open navigates away from
/new entirely (standard
modal behaviour — the modal does not push a history entry)
After creation, pressing browser back goes to wherever the user came from (workflow list), not
back to /new. This is consistent with the AI path: replaceState swaps /new → /w/{id}
without adding a history entry.
Implementation
Component design: portability first
The modal component should be a pure presentational component — it receives data as props
and fires callbacks. It has no knowledge of channels, workflow state, or creation logic.
interface WorkflowTemplateBrowserProps {
templates: TemplateItem[]
onSelect: (template: TemplateItem) => void
onClose: () => void
}
Where TemplateItem is a minimal type:
interface TemplateItem {
id: string
name: string
description?: string
code: string // raw YAML string
}
This matches the shape already returned by fetchTemplates in
assets/js/collaborative-editor/api/templates.ts (which calls 'list_templates' over the
channel) and the BaseTemplate type in
assets/js/collaborative-editor/constants/baseTemplates.ts.
Do not pass workflow store references, save functions, or creation callbacks into this
component. The onSelect callback receives the raw template — the parent handles everything
else. This is the portability contract: the component can be dropped anywhere and only needs
a templates array and two callbacks.
Template data source (existing, reused)
Templates are already fetched via the Phoenix Channel:
fetchTemplates in assets/js/collaborative-editor/api/templates.ts sends
'list_templates' over the channel
- The channel handler in
lib/lightning_web/channels/workflow_channel.ex:608 calls
Lightning.WorkflowTemplates.list_templates/0 and returns rendered templates
- Base templates (webhook + cron) live in
assets/js/collaborative-editor/constants/baseTemplates.ts
TemplatePanel.tsx:36-277 shows the existing merge pattern: base templates are prepended
to user templates before rendering
The parent of the new modal should fetch templates on mount using the same fetchTemplates
call and merge them with the base templates in the same order. No new channel messages or
backend changes are needed.
Workflow creation on card select
When onSelect fires with a template, the parent component should:
- Call
parseWorkflowYAML(template.code) (from assets/js/yaml/util.ts:291) to parse YAML
into a WorkflowSpec
- Call
convertWorkflowSpecToState(spec) (assets/js/yaml/util.ts:157) to convert to a
YAMLWorkflowState
- Call
workflowStore.importWorkflow(workflowState) — this already:
- Sends
'validate_workflow_name' over the channel (workflow_channel.ex:451) to enforce
uniqueness (appending " 1", " 2" etc. if needed)
- Calls
YAMLStateToYDoc.applyToYDoc(ydoc, validatedState) to write into Y.Doc
- Fires
flowEvents.dispatch('fit-view') to frame the canvas
- After
importWorkflow succeeds, call saveWorkflow({ silent: true }) — same auto-save
pattern as the AI path (Issue 2, hooks/useAIWorkflowApplications.ts:171-228)
This is the same sequence the existing TemplatePanel.tsx uses for steps 1-3, and the same
auto-save pattern Issue 2 adds for step 4. No new logic is needed — only the wiring.
Naming behaviour
The workflow name comes from the YAML name: field in the template's code. The
validate_workflow_name channel message enforces uniqueness within the project (existing
behaviour). The modal component does not touch naming — it passes the raw template to
onSelect and the existing importWorkflow + channel validation handles the rest.
Do not add any rename event listeners, name-update callbacks, or name-watching logic to the
modal component. The name should not be connected to the modal in any observable way.
Modal open/close state
The modal is controlled by a boolean state variable in the landing screen component (Issue 1).
- "Browse templates" card click → sets
isTemplateBrowserOpen = true
- X button in modal → sets
isTemplateBrowserOpen = false (returns to landing screen)
- Card click → triggers
onSelect → after workflow creation, navigates to canvas (landing
screen is no longer rendered)
The modal does not call pushState or add a history entry. Pressing browser back while
the modal is open will navigate away from /new by natural browser behaviour — this is
intentional and consistent with standard modal behaviour anywhere else in the app.
No canvas preview on hover
The existing TemplatePanel previews templates on the canvas as the user browses. This
behaviour is not being built. Clicking a card is the only action. Do not wire up
uiStore.selectTemplate, TemplateDetailsCard, or any preview mechanism.
Files to modify / create
| File |
Change |
New: components/WorkflowTemplateBrowser.tsx |
Portable modal component — accepts templates, onSelect, onClose props |
assets/js/collaborative-editor/api/templates.ts |
No change — reuse fetchTemplates as-is |
assets/js/collaborative-editor/constants/baseTemplates.ts |
No change — reuse base templates as-is |
| Landing screen component (Issue 1) |
Fetch templates on mount; control modal open state; wire onSelect to importWorkflow + saveWorkflow |
assets/js/yaml/util.ts |
No change — reuse parseWorkflowYAML (line 291) and convertWorkflowSpecToState (line 157) |
Note: TemplatePanel.tsx, TemplateCard.tsx, TemplateSearchInput.tsx, and
TemplateDetailsCard.tsx are not modified. The new component is written fresh and is
intentionally not a fork of TemplatePanel.tsx — that component carries canvas preview
logic, selection state, and left-panel-specific concerns that do not belong here.
Out of scope
- Template category filters (Event-based, Scheduled) — flat list only; categories may come later
- Canvas preview on hover
- "Build with AI" fallback from the search bar (present in
TemplateSearchInput.tsx — not
replicated here)
- Any changes to the template data model or backend
TemplatePanel.tsx and the existing left-panel browsing flow — untouched
Acceptance Criteria
Modal opens from landing screen
Search
Card selection creates the workflow
Closing the modal
Back navigation
Portability
Parent epic: #4848 AI-First Starting UX
Depends on: #4856
What this issue builds
Three concrete pieces:
onSelectcallback; knows nothing about workflow creation logicChannel and passes them into the modal component
saves the workflow to the DB, ending pink toolbar state
What is not changing: the template data source (
list_templateschannel message +WorkflowTemplates.list_templates/0), theimportWorkflowfunction increateWorkflowStore.ts,the
validate_workflow_namechannel message, or the template YAML content itself.TBC: The designs for the modal may change
User flow
/projects/{id}/w/{workflow_id}, Save becomes enabled/newentirely (standardmodal behaviour — the modal does not push a history entry)
After creation, pressing browser back goes to wherever the user came from (workflow list), not
back to
/new. This is consistent with the AI path:replaceStateswaps/new→/w/{id}without adding a history entry.
Implementation
Component design: portability first
The modal component should be a pure presentational component — it receives data as props
and fires callbacks. It has no knowledge of channels, workflow state, or creation logic.
Where
TemplateItemis a minimal type:This matches the shape already returned by
fetchTemplatesinassets/js/collaborative-editor/api/templates.ts(which calls'list_templates'over thechannel) and the
BaseTemplatetype inassets/js/collaborative-editor/constants/baseTemplates.ts.Do not pass workflow store references, save functions, or creation callbacks into this
component. The
onSelectcallback receives the raw template — the parent handles everythingelse. This is the portability contract: the component can be dropped anywhere and only needs
a templates array and two callbacks.
Template data source (existing, reused)
Templates are already fetched via the Phoenix Channel:
fetchTemplatesinassets/js/collaborative-editor/api/templates.tssends'list_templates'over the channellib/lightning_web/channels/workflow_channel.ex:608callsLightning.WorkflowTemplates.list_templates/0and returns rendered templatesassets/js/collaborative-editor/constants/baseTemplates.tsTemplatePanel.tsx:36-277shows the existing merge pattern: base templates are prependedto user templates before rendering
The parent of the new modal should fetch templates on mount using the same
fetchTemplatescall and merge them with the base templates in the same order. No new channel messages or
backend changes are needed.
Workflow creation on card select
When
onSelectfires with a template, the parent component should:parseWorkflowYAML(template.code)(fromassets/js/yaml/util.ts:291) to parse YAMLinto a
WorkflowSpecconvertWorkflowSpecToState(spec)(assets/js/yaml/util.ts:157) to convert to aYAMLWorkflowStateworkflowStore.importWorkflow(workflowState)— this already:'validate_workflow_name'over the channel (workflow_channel.ex:451) to enforceuniqueness (appending " 1", " 2" etc. if needed)
YAMLStateToYDoc.applyToYDoc(ydoc, validatedState)to write into Y.DocflowEvents.dispatch('fit-view')to frame the canvasimportWorkflowsucceeds, callsaveWorkflow({ silent: true })— same auto-savepattern as the AI path (Issue 2,
hooks/useAIWorkflowApplications.ts:171-228)This is the same sequence the existing
TemplatePanel.tsxuses for steps 1-3, and the sameauto-save pattern Issue 2 adds for step 4. No new logic is needed — only the wiring.
Naming behaviour
The workflow name comes from the YAML
name:field in the template'scode. Thevalidate_workflow_namechannel message enforces uniqueness within the project (existingbehaviour). The modal component does not touch naming — it passes the raw template to
onSelectand the existingimportWorkflow+ channel validation handles the rest.Do not add any rename event listeners, name-update callbacks, or name-watching logic to the
modal component. The name should not be connected to the modal in any observable way.
Modal open/close state
The modal is controlled by a boolean state variable in the landing screen component (Issue 1).
isTemplateBrowserOpen = trueisTemplateBrowserOpen = false(returns to landing screen)onSelect→ after workflow creation, navigates to canvas (landingscreen is no longer rendered)
The modal does not call
pushStateor add a history entry. Pressing browser back whilethe modal is open will navigate away from
/newby natural browser behaviour — this isintentional and consistent with standard modal behaviour anywhere else in the app.
No canvas preview on hover
The existing
TemplatePanelpreviews templates on the canvas as the user browses. Thisbehaviour is not being built. Clicking a card is the only action. Do not wire up
uiStore.selectTemplate,TemplateDetailsCard, or any preview mechanism.Files to modify / create
components/WorkflowTemplateBrowser.tsxtemplates,onSelect,onClosepropsassets/js/collaborative-editor/api/templates.tsfetchTemplatesas-isassets/js/collaborative-editor/constants/baseTemplates.tsonSelecttoimportWorkflow+saveWorkflowassets/js/yaml/util.tsparseWorkflowYAML(line 291) andconvertWorkflowSpecToState(line 157)Note:
TemplatePanel.tsx,TemplateCard.tsx,TemplateSearchInput.tsx, andTemplateDetailsCard.tsxare not modified. The new component is written fresh and isintentionally not a fork of
TemplatePanel.tsx— that component carries canvas previewlogic, selection state, and left-panel-specific concerns that do not belong here.
Out of scope
TemplateSearchInput.tsx— notreplicated here)
TemplatePanel.tsxand the existing left-panel browsing flow — untouchedAcceptance Criteria
Modal opens from landing screen
/new, Savedisabled, no DB record)
user-created templates, same set as existing template browser)
Search
Card selection creates the workflow
/projects/{id}/w/{workflow_id}without a page reloadworkflow 1" if the name already exists in the project)
Closing the modal
to the landing screen
Back navigation
/newentirely —consistent with standard modal behaviour
(e.g. workflow list) — not back to
/newor the modalPortability
onSelectandonCloseare the only callbacks — no other workflow-state coupling