diff --git a/cypress/e2e/23-variables.cy.ts b/cypress/e2e/23-variables.cy.ts index a0af3c09aca59..e3d67ab987e65 100644 --- a/cypress/e2e/23-variables.cy.ts +++ b/cypress/e2e/23-variables.cy.ts @@ -101,18 +101,22 @@ describe('Variables', () => { variablesPage.getters.searchBar().type('NEW'); variablesPage.getters.variablesRows().should('have.length', 1); variablesPage.getters.variableRow('NEW').should('contain.text', 'ENV_VAR_NEW'); + cy.url().should('include', 'search=NEW'); // Multiple Results variablesPage.getters.searchBar().clear().type('ENV_VAR'); variablesPage.getters.variablesRows().should('have.length', 2); + cy.url().should('include', 'search=ENV_VAR'); // All Results variablesPage.getters.searchBar().clear().type('ENV'); variablesPage.getters.variablesRows().should('have.length', 3); + cy.url().should('include', 'search=ENV'); // No Results variablesPage.getters.searchBar().clear().type('Some non-existent variable'); variablesPage.getters.variablesRows().should('not.exist'); + cy.url().should('include', 'search=Some+non-existent+variable'); cy.contains('No variables found').should('be.visible'); }); diff --git a/packages/editor-ui/src/api/workflows.ts b/packages/editor-ui/src/api/workflows.ts index e565a95c9e6ed..a76e3c1880ccd 100644 --- a/packages/editor-ui/src/api/workflows.ts +++ b/packages/editor-ui/src/api/workflows.ts @@ -11,7 +11,7 @@ import type { ExecutionSummary, IDataObject, } from 'n8n-workflow'; -import { makeRestApiRequest } from '@/utils/apiUtils'; +import { getFullApiResponse, makeRestApiRequest } from '@/utils/apiUtils'; export async function getNewWorkflow(context: IRestApiContext, data?: IDataObject) { const response = await makeRestApiRequest( @@ -32,10 +32,11 @@ export async function getWorkflow(context: IRestApiContext, id: string, filter?: return await makeRestApiRequest(context, 'GET', `/workflows/${id}`, sendData); } -export async function getWorkflows(context: IRestApiContext, filter?: object) { - return await makeRestApiRequest(context, 'GET', '/workflows', { +export async function getWorkflows(context: IRestApiContext, filter?: object, options?: object) { + return await getFullApiResponse(context, 'GET', '/workflows', { includeScopes: true, ...(filter ? { filter } : {}), + ...(options ? options : {}), }); } diff --git a/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue b/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue index f73c8be99a2ff..62753180a2c51 100644 --- a/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue +++ b/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue @@ -7,7 +7,7 @@ import Modal from '@/components/Modal.vue'; import { useSettingsStore } from '@/stores/settings.store'; import { useWorkflowsStore } from '@/stores/workflows.store'; import type { IWorkflowDataUpdate } from '@/Interface'; -import { createEventBus } from 'n8n-design-system/utils'; +import { createEventBus, type EventBus } from 'n8n-design-system/utils'; import { useCredentialsStore } from '@/stores/credentials.store'; import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers'; import { useRouter } from 'vue-router'; @@ -17,7 +17,7 @@ import { useTelemetry } from '@/composables/useTelemetry'; const props = defineProps<{ modalName: string; isActive: boolean; - data: { tags: string[]; id: string; name: string }; + data: { tags: string[]; id: string; name: string; externalEventBus?: EventBus }; }>(); const router = useRouter(); @@ -111,6 +111,7 @@ const save = async (): Promise => { workflow_id: props.data.id, sharing_role: workflowHelpers.getWorkflowProjectRole(props.data.id), }); + props.data.externalEventBus?.emit('workflow-duplicated', { id: props.data.id }); } } catch (error) { if (error.httpStatusCode === 403) { diff --git a/packages/editor-ui/src/components/Projects/ProjectMoveResourceModal.vue b/packages/editor-ui/src/components/Projects/ProjectMoveResourceModal.vue index 2fffdc4c8dba2..cab0bd7a99765 100644 --- a/packages/editor-ui/src/components/Projects/ProjectMoveResourceModal.vue +++ b/packages/editor-ui/src/components/Projects/ProjectMoveResourceModal.vue @@ -13,6 +13,7 @@ import ProjectMoveSuccessToastMessage from '@/components/Projects/ProjectMoveSuc import { useToast } from '@/composables/useToast'; import { getResourcePermissions } from '@/permissions'; import { sortByProperty } from '@/utils/sortUtils'; +import type { EventBus } from 'n8n-design-system/utils'; const props = defineProps<{ modalName: string; @@ -20,6 +21,7 @@ const props = defineProps<{ resource: IWorkflowDb | ICredentialsResponse; resourceType: ResourceType; resourceTypeLabel: string; + eventBus?: EventBus; }; }>(); @@ -104,6 +106,13 @@ const moveResource = async () => { type: 'success', duration: 8000, }); + if (props.data.eventBus) { + props.data.eventBus.emit('resource-moved', { + resourceId: props.data.resource.id, + resourceType: props.data.resourceType, + targetProjectId: selectedProject.value.id, + }); + } } catch (error) { toast.showError( error.message, diff --git a/packages/editor-ui/src/components/WorkflowActivator.vue b/packages/editor-ui/src/components/WorkflowActivator.vue index 888f473cc0c3c..c76b90e6374c3 100644 --- a/packages/editor-ui/src/components/WorkflowActivator.vue +++ b/packages/editor-ui/src/components/WorkflowActivator.vue @@ -18,6 +18,11 @@ const props = defineProps<{ workflowId: string; workflowPermissions: PermissionsRecord['workflow']; }>(); + +const emit = defineEmits<{ + 'update:workflowActive': [value: { id: string; active: boolean }]; +}>(); + const { showMessage } = useToast(); const workflowActivate = useWorkflowActivate(); @@ -109,7 +114,11 @@ const shouldShowFreeAiCreditsWarning = computed((): boolean => { }); async function activeChanged(newActiveState: boolean) { - return await workflowActivate.updateWorkflowActivation(props.workflowId, newActiveState); + const newState = await workflowActivate.updateWorkflowActivation( + props.workflowId, + newActiveState, + ); + emit('update:workflowActive', { id: props.workflowId, active: newState }); } async function displayActivationError() { diff --git a/packages/editor-ui/src/components/WorkflowCard.vue b/packages/editor-ui/src/components/WorkflowCard.vue index 3a12304afcc71..395685140e587 100644 --- a/packages/editor-ui/src/components/WorkflowCard.vue +++ b/packages/editor-ui/src/components/WorkflowCard.vue @@ -25,6 +25,7 @@ import { useI18n } from '@/composables/useI18n'; import { useRouter } from 'vue-router'; import { useTelemetry } from '@/composables/useTelemetry'; import { ResourceType } from '@/utils/projects.utils'; +import type { EventBus } from 'n8n-design-system/utils'; const WORKFLOW_LIST_ITEM_ACTIONS = { OPEN: 'open', @@ -38,6 +39,7 @@ const props = withDefaults( defineProps<{ data: IWorkflowDb; readOnly?: boolean; + workflowListEventBus?: EventBus; }>(), { data: () => ({ @@ -53,12 +55,15 @@ const props = withDefaults( versionId: '', }), readOnly: false, + workflowListEventBus: undefined, }, ); const emit = defineEmits<{ 'expand:tags': []; 'click:tag': [tagId: string, e: PointerEvent]; + 'workflow:deleted': []; + 'workflow:active-toggle': [value: { id: string; active: boolean }]; }>(); const toast = useToast(); @@ -160,6 +165,7 @@ async function onAction(action: string) { tags: (props.data.tags ?? []).map((tag) => typeof tag !== 'string' && 'id' in tag ? tag.id : tag, ), + externalEventBus: props.workflowListEventBus, }, }); break; @@ -217,6 +223,7 @@ async function deleteWorkflow() { title: locale.baseText('mainSidebar.showMessage.handleSelect1.title'), type: 'success', }); + emit('workflow:deleted'); } function moveResource() { @@ -226,9 +233,14 @@ function moveResource() { resource: props.data, resourceType: ResourceType.Workflow, resourceTypeLabel: resourceTypeLabel.value, + eventBus: props.workflowListEventBus, }, }); } + +const emitWorkflowActiveToggle = (value: { id: string; active: boolean }) => { + emit('workflow:active-toggle', value); +};