From f933fbecf9f9b1d07bc132f8433b2239b450e1ab Mon Sep 17 00:00:00 2001 From: Frank Midigo Date: Mon, 17 Nov 2025 12:07:09 +0300 Subject: [PATCH 1/4] Add workflow template publishing feature to collaborative editor Implement UI and backend integration for publishing workflows as templates and updating existing templates. This feature is restricted to superusers and requires workflows to be saved before publishing. Frontend changes: - Add TemplatePublishPanel component with form for name, description, and tags - Add TextAreaField form component with tag management UI - Add publish/update template button to CodeViewPanel - Integrate workflow_template state into SessionContextStore - Add template_updated channel listener - Add useWorkflowTemplate hook for accessing template data - Show button state based on unsaved changes and user permissions Backend integration: - Connect to publish_template channel request - Send YAML workflow code and positions to backend - Handle template_updated messages from server - Support both create and update operations Test updates: - Add workflow_template field to session context mocks - Mock useUser, useWorkflowTemplate, and useURLState in CodeViewPanel tests - Update all session context factory functions with template support --- CHANGELOG.md | 3 + .../components/WorkflowEditor.tsx | 1 + .../components/form/index.tsx | 2 + .../components/form/text-field.tsx | 3 + .../components/form/textarea-field.tsx | 30 ++ .../components/inspector/CodeViewPanel.tsx | 48 +++ .../inspector/TemplatePublishPanel.tsx | 254 ++++++++++++++ .../components/inspector/index.tsx | 26 +- .../hooks/useSessionContext.ts | 18 + .../stores/createSessionContextStore.ts | 28 ++ .../types/sessionContext.ts | 21 ++ .../__helpers__/sessionContextFactory.ts | 9 + .../inspector/CodeViewPanel.test.tsx | 23 ++ .../contexts/StoreProvider.test.tsx | 1 + .../hooks/useJobDeleteValidation.test.tsx | 1 + .../hooks/useSessionContext.test.tsx | 8 + .../types/sessionContext.test.ts | 26 ++ .../channels/workflow_channel.ex | 68 +++- .../channels/workflow_channel_test.exs | 319 ++++++++++++++++++ 19 files changed, 885 insertions(+), 4 deletions(-) create mode 100644 assets/js/collaborative-editor/components/form/textarea-field.tsx create mode 100644 assets/js/collaborative-editor/components/inspector/TemplatePublishPanel.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index c1918de03e..745e8294ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ and this project adheres to ### Added +- Add workflow template publishing to collaborative editor + [#3921](https://github.com/OpenFn/lightning/issues/3921) + ### Changed - Enable usage limit checks across the whole application diff --git a/assets/js/collaborative-editor/components/WorkflowEditor.tsx b/assets/js/collaborative-editor/components/WorkflowEditor.tsx index a43d17bd6b..9a6b1967ac 100644 --- a/assets/js/collaborative-editor/components/WorkflowEditor.tsx +++ b/assets/js/collaborative-editor/components/WorkflowEditor.tsx @@ -203,6 +203,7 @@ export function WorkflowEditor({ const showInspector = searchParams.get('panel') === 'settings' || searchParams.get('panel') === 'code' || + searchParams.get('panel') === 'publish-template' || Boolean(currentNode.node); const handleMethodChange = (method: 'template' | 'import' | 'ai' | null) => { diff --git a/assets/js/collaborative-editor/components/form/index.tsx b/assets/js/collaborative-editor/components/form/index.tsx index fb0f1506d1..b60a7915ee 100644 --- a/assets/js/collaborative-editor/components/form/index.tsx +++ b/assets/js/collaborative-editor/components/form/index.tsx @@ -6,6 +6,7 @@ import { useValidation } from '#/collaborative-editor/hooks/useValidation'; import { NumberField } from './number-field'; import { SelectField } from './select-field'; import { TextField } from './text-field'; +import { TextAreaField } from './textarea-field'; import { ToggleField } from './toggle-field'; export const { fieldContext, formContext, useFieldContext } = @@ -17,6 +18,7 @@ const { useAppForm: useBaseAppForm } = createFormHook({ formContext, fieldComponents: { TextField, + TextAreaField, SelectField, ToggleField, NumberField, diff --git a/assets/js/collaborative-editor/components/form/text-field.tsx b/assets/js/collaborative-editor/components/form/text-field.tsx index bbd4b5b5ce..6afbf49db2 100644 --- a/assets/js/collaborative-editor/components/form/text-field.tsx +++ b/assets/js/collaborative-editor/components/form/text-field.tsx @@ -5,9 +5,11 @@ import { useFieldContext } from '.'; export function TextField({ label, disabled = false, + placeholder, }: { label: string; disabled?: boolean; + placeholder?: string; }) { const field = useFieldContext(); return ( @@ -18,6 +20,7 @@ export function TextField({ value={field.state.value || ''} onChange={e => field.handleChange(e.target.value)} disabled={disabled} + placeholder={placeholder} className={INPUT_CLASSES} /> diff --git a/assets/js/collaborative-editor/components/form/textarea-field.tsx b/assets/js/collaborative-editor/components/form/textarea-field.tsx new file mode 100644 index 0000000000..6b2a019726 --- /dev/null +++ b/assets/js/collaborative-editor/components/form/textarea-field.tsx @@ -0,0 +1,30 @@ +import { FormField, INPUT_CLASSES } from './form-field'; + +import { useFieldContext } from '.'; + +export function TextAreaField({ + label, + disabled = false, + placeholder, + rows = 4, +}: { + label: string; + disabled?: boolean; + placeholder?: string; + rows?: number; +}) { + const field = useFieldContext(); + return ( + +