fix(editor): Hide 'Move' modal toast links that can't be accessed and better empty state (no-changelog) (#15977)

This commit is contained in:
Jaakko Husso
2025-06-04 11:49:23 +03:00
committed by GitHub
parent 5c7f682b73
commit eac27b7ac0
4 changed files with 257 additions and 139 deletions

View File

@@ -980,10 +980,12 @@
"folders.move.modal.message.usedCredentials.warning": "Workflow may not execute correctly if you choose not to share the credentials.", "folders.move.modal.message.usedCredentials.warning": "Workflow may not execute correctly if you choose not to share the credentials.",
"folders.move.success.title": "Successfully moved folder", "folders.move.success.title": "Successfully moved folder",
"folders.move.success.message": "<b>{folderName}</b> has been moved to <b>{newFolderName}</b>, along with all its workflows and subfolders.<br/><br/><a href=\"{link}\">View {newFolderName}</a>", "folders.move.success.message": "<b>{folderName}</b> has been moved to <b>{newFolderName}</b>, along with all its workflows and subfolders.<br/><br/><a href=\"{link}\">View {newFolderName}</a>",
"folders.move.success.messageNoAccess": "<b>{folderName}</b> has been moved to <b>{newFolderName}</b>, along with all its workflows and subfolders.",
"folders.move.error.title": "Problem moving folder", "folders.move.error.title": "Problem moving folder",
"folders.move.workflow.error.title": "Problem moving workflow", "folders.move.workflow.error.title": "Problem moving workflow",
"folders.move.workflow.success.title": "Successfully moved workflow", "folders.move.workflow.success.title": "Successfully moved workflow",
"folders.move.workflow.success.message": "<b>{workflowName}</b> has been moved to <b>{newFolderName}</b>.<br/><br/><a href=\"{link}\">View {newFolderName}</a>", "folders.move.workflow.success.message": "<b>{workflowName}</b> has been moved to <b>{newFolderName}</b>.<br/><br/><a href=\"{link}\">View {newFolderName}</a>",
"folders.move.workflow.success.messageNoAccess": "<b>{workflowName}</b> has been moved to <b>{newFolderName}</b>.",
"folders.move.project.root.name": "No folder (project root)", "folders.move.project.root.name": "No folder (project root)",
"folders.open.error.title": "Problem opening folder", "folders.open.error.title": "Problem opening folder",
"folders.create.error.title": "Problem creating folder", "folders.create.error.title": "Problem creating folder",

View File

