From 492aca09ff399dc1648797a358c8f15665cb2977 Mon Sep 17 00:00:00 2001 From: Charlie Kolb Date: Fri, 5 Dec 2025 12:20:27 +0100 Subject: [PATCH] feat(editor): Move workflow description edit button to settings (#22301) --- .../frontend/@n8n/i18n/src/locales/en.json | 10 +- .../MainHeader/ActionsDropdownMenu.vue | 20 + .../WorkflowDescriptionPopover.test.ts | 638 ------------------ .../MainHeader/WorkflowDescriptionPopover.vue | 239 ------- .../components/MainHeader/WorkflowDetails.vue | 7 - .../editor-ui/src/app/components/Modals.vue | 8 + .../WorkflowDescriptionModal.test.ts | 490 ++++++++++++++ .../components/WorkflowDescriptionModal.vue | 168 +++++ .../editor-ui/src/app/constants/actions.ts | 1 + .../editor-ui/src/app/constants/modals.ts | 1 + .../editor-ui/src/app/stores/ui.store.ts | 2 + .../src/app/stores/workflows.store.ts | 14 +- 12 files changed, 703 insertions(+), 895 deletions(-) delete mode 100644 packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDescriptionPopover.test.ts delete mode 100644 packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDescriptionPopover.vue create mode 100644 packages/frontend/editor-ui/src/app/components/WorkflowDescriptionModal.test.ts create mode 100644 packages/frontend/editor-ui/src/app/components/WorkflowDescriptionModal.vue diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json index cbffe12e6e1..c8762b583eb 100644 --- a/packages/frontend/@n8n/i18n/src/locales/en.json +++ b/packages/frontend/@n8n/i18n/src/locales/en.json @@ -1176,9 +1176,8 @@ "folder.delete.modal.confirmation": "What should we do with {folders} {workflows} in this folder?", "folder.count": "the {count} folder | the {count} folders", "workflow.count": "the {count} workflow | the {count} workflows", - "workflow.description.tooltip": "Edit workflow description", - "workflow.description.placeholder": "Describe the purpose and functionality of this workflow", - "workflow.description.placeholder.mcp": "To help MCP clients understand when to use this workflow, add a short workflow description that describes what it does.", + "workflow.description.mcp": "Clear descriptions help other users and MCP clients understand the purpose of your workflow", + "workflow.description.nomcp": "Clear descriptions help other users understand the purpose of your workflow", "workflow.description.error.title": "Problem updating workflow description", "folder.and.workflow.separator": "and", "folders.delete.action": "Archive all workflows and delete subfolders", @@ -1312,9 +1311,10 @@ "mcp.workflowDeactivated.message": "MCP Access has been disabled for this workflow because it is deactivated", "menuActions.duplicate": "Duplicate", "menuActions.download": "Download", - "menuActions.push": "Push to Git", + "menuActions.push": "Push to git", + "menuActions.editDescription": "Edit description", "menuActions.importFromUrl": "Import from URL...", - "menuActions.importFromFile": "Import from File...", + "menuActions.importFromFile": "Import from file...", "menuActions.delete": "Delete", "menuActions.archive": "Archive", "menuActions.unarchive": "Unarchive", diff --git a/packages/frontend/editor-ui/src/app/components/MainHeader/ActionsDropdownMenu.vue b/packages/frontend/editor-ui/src/app/components/MainHeader/ActionsDropdownMenu.vue index 327f2f46cbe..1bfe41a8690 100644 --- a/packages/frontend/editor-ui/src/app/components/MainHeader/ActionsDropdownMenu.vue +++ b/packages/frontend/editor-ui/src/app/components/MainHeader/ActionsDropdownMenu.vue @@ -15,6 +15,7 @@ import { IS_DRAFT_PUBLISH_ENABLED, WORKFLOW_SHARE_MODAL_KEY, EnterpriseEditionFeature, + WORKFLOW_DESCRIPTION_MODAL_KEY, } from '@/app/constants'; import { hasPermission } from '@/app/utils/rbac/permissions'; import { useRoute } from 'vue-router'; @@ -163,6 +164,11 @@ const workflowMenuItems = computed { switch (action) { + case WORKFLOW_MENU_ACTIONS.EDIT_DESCRIPTION: { + const workflowId = getWorkflowId(props.id, route.params.name); + if (!workflowId) return; + + const workflowDescription = workflowsStore.getWorkflowById(workflowId).description; + uiStore.openModalWithData({ + name: WORKFLOW_DESCRIPTION_MODAL_KEY, + data: { + workflowId, + workflowDescription, + }, + }); + break; + } case WORKFLOW_MENU_ACTIONS.DUPLICATE: { uiStore.openModalWithData({ name: DUPLICATE_MODAL_KEY, diff --git a/packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDescriptionPopover.test.ts b/packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDescriptionPopover.test.ts deleted file mode 100644 index 8a690211772..00000000000 --- a/packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDescriptionPopover.test.ts +++ /dev/null @@ -1,638 +0,0 @@ -import { createComponentRenderer } from '@/__tests__/render'; -import { type MockedStore, mockedStore } from '@/__tests__/utils'; -import { createTestingPinia } from '@pinia/testing'; -import userEvent from '@testing-library/user-event'; -import { nextTick } from 'vue'; -import WorkflowDescriptionPopover from '@/app/components/MainHeader/WorkflowDescriptionPopover.vue'; -import { useWorkflowsStore } from '@/app/stores/workflows.store'; -import { useUIStore } from '@/app/stores/ui.store'; -import { useSettingsStore } from '@/app/stores/settings.store'; -import { useToast } from '@/app/composables/useToast'; -import { useTelemetry } from '@/app/composables/useTelemetry'; -import { STORES } from '@n8n/stores'; - -vi.mock('@/app/composables/useToast', () => { - const showError = vi.fn(); - return { - useToast: () => ({ - showError, - }), - }; -}); - -vi.mock('@/app/composables/useTelemetry', () => { - const track = vi.fn(); - return { - useTelemetry: () => ({ - track, - }), - }; -}); - -const initialState = { - [STORES.SETTINGS]: { - settings: { - modules: { - mcp: { - enabled: false, - }, - }, - }, - }, -}; - -const renderComponent = createComponentRenderer(WorkflowDescriptionPopover, { - pinia: createTestingPinia({ initialState }), -}); - -describe('WorkflowDescriptionPopover', () => { - let workflowsStore: MockedStore; - let uiStore: MockedStore; - let settingsStore: MockedStore; - let telemetry: ReturnType; - let toast: ReturnType; - - beforeEach(() => { - workflowsStore = mockedStore(useWorkflowsStore); - uiStore = mockedStore(useUIStore); - settingsStore = mockedStore(useSettingsStore); - telemetry = useTelemetry(); - toast = useToast(); - - // Reset mocks - workflowsStore.saveWorkflowDescription = vi.fn().mockResolvedValue(undefined); - workflowsStore.workflow = { - id: 'test-workflow-id', - name: 'Test Workflow', - active: false, - activeVersionId: null, - isArchived: false, - createdAt: Date.now(), - updatedAt: Date.now(), - versionId: '1', - nodes: [], - connections: {}, - }; - uiStore.stateIsDirty = false; - }); - - afterEach(() => { - vi.clearAllMocks(); - }); - - describe('Component rendering', () => { - it('should render the description button and default description', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - const button = getByTestId('workflow-description-button'); - await userEvent.click(button); - - const textarea = getByTestId('workflow-description-input'); - expect(textarea).toHaveValue('Initial description'); - }); - - it('should render empty string if there is no description', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - }, - }); - - const button = getByTestId('workflow-description-button'); - await userEvent.click(button); - - const textarea = getByTestId('workflow-description-input'); - expect(textarea).toHaveValue(''); - }); - }); - - describe('Popover interaction', () => { - it('should open popover when button is clicked', async () => { - const { getByTestId, queryByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Test description', - }, - }); - - const button = getByTestId('workflow-description-button'); - expect(queryByTestId('workflow-description-edit-content')).not.toBeInTheDocument(); - - await userEvent.click(button); - expect(getByTestId('workflow-description-edit-content')).toBeInTheDocument(); - }); - - it('should focus textarea when popover opens', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - }, - }); - - const button = getByTestId('workflow-description-button'); - await userEvent.click(button); - await nextTick(); - - const textarea = getByTestId('workflow-description-input'); - expect(textarea).toHaveFocus(); - }); - - it('should save description when popover closes', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - const button = getByTestId('workflow-description-button'); - await userEvent.click(button); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.clear(textarea); - await userEvent.type(textarea, 'Updated description'); - - // Click outside to close popover - await userEvent.click(document.body); - - expect(workflowsStore.saveWorkflowDescription).toHaveBeenCalledWith( - 'test-workflow-id', - 'Updated description', - ); - }); - }); - - describe('Save and Cancel functionality', () => { - it('should save description when save button is clicked', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.clear(textarea); - await userEvent.type(textarea, 'New description'); - - const saveButton = getByTestId('workflow-description-save-button'); - await userEvent.click(saveButton); - - expect(workflowsStore.saveWorkflowDescription).toHaveBeenCalledWith( - 'test-workflow-id', - 'New description', - ); - expect(telemetry.track).toHaveBeenCalledWith('User set workflow description', { - workflow_id: 'test-workflow-id', - description: 'New description', - }); - }); - - it('should save empty string when description is cleared', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.clear(textarea); - - const saveButton = getByTestId('workflow-description-save-button'); - await userEvent.click(saveButton); - - expect(workflowsStore.saveWorkflowDescription).toHaveBeenCalledWith('test-workflow-id', ''); - expect(telemetry.track).toHaveBeenCalledWith('User set workflow description', { - workflow_id: 'test-workflow-id', - description: '', - }); - }); - - it('should disable save button when description has not changed', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const saveButton = getByTestId('workflow-description-save-button'); - expect(saveButton).toBeDisabled(); - }); - - it('should disable save button when whitespace-only changes result in same trimmed value', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: '', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - // Type only whitespace - await userEvent.type(textarea, ' '); - - const saveButton = getByTestId('workflow-description-save-button'); - // Should be disabled since trimmed value is still empty - expect(saveButton).toBeDisabled(); - }); - - it('should not save on Enter key when only whitespace is entered', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: '', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - // Type only whitespace - await userEvent.type(textarea, ' '); - await userEvent.keyboard('{Enter}'); - - // Should not save since canSave is false - expect(workflowsStore.saveWorkflowDescription).not.toHaveBeenCalled(); - }); - - it('should enable save button when description changes', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.type(textarea, ' updated'); - - const saveButton = getByTestId('workflow-description-save-button'); - expect(saveButton).not.toBeDisabled(); - }); - - it('should revert changes when cancel button is clicked', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.clear(textarea); - await userEvent.type(textarea, 'Changed description'); - - const cancelButton = getByTestId('workflow-description-cancel-button'); - await userEvent.click(cancelButton); - - // Re-open popover to check value - await userEvent.click(getByTestId('workflow-description-button')); - const textareaAfterCancel = getByTestId('workflow-description-input'); - expect(textareaAfterCancel).toHaveValue('Initial description'); - }); - - it('should disable cancel button during save', async () => { - workflowsStore.saveWorkflowDescription = vi.fn( - async () => await new Promise((resolve) => setTimeout(resolve, 100)), - ); - - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.type(textarea, ' updated'); - - const saveButton = getByTestId('workflow-description-save-button'); - const cancelButton = getByTestId('workflow-description-cancel-button'); - - await userEvent.click(saveButton); - - // During save, cancel should be disabled - expect(cancelButton).toBeDisabled(); - }); - }); - - describe('Keyboard shortcuts', () => { - it('should save when Enter key is pressed', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.clear(textarea); - await userEvent.type(textarea, 'New description'); - await userEvent.keyboard('{Enter}'); - - expect(workflowsStore.saveWorkflowDescription).toHaveBeenCalledWith( - 'test-workflow-id', - 'New description', - ); - }); - - it('should allow new lines with Shift+Enter', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: '', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.type(textarea, 'Line 1'); - await userEvent.keyboard('{Shift>}{Enter}{/Shift}'); - await userEvent.type(textarea, 'Line 2'); - - expect(textarea).toHaveValue('Line 1\nLine 2'); - expect(workflowsStore.saveWorkflowDescription).not.toHaveBeenCalled(); - }); - - it('should cancel when Escape key is pressed', async () => { - const { getByTestId, queryByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.clear(textarea); - await userEvent.type(textarea, 'Changed description'); - await userEvent.keyboard('{Escape}'); - - // Check that popover is closed - expect(queryByTestId('workflow-description-edit-content')).not.toBeInTheDocument(); - - // Re-open to verify changes were reverted - await userEvent.click(getByTestId('workflow-description-button')); - const textareaAfterEscape = getByTestId('workflow-description-input'); - expect(textareaAfterEscape).toHaveValue('Initial description'); - }); - }); - - describe('Error handling', () => { - it('should show error toast when save fails', async () => { - const error = new Error('Save failed'); - workflowsStore.saveWorkflowDescription = vi.fn().mockRejectedValue(error); - - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.type(textarea, ' updated'); - - const saveButton = getByTestId('workflow-description-save-button'); - await userEvent.click(saveButton); - - await vi.waitFor(() => { - expect(toast.showError).toHaveBeenCalledWith( - error, - 'Problem updating workflow description', - ); - }); - }); - - it('should revert to last saved value on error', async () => { - const error = new Error('Save failed'); - workflowsStore.saveWorkflowDescription = vi.fn().mockRejectedValue(error); - - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.clear(textarea); - await userEvent.type(textarea, 'Failed update'); - - const saveButton = getByTestId('workflow-description-save-button'); - await userEvent.click(saveButton); - - await vi.waitFor(() => { - expect(textarea).toHaveValue('Initial description'); - }); - }); - }); - - describe('Dirty state management', () => { - it('should set dirty flag when description changes', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - expect(uiStore.stateIsDirty).toBe(false); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.type(textarea, ' updated'); - - expect(uiStore.stateIsDirty).toBe(true); - }); - - it('should clear dirty flag when saving', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.type(textarea, ' updated'); - - expect(uiStore.stateIsDirty).toBe(true); - - const saveButton = getByTestId('workflow-description-save-button'); - await userEvent.click(saveButton); - - await vi.waitFor(() => { - expect(uiStore.stateIsDirty).toBe(false); - }); - }); - - it('should clear dirty flag when canceling', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.type(textarea, ' updated'); - - expect(uiStore.stateIsDirty).toBe(true); - - const cancelButton = getByTestId('workflow-description-cancel-button'); - await userEvent.click(cancelButton); - - expect(uiStore.stateIsDirty).toBe(false); - }); - - it('should handle whitespace-only changes correctly', async () => { - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: ' Initial ', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.clear(textarea); - await userEvent.type(textarea, 'Initial'); - - // Should not be dirty since trimmed values are the same - expect(uiStore.stateIsDirty).toBe(false); - }); - }); - - describe('MCP tooltips', () => { - it('should show base tooltip when MCP is disabled', async () => { - // Ensure MCP is disabled - settingsStore.isModuleActive = vi.fn().mockReturnValue(false); - settingsStore.moduleSettings.mcp = { mcpAccessEnabled: false }; - - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: '', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - // The tooltip text appears as placeholder in the textarea - const textarea = getByTestId('workflow-description-input'); - const placeholder = textarea.getAttribute('placeholder'); - - expect(placeholder).toContain('Edit workflow description'); - expect(placeholder).not.toContain('MCP clients'); - }); - - it('should show MCP tooltip when MCP is enabled', async () => { - // Enable MCP module - settingsStore.isModuleActive = vi.fn().mockReturnValue(true); - settingsStore.moduleSettings.mcp = { mcpAccessEnabled: true }; - - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: '', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - const placeholder = textarea.getAttribute('placeholder'); - - expect(placeholder).toContain('MCP clients'); - expect(placeholder).not.toContain('Edit workflow description'); - }); - }); - - describe('UI state tracking', () => { - it('should track active actions during save', async () => { - const addActiveActionSpy = vi.spyOn(uiStore, 'addActiveAction'); - const removeActiveActionSpy = vi.spyOn(uiStore, 'removeActiveAction'); - - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.type(textarea, ' updated'); - - const saveButton = getByTestId('workflow-description-save-button'); - await userEvent.click(saveButton); - - expect(addActiveActionSpy).toHaveBeenCalledWith('workflowSaving'); - - await vi.waitFor(() => { - expect(removeActiveActionSpy).toHaveBeenCalledWith('workflowSaving'); - }); - }); - - it('should remove active action even on error', async () => { - const removeActiveActionSpy = vi.spyOn(uiStore, 'removeActiveAction'); - workflowsStore.saveWorkflowDescription = vi.fn().mockRejectedValue(new Error('Failed')); - - const { getByTestId } = renderComponent({ - props: { - workflowId: 'test-workflow-id', - workflowDescription: 'Initial description', - }, - }); - - await userEvent.click(getByTestId('workflow-description-button')); - - const textarea = getByTestId('workflow-description-input'); - await userEvent.type(textarea, ' updated'); - - const saveButton = getByTestId('workflow-description-save-button'); - await userEvent.click(saveButton); - - await vi.waitFor(() => { - expect(removeActiveActionSpy).toHaveBeenCalledWith('workflowSaving'); - }); - }); - }); -}); diff --git a/packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDescriptionPopover.vue b/packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDescriptionPopover.vue deleted file mode 100644 index 504f6098642..00000000000 --- a/packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDescriptionPopover.vue +++ /dev/null @@ -1,239 +0,0 @@ - - - - diff --git a/packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDetails.vue b/packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDetails.vue index c05d89052f1..dd16f09acf2 100644 --- a/packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDetails.vue +++ b/packages/frontend/editor-ui/src/app/components/MainHeader/WorkflowDetails.vue @@ -42,8 +42,6 @@ import { } from 'vue'; import { useRoute, useRouter } from 'vue-router'; -import WorkflowDescriptionPopover from './WorkflowDescriptionPopover.vue'; - import { N8nBadge, N8nInlineTextEdit } from '@n8n/design-system'; import { useSettingsStore } from '@/app/stores/settings.store'; import { useUIStore } from '@/app/stores/ui.store'; @@ -549,11 +547,6 @@ onBeforeUnmount(() => { > {{ locale.baseText('workflows.item.archived') }} - diff --git a/packages/frontend/editor-ui/src/app/components/Modals.vue b/packages/frontend/editor-ui/src/app/components/Modals.vue index 36b630c0b60..77ca22391c5 100644 --- a/packages/frontend/editor-ui/src/app/components/Modals.vue +++ b/packages/frontend/editor-ui/src/app/components/Modals.vue @@ -30,6 +30,7 @@ import { EXPERIMENT_TEMPLATE_RECO_V3_KEY, EXPERIMENT_TEMPLATES_DATA_QUALITY_KEY, CONFIRM_PASSWORD_MODAL_KEY, + WORKFLOW_DESCRIPTION_MODAL_KEY, WORKFLOW_PUBLISH_MODAL_KEY, WORKFLOW_HISTORY_PUBLISH_MODAL_KEY, } from '@/app/constants'; @@ -114,6 +115,7 @@ import NodeRecommendationModalV2 from '@/experiments/templateRecoV2/components/N import NodeRecommendationModalV3 from '@/experiments/personalizedTemplatesV3/components/NodeRecommendationModal.vue'; import NodeRecommendationModalTDQ from '@/experiments/templatesDataQuality/components/NodeRecommendationModal.vue'; import VariableModal from '@/features/settings/environments.ee/components/VariableModal.vue'; +import WorkflowDescriptionModal from '@/app/components/WorkflowDescriptionModal.vue'; import WorkflowPublishModal from '@/app/components/MainHeader/WorkflowPublishModal.vue'; import WorkflowHistoryPublishModal from '@/features/workflows/workflowHistory/components/WorkflowHistoryPublishModal.vue'; @@ -419,6 +421,12 @@ import WorkflowHistoryPublishModal from '@/features/workflows/workflowHistory/co + + + +