diff --git a/packages/cli/src/workflows/workflow.service.ee.ts b/packages/cli/src/workflows/workflow.service.ee.ts index a4eacfa8c5d8f..b36f4afe9cda9 100644 --- a/packages/cli/src/workflows/workflow.service.ee.ts +++ b/packages/cli/src/workflows/workflow.service.ee.ts @@ -279,9 +279,14 @@ export class EnterpriseWorkflowService { destinationParentFolderId?: string, ) { // 1. get workflow - const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [ - 'workflow:move', - ]); + const workflow = await this.workflowFinderService.findWorkflowForUser( + workflowId, + user, + ['workflow:move'], + { + includeParentFolder: true, + }, + ); NotFoundError.isDefinedAndNotNull( workflow, `Could not find workflow with the id "${workflowId}". Make sure you have the permission to move it.`, @@ -309,9 +314,12 @@ export class EnterpriseWorkflowService { ); // 5. checks - if (sourceProject.id === destinationProject.id) { + if ( + sourceProject.id === destinationProject.id && + destinationParentFolderId === workflow.parentFolder?.id + ) { throw new TransferWorkflowError( - "You can't transfer a workflow into the project that's already owning it.", + "You can't transfer a workflow into the same destination it already belongs to.", ); } diff --git a/packages/frontend/editor-ui/src/app/components/WorkflowCard.test.ts b/packages/frontend/editor-ui/src/app/components/WorkflowCard.test.ts index 7c4ae220c5ef9..49a0a0717616e 100644 --- a/packages/frontend/editor-ui/src/app/components/WorkflowCard.test.ts +++ b/packages/frontend/editor-ui/src/app/components/WorkflowCard.test.ts @@ -274,35 +274,6 @@ describe('WorkflowCard', () => { expect(actions).not.toHaveTextContent('Move'); }); - it("should not show 'Move' action on the 'Workflows' page", async () => { - vi.spyOn(settingsStore, 'isFoldersFeatureEnabled', 'get').mockReturnValue(true); - - const data = createWorkflow({ - scopes: ['workflow:update'], - }); - - vi.spyOn(vueRouter, 'useRoute').mockReturnValueOnce({ - name: VIEWS.WORKFLOWS, - } as vueRouter.RouteLocationNormalizedLoadedGeneric); - - const { getByTestId } = renderComponent({ props: { data } }); - const cardActions = getByTestId('workflow-card-actions'); - - expect(cardActions).toBeInTheDocument(); - - const cardActionsOpener = within(cardActions).getByRole('button'); - expect(cardActionsOpener).toBeInTheDocument(); - - const controllingId = cardActionsOpener.getAttribute('aria-controls'); - - await userEvent.click(cardActions); - const actions = document.querySelector(`#${controllingId}`); - if (!actions) { - throw new Error('Actions menu not found'); - } - expect(actions).not.toHaveTextContent('Move'); - }); - it("should not show 'Move' action on read only instance", async () => { vi.spyOn(projectsStore, 'isTeamProjectFeatureEnabled', 'get').mockReturnValue(true); vi.spyOn(settingsStore, 'isFoldersFeatureEnabled', 'get').mockReturnValue(true); diff --git a/packages/frontend/editor-ui/src/app/components/WorkflowCard.vue b/packages/frontend/editor-ui/src/app/components/WorkflowCard.vue index 177c440e34df9..b014cbfec131a 100644 --- a/packages/frontend/editor-ui/src/app/components/WorkflowCard.vue +++ b/packages/frontend/editor-ui/src/app/components/WorkflowCard.vue @@ -90,6 +90,7 @@ const emit = defineEmits<{ name: string; parentFolderId?: string; sharedWithProjects?: ProjectSharingData[]; + homeProjectId?: string; }, ]; }>(); @@ -135,10 +136,6 @@ const canCreateWorkflow = computed( () => globalPermissions.value.create ?? projectPermissions.value.create, ); -const showFolders = computed(() => { - return props.areFoldersEnabled && route.name !== VIEWS.WORKFLOWS; -}); - const showCardBreadcrumbs = computed(() => { return props.showOwnershipBadge && !isSomeoneElsesWorkflow.value && cardBreadcrumbs.value.length; }); @@ -197,9 +194,9 @@ const actions = computed(() => { // TODO: add test to verify that moving a readonly card is not possible if ( !props.readOnly && + props.areFoldersEnabled && (workflowPermissions.value.update || (workflowPermissions.value.move && projectsStore.isTeamProjectFeatureEnabled)) && - showFolders.value && route.name !== VIEWS.SHARED_WORKFLOWS ) { items.push({ @@ -346,6 +343,7 @@ async function onAction(action: string) { name: props.data.name, parentFolderId: props.data.parentFolder?.id, sharedWithProjects: props.data.sharedWithProjects, + homeProjectId: props.data.homeProject?.id, }); break; case WORKFLOW_LIST_ITEM_ACTIONS.ENABLE_MCP_ACCESS: diff --git a/packages/frontend/editor-ui/src/app/stores/ui.store.ts b/packages/frontend/editor-ui/src/app/stores/ui.store.ts index fad8cfdf65e77..d22d124174b5c 100644 --- a/packages/frontend/editor-ui/src/app/stores/ui.store.ts +++ b/packages/frontend/editor-ui/src/app/stores/ui.store.ts @@ -537,6 +537,7 @@ export const useUIStore = defineStore(STORES.UI, () => { name: string; parentFolderId?: string; sharedWithProjects?: ProjectSharingData[]; + homeProjectId?: string; }, workflowListEventBus: EventBus, ) => { diff --git a/packages/frontend/editor-ui/src/app/views/WorkflowsView.vue b/packages/frontend/editor-ui/src/app/views/WorkflowsView.vue index 5e68b90333140..73b4dfc5d8e82 100644 --- a/packages/frontend/editor-ui/src/app/views/WorkflowsView.vue +++ b/packages/frontend/editor-ui/src/app/views/WorkflowsView.vue @@ -1598,6 +1598,7 @@ const moveWorkflowToFolder = async (payload: { name: string; parentFolderId?: string; sharedWithProjects?: ProjectSharingData[]; + homeProjectId?: string; }) => { if (showRegisteredCommunityCTA.value) { uiStore.openModalWithData({ @@ -1613,6 +1614,7 @@ const moveWorkflowToFolder = async (payload: { name: payload.name, parentFolderId: payload.parentFolderId, sharedWithProjects: payload.sharedWithProjects, + homeProjectId: payload.homeProjectId, }, workflowListEventBus, ); diff --git a/packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderModal.test.ts b/packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderModal.test.ts index 1bd3ad3f495be..3a20b62241e4c 100644 --- a/packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderModal.test.ts +++ b/packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderModal.test.ts @@ -84,6 +84,7 @@ const homeProject = createProjectSharingData(); const enableSharing = { enterprise: { sharing: true, + projects: { team: { limit: -1 } }, }, } as FrontendSettings; @@ -152,7 +153,9 @@ describe('MoveToFolderModal', () => { settingsStore = mockedStore(useSettingsStore); settingsStore.settings = { - enterprise: {}, + enterprise: { + projects: { team: { limit: -1 } }, + }, } as FrontendSettings; credentialsStore = mockedStore(useCredentialsStore); diff --git a/packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderModal.vue b/packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderModal.vue index 206dacfb45bd3..fdd512af757c1 100644 --- a/packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderModal.vue +++ b/packages/frontend/editor-ui/src/features/core/folders/components/MoveToFolderModal.vue @@ -46,6 +46,7 @@ type Props = { name: string; parentFolderId?: string; sharedWithProjects?: ProjectSharingData[]; + homeProjectId?: string; }; workflowListEventBus: EventBus; }; @@ -317,7 +318,15 @@ const isFolderSelectable = computed(() => { return isOwnPersonalProject.value || !isPersonalProject.value; }); +// If there is not current project (e.g. on the Overview page), default to the resource's home project +const currentResourceProjectId = computed(() => { + return projectsStore.currentProject?.id ?? props.data.resource.homeProjectId; +}); + onMounted(async () => { + if (!projectsStore.isTeamProjectFeatureEnabled) { + selectedProject.value = projectsStore.personalProject; + } if (isResourceWorkflow.value) { const [workflow, credentials] = await Promise.all([ workflowsStore.fetchWorkflow(props.data.resource.id), @@ -407,7 +416,7 @@ onMounted(async () => { ref="moveToFolderDropdown" :selected-location="selectedFolder" :selected-project-id="selectedProject.id" - :current-project-id="projectsStore.currentProject?.id" + :current-project-id="currentResourceProjectId" :current-folder-id="currentFolder?.id" :parent-folder-id="props.data.resource.parentFolderId" :exclude-only-parent="props.data.resourceType === 'workflow'"