@@ -487,14 +487,21 @@ describe('MoveToFolderModal', () => {
await userEvent.click(submitButton); await userEvent.click(submitButton);
expect(mockEventBus.emit).toHaveBeenCalledWith('folder-transferred', { expect(mockEventBus.emit).toHaveBeenCalledWith('folder-transferred', {
newParent: { source: {
projectId: personalProject.id,
folder: {
id: TEST_FOLDER_RESOURCE.id,
name: TEST_FOLDER_RESOURCE.name,
},
},
destination: {
projectId: teamProjects[0].id,
parentFolder: {
id: folder.id, id: folder.id,
name: folder.name, name: folder.name,
type: folder.resource,
}, },
folder: { id: TEST_FOLDER_RESOURCE.id, name: TEST_FOLDER_RESOURCE.name }, canAccess: true,
projectId: personalProject.id, },
destinationProjectId: teamProjects[0].id,
shareCredentials: undefined, shareCredentials: undefined,
}); });
}); });
@@ -536,14 +543,21 @@ describe('MoveToFolderModal', () => {
await userEvent.click(submitButton); await userEvent.click(submitButton);
expect(mockEventBus.emit).toHaveBeenCalledWith('folder-transferred', { expect(mockEventBus.emit).toHaveBeenCalledWith('folder-transferred', {
newParent: { source: {
projectId: personalProject.id,
folder: {
id: TEST_FOLDER_RESOURCE.id,
name: TEST_FOLDER_RESOURCE.name,
},
},
destination: {
projectId: teamProjects[0].id,
parentFolder: {
id: folder.id, id: folder.id,
name: folder.name, name: folder.name,
type: folder.resource,
}, },
folder: { id: TEST_FOLDER_RESOURCE.id, name: TEST_FOLDER_RESOURCE.name }, canAccess: true,
projectId: personalProject.id, },
destinationProjectId: teamProjects[0].id,
shareCredentials: [shareableUsedCredential.id], shareCredentials: [shareableUsedCredential.id],
}); });
}); });
@@ -584,14 +598,21 @@ describe('MoveToFolderModal', () => {
await userEvent.click(submitButton); await userEvent.click(submitButton);
expect(mockEventBus.emit).toHaveBeenCalledWith('folder-transferred', { expect(mockEventBus.emit).toHaveBeenCalledWith('folder-transferred', {
newParent: { source: {
projectId: personalProject.id,
folder: {
id: TEST_FOLDER_RESOURCE.id,
name: TEST_FOLDER_RESOURCE.name,
},
},
destination: {
projectId: teamProjects[0].id,
parentFolder: {
id: folder.id, id: folder.id,
name: folder.name, name: folder.name,
type: folder.resource,
}, },
folder: { id: TEST_FOLDER_RESOURCE.id, name: TEST_FOLDER_RESOURCE.name }, canAccess: true,
projectId: personalProject.id, },
destinationProjectId: teamProjects[0].id,
shareCredentials: undefined, shareCredentials: undefined,
}); });
}); });
@@ -628,14 +649,21 @@ describe('MoveToFolderModal', () => {
await userEvent.click(submitButton); await userEvent.click(submitButton);
expect(mockEventBus.emit).toHaveBeenCalledWith('folder-transferred', { expect(mockEventBus.emit).toHaveBeenCalledWith('folder-transferred', {
newParent: { source: {
id: anotherUser.id,
name: anotherUser.name,
type: 'project',
},
folder: { id: TEST_FOLDER_RESOURCE.id, name: TEST_FOLDER_RESOURCE.name },
projectId: personalProject.id, projectId: personalProject.id,
destinationProjectId: anotherUser.id, folder: {
id: TEST_FOLDER_RESOURCE.id,
name: TEST_FOLDER_RESOURCE.name,
},
},
destination: {
projectId: anotherUser.id,
parentFolder: {
id: undefined,
name: anotherUser.name,
},
canAccess: false,
},
shareCredentials: undefined, shareCredentials: undefined,
}); });
}); });
@@ -731,17 +759,21 @@ describe('MoveToFolderModal', () => {
await userEvent.click(submitButton); await userEvent.click(submitButton);
expect(mockEventBus.emit).toHaveBeenCalledWith('workflow-transferred', { expect(mockEventBus.emit).toHaveBeenCalledWith('workflow-transferred', {
newParent: { source: {
id: folder.id, projectId: personalProject.id,
name: folder.name,
type: folder.resource,
},
workflow: { workflow: {
id: TEST_WORKFLOW_RESOURCE.id, id: TEST_WORKFLOW_RESOURCE.id,
name: TEST_WORKFLOW_RESOURCE.name, name: TEST_WORKFLOW_RESOURCE.name,
oldParentId: TEST_WORKFLOW_RESOURCE.parentFolderId,
}, },
},
destination: {
projectId: teamProjects[0].id, projectId: teamProjects[0].id,
parentFolder: {
id: folder.id,
name: folder.name,
},
canAccess: true,
},
shareCredentials: undefined, shareCredentials: undefined,
}); });
}); });
@@ -778,17 +810,21 @@ describe('MoveToFolderModal', () => {
await userEvent.click(submitButton); await userEvent.click(submitButton);
expect(mockEventBus.emit).toHaveBeenCalledWith('workflow-transferred', { expect(mockEventBus.emit).toHaveBeenCalledWith('workflow-transferred', {
newParent: { source: {
id: anotherUser.id, projectId: personalProject.id,
name: anotherUser.name,
type: 'project',
},
workflow: { workflow: {
id: TEST_WORKFLOW_RESOURCE.id, id: TEST_WORKFLOW_RESOURCE.id,
name: TEST_WORKFLOW_RESOURCE.name, name: TEST_WORKFLOW_RESOURCE.name,
oldParentId: TEST_WORKFLOW_RESOURCE.parentFolderId,
}, },
},
destination: {
projectId: anotherUser.id, projectId: anotherUser.id,
parentFolder: {
id: undefined,
name: anotherUser.name,
},
canAccess: false,
},
shareCredentials: undefined, shareCredentials: undefined,
}); });
}); });

View File

@@ -60,14 +60,14 @@ const credentialsStore = useCredentialsStore();
const workflowsStore = useWorkflowsStore(); const workflowsStore = useWorkflowsStore();
const selectedFolder = ref<ChangeLocationSearchResult | null>(null); const selectedFolder = ref<ChangeLocationSearchResult | null>(null);
const selectedProject = ref<ProjectSharingData | null>(projectsStore.currentProject ?? null); const selectedProject = ref<ProjectSharingData | null>(projectsStore.currentProject);
const isPersonalProject = computed(() => { const isPersonalProject = computed(() => {
return selectedProject.value?.type === ProjectTypes.Personal; return selectedProject.value?.type === ProjectTypes.Personal;
}); });
const isOwnPersonalProject = computed(() => { const isOwnPersonalProject = computed(() => {
return ( return (
selectedProject.value?.type === ProjectTypes.Personal && selectedProject.value?.type === ProjectTypes.Personal &&
selectedProject.value.id === projectsStore.personalProject?.id selectedProject.value?.id === projectsStore.personalProject?.id
); );
}); });
const isTransferringOwnership = computed(() => { const isTransferringOwnership = computed(() => {
@@ -196,12 +196,29 @@ const onSubmit = () => {
}; };
if (props.data.resourceType === 'folder') { if (props.data.resourceType === 'folder') {
if (selectedProject.value?.id !== projectsStore.currentProject?.id) { if (selectedProject.value.id !== projectsStore.currentProject?.id) {
props.data.workflowListEventBus.emit('folder-transferred', { props.data.workflowListEventBus.emit('folder-transferred', {
newParent, source: {
folder: { id: props.data.resource.id, name: props.data.resource.name },
projectId: projectsStore.currentProject?.id, projectId: projectsStore.currentProject?.id,
destinationProjectId: selectedProject.value.id, folder: {
id: props.data.resource.id,
name: props.data.resource.name,
},
},
destination: {
projectId: selectedProject.value.id,
parentFolder: {
id:
selectedFolder.value && selectedFolder.value.id !== selectedProject.value.id
? selectedFolder.value.id
: undefined,
name:
selectedFolder.value && selectedFolder.value.id !== selectedProject.value.id
? selectedFolder.value.name
: targetProjectName.value,
},
canAccess: isFolderSelectable.value,
},
shareCredentials: shareUsedCredentials.value shareCredentials: shareUsedCredentials.value
? shareableCredentials.value.map((c) => c.id) ? shareableCredentials.value.map((c) => c.id)
: undefined, : undefined,
@@ -215,12 +232,26 @@ const onSubmit = () => {
} else { } else {
if (isTransferringOwnership.value) { if (isTransferringOwnership.value) {
props.data.workflowListEventBus.emit('workflow-transferred', { props.data.workflowListEventBus.emit('workflow-transferred', {
newParent, source: {
projectId: selectedProject.value.id, projectId: projectsStore.currentProject?.id,
workflow: { workflow: {
id: props.data.resource.id, id: props.data.resource.id,
name: props.data.resource.name, name: props.data.resource.name,
oldParentId: props.data.resource.parentFolderId, },
},
destination: {
projectId: selectedProject.value.id,
parentFolder: {
id:
selectedFolder.value && selectedFolder.value.id !== selectedProject.value.id
? selectedFolder.value.id
: undefined,
name:
selectedFolder.value && selectedFolder.value.id !== selectedProject.value.id
? selectedFolder.value.name
: targetProjectName.value,
},
canAccess: isFolderSelectable.value,
}, },
shareCredentials: shareUsedCredentials.value shareCredentials: shareUsedCredentials.value
? shareableCredentials.value.map((c) => c.id) ? shareableCredentials.value.map((c) => c.id)
@@ -237,6 +268,7 @@ const onSubmit = () => {
}); });
} }
} }
uiStore.closeModal(MOVE_FOLDER_MODAL_KEY); uiStore.closeModal(MOVE_FOLDER_MODAL_KEY);
}; };

View File

@@ -1181,6 +1181,7 @@ const moveFolder = async (payload: {
}; };
}) => { }) => {
if (!route.params.projectId) return; if (!route.params.projectId) return;
try { try {
await foldersStore.moveFolder( await foldersStore.moveFolder(
route.params.projectId as string, route.params.projectId as string,
@@ -1227,43 +1228,60 @@ const moveFolder = async (payload: {
}; };
const onFolderTransferred = async (payload: { const onFolderTransferred = async (payload: {
folder: { id: string; name: string }; source: {
projectId: string; projectId: string;
destinationProjectId: string; folder: { id: string; name: string };
newParent: { id: string; name: string; type: 'folder' | 'project' }; };
destination: {
projectId: string;
parentFolder: { id: string | undefined; name: string };
canAccess: boolean;
};
shareCredentials?: string[]; shareCredentials?: string[];
}) => { }) => {
const destinationParentFolderId = try {
payload.newParent.type === 'folder' ? payload.newParent.id : undefined;
await foldersStore.moveFolderToProject( await foldersStore.moveFolderToProject(
payload.projectId, payload.source.projectId,
payload.folder.id, payload.source.folder.id,
payload.destinationProjectId, payload.destination.projectId,
destinationParentFolderId, payload.destination.parentFolder.id,
payload.shareCredentials, payload.shareCredentials,
); );
const isCurrentFolder = currentFolderId.value === payload.folder.id; const isCurrentFolder = currentFolderId.value === payload.source.folder.id;
const newFolderURL = router.resolve({ const newFolderURL = router.resolve({
name: VIEWS.PROJECTS_FOLDERS, name: VIEWS.PROJECTS_FOLDERS,
params: { params: {
projectId: payload.destinationProjectId, projectId: payload.destination.canAccess
folderId: destinationParentFolderId, ? payload.destination.projectId
: payload.source.projectId,
folderId: payload.destination.canAccess ? payload.source.folder.id : undefined,
}, },
}).href; }).href;
if (isCurrentFolder) { if (isCurrentFolder) {
// If we just moved the current folder, automatically navigate to the new folder if (payload.destination.canAccess) {
// If we just moved the current folder and can access the destination navigate there
void router.push(newFolderURL); void router.push(newFolderURL);
} else { } else {
// Else show success message and update the list // Otherwise navigate to the workflows page of the source project
void router.push({
name: VIEWS.PROJECTS_WORKFLOWS,
params: {
projectId: payload.source.projectId,
},
});
}
} else {
await refreshWorkflows();
if (payload.destination.canAccess) {
toast.showToast({ toast.showToast({
title: i18n.baseText('folders.move.success.title'), title: i18n.baseText('folders.move.success.title'),
message: i18n.baseText('folders.move.success.message', { message: i18n.baseText('folders.move.success.message', {
interpolate: { interpolate: {
folderName: payload.folder.name, folderName: payload.source.folder.name,
newFolderName: payload.newParent.name, newFolderName: payload.destination.parentFolder.name,
}, },
}), }),
onClick: (event: MouseEvent | undefined) => { onClick: (event: MouseEvent | undefined) => {
@@ -1274,8 +1292,21 @@ const onFolderTransferred = async (payload: {
}, },
type: 'success', type: 'success',
}); });
} else {
await fetchWorkflows(); toast.showToast({
title: i18n.baseText('folders.move.success.title'),
message: i18n.baseText('folders.move.success.messageNoAccess', {
interpolate: {
folderName: payload.source.folder.name,
newFolderName: payload.destination.parentFolder.name,
},
}),
type: 'success',
});
}
}
} catch (error) {
toast.showError(error, i18n.baseText('folders.move.error.title'));
} }
}; };
@@ -1305,30 +1336,35 @@ const moveWorkflowToFolder = async (payload: {
}; };
const onWorkflowTransferred = async (payload: { const onWorkflowTransferred = async (payload: {
source: {
projectId: string; projectId: string;
workflow: { id: string; name: string; oldParentId: string }; workflow: { id: string; name: string };
newParent: { id: string; name: string; type: 'folder' | 'project' }; };
destination: {
projectId: string;
parentFolder: { id: string | undefined; name: string };
canAccess: boolean;
};
shareCredentials?: string[]; shareCredentials?: string[];
}) => { }) => {
const parentFolderId = payload.newParent.type === 'folder' ? payload.newParent.id : undefined; try {
await projectsStore.moveResourceToProject( await projectsStore.moveResourceToProject(
'workflow', 'workflow',
payload.workflow.id, payload.source.workflow.id,
payload.projectId, payload.destination.projectId,
parentFolderId, payload.destination.parentFolder.id,
payload.shareCredentials, payload.shareCredentials,
); );
await fetchWorkflows(); await refreshWorkflows();
try { if (payload.destination.canAccess) {
toast.showToast({ toast.showToast({
title: i18n.baseText('folders.move.workflow.success.title'), title: i18n.baseText('folders.move.workflow.success.title'),
message: i18n.baseText('folders.move.workflow.success.message', { message: i18n.baseText('folders.move.workflow.success.message', {
interpolate: { interpolate: {
workflowName: payload.workflow.name, workflowName: payload.source.workflow.name,
newFolderName: payload.newParent.name, newFolderName: payload.destination.parentFolder.name,
}, },
}), }),
onClick: (event: MouseEvent | undefined) => { onClick: (event: MouseEvent | undefined) => {
@@ -1337,14 +1373,26 @@ const onWorkflowTransferred = async (payload: {
void router.push({ void router.push({
name: VIEWS.PROJECTS_FOLDERS, name: VIEWS.PROJECTS_FOLDERS,
params: { params: {
projectId: payload.projectId, projectId: payload.destination.projectId,
folderId: payload.newParent.type === 'folder' ? payload.newParent.id : undefined, folderId: payload.destination.parentFolder.id,
}, },
}); });
} }
}, },
type: 'success', type: 'success',
}); });
} else {
toast.showToast({
title: i18n.baseText('folders.move.workflow.success.title'),
message: i18n.baseText('folders.move.workflow.success.messageNoAccess', {
interpolate: {
workflowName: payload.source.workflow.name,
newFolderName: payload.destination.parentFolder.name,
},
}),
type: 'success',
});
}
} catch (error) { } catch (error) {
toast.showError(error, i18n.baseText('folders.move.workflow.error.title')); toast.showError(error, i18n.baseText('folders.move.workflow.error.title'));
} }