feat(editor): Combine 'Move to Folder' and 'Change owner' modals (#15756)

This new modal also allows transferring entire folders to other projects and users.
This commit is contained in:
Jaakko Husso
2025-05-28 23:41:07 +03:00
committed by GitHub
parent ba70cab9d5
commit e860dd6d2e
27 changed files with 1989 additions and 292 deletions

View File

@@ -282,7 +282,6 @@ const workflowListResources = computed<Resource[]>(() => {
createdAt: resource.createdAt.toString(),
updatedAt: resource.updatedAt.toString(),
homeProject: resource.homeProject,
sharedWithProjects: resource.sharedWithProjects,
workflowCount: resource.workflowCount,
subFolderCount: resource.subFolderCount,
parentFolder: resource.parentFolder,
@@ -435,7 +434,9 @@ onMounted(async () => {
workflowListEventBus.on('workflow-duplicated', fetchWorkflows);
workflowListEventBus.on('folder-deleted', onFolderDeleted);
workflowListEventBus.on('folder-moved', moveFolder);
workflowListEventBus.on('folder-transferred', onFolderTransferred);
workflowListEventBus.on('workflow-moved', onWorkflowMoved);
workflowListEventBus.on('workflow-transferred', onWorkflowTransferred);
});
onBeforeUnmount(() => {
@@ -443,7 +444,9 @@ onBeforeUnmount(() => {
workflowListEventBus.off('workflow-duplicated', fetchWorkflows);
workflowListEventBus.off('folder-deleted', onFolderDeleted);
workflowListEventBus.off('folder-moved', moveFolder);
workflowListEventBus.off('folder-transferred', onFolderTransferred);
workflowListEventBus.off('workflow-moved', onWorkflowMoved);
workflowListEventBus.off('workflow-transferred', onWorkflowTransferred);
});
/**
@@ -1082,7 +1085,6 @@ const createFolder = async (
createdAt: newFolder.createdAt,
updatedAt: newFolder.updatedAt,
homeProject: projectsStore.currentProject as ProjectSharingData,
sharedWithProjects: [],
workflowCount: 0,
subFolderCount: 0,
},
@@ -1184,7 +1186,7 @@ const moveFolder = async (payload: {
await foldersStore.moveFolder(
route.params.projectId as string,
payload.folder.id,
payload.newParent.type === 'project' ? '0' : payload.newParent.id,
payload.newParent.type === 'folder' ? payload.newParent.id : '0',
);
const isCurrentFolder = currentFolderId.value === payload.folder.id;
@@ -1192,7 +1194,7 @@ const moveFolder = async (payload: {
name: VIEWS.PROJECTS_FOLDERS,
params: {
projectId: route.params.projectId,
folderId: payload.newParent.type === 'project' ? undefined : payload.newParent.id,
folderId: payload.newParent.type === 'folder' ? payload.newParent.id : undefined,
},
}).href;
if (isCurrentFolder && !payload.options?.skipNavigation) {
@@ -1203,7 +1205,10 @@ const moveFolder = async (payload: {
toast.showToast({
title: i18n.baseText('folders.move.success.title'),
message: i18n.baseText('folders.move.success.message', {
interpolate: { folderName: payload.folder.name, newFolderName: payload.newParent.name },
interpolate: {
folderName: payload.folder.name,
newFolderName: payload.newParent.name,
},
}),
onClick: (event: MouseEvent | undefined) => {
if (event?.target instanceof HTMLAnchorElement) {
@@ -1222,10 +1227,64 @@ const moveFolder = async (payload: {
}
};
const onFolderTransferred = async (payload: {
folder: { id: string; name: string };
projectId: string;
destinationProjectId: string;
newParent: { id: string; name: string; type: 'folder' | 'project' };
shareCredentials?: string[];
}) => {
const destinationParentFolderId =
payload.newParent.type === 'folder' ? payload.newParent.id : undefined;
await foldersStore.moveFolderToProject(
payload.projectId,
payload.folder.id,
payload.destinationProjectId,
destinationParentFolderId,
payload.shareCredentials,
);
const isCurrentFolder = currentFolderId.value === payload.folder.id;
const newFolderURL = router.resolve({
name: VIEWS.PROJECTS_FOLDERS,
params: {
projectId: payload.destinationProjectId,
folderId: destinationParentFolderId,
},
}).href;
if (isCurrentFolder) {
// If we just moved the current folder, automatically navigate to the new folder
void router.push(newFolderURL);
} else {
// Else show success message and update the list
toast.showToast({
title: i18n.baseText('folders.move.success.title'),
message: i18n.baseText('folders.move.success.message', {
interpolate: {
folderName: payload.folder.name,
newFolderName: payload.newParent.name,
},
}),
onClick: (event: MouseEvent | undefined) => {
if (event?.target instanceof HTMLAnchorElement) {
event.preventDefault();
void router.push(newFolderURL);
}
},
type: 'success',
});
await fetchWorkflows();
}
};
const moveWorkflowToFolder = async (payload: {
id: string;
name: string;
parentFolderId?: string;
sharedWithProjects?: ProjectSharingData[];
}) => {
if (showRegisteredCommunityCTA.value) {
uiStore.openModalWithData({
@@ -1236,11 +1295,62 @@ const moveWorkflowToFolder = async (payload: {
}
uiStore.openMoveToFolderModal(
'workflow',
{ id: payload.id, name: payload.name, parentFolderId: payload.parentFolderId },
{
id: payload.id,
name: payload.name,
parentFolderId: payload.parentFolderId,
sharedWithProjects: payload.sharedWithProjects,
},
workflowListEventBus,
);
};
const onWorkflowTransferred = async (payload: {
projectId: string;
workflow: { id: string; name: string; oldParentId: string };
newParent: { id: string; name: string; type: 'folder' | 'project' };
shareCredentials?: string[];
}) => {
const parentFolderId = payload.newParent.type === 'folder' ? payload.newParent.id : undefined;
await projectsStore.moveResourceToProject(
'workflow',
payload.workflow.id,
payload.projectId,
parentFolderId,
payload.shareCredentials,
);
await fetchWorkflows();
try {
toast.showToast({
title: i18n.baseText('folders.move.workflow.success.title'),
message: i18n.baseText('folders.move.workflow.success.message', {
interpolate: {
workflowName: payload.workflow.name,
newFolderName: payload.newParent.name,
},
}),
onClick: (event: MouseEvent | undefined) => {
if (event?.target instanceof HTMLAnchorElement) {
event.preventDefault();
void router.push({
name: VIEWS.PROJECTS_FOLDERS,
params: {
projectId: payload.projectId,
folderId: payload.newParent.type === 'folder' ? payload.newParent.id : undefined,
},
});
}
},
type: 'success',
});
} catch (error) {
toast.showError(error, i18n.baseText('folders.move.workflow.error.title'));
}
};
const onWorkflowMoved = async (payload: {
workflow: { id: string; name: string; oldParentId: string };
newParent: { id: string; name: string; type: 'folder' | 'project' };
@@ -1254,14 +1364,14 @@ const onWorkflowMoved = async (payload: {
name: VIEWS.PROJECTS_FOLDERS,
params: {
projectId: route.params.projectId,
folderId: payload.newParent.type === 'project' ? undefined : payload.newParent.id,
folderId: payload.newParent.type === 'folder' ? payload.newParent.id : undefined,
},
}).href;
const workflowResource = workflowsAndFolders.value.find(
(resource): resource is WorkflowListItem => resource.id === payload.workflow.id,
);
await workflowsStore.updateWorkflow(payload.workflow.id, {
parentFolderId: payload.newParent.type === 'project' ? '0' : payload.newParent.id,
parentFolderId: payload.newParent.type === 'folder' ? payload.newParent.id : '0',
versionId: workflowResource?.versionId,
});
if (!payload.options?.skipFetch) {
@@ -1270,7 +1380,10 @@ const onWorkflowMoved = async (payload: {
toast.showToast({
title: i18n.baseText('folders.move.workflow.success.title'),
message: i18n.baseText('folders.move.workflow.success.message', {
interpolate: { workflowName: payload.workflow.name, newFolderName: payload.newParent.name },
interpolate: {
workflowName: payload.workflow.name,
newFolderName: payload.newParent.name,
},
}),
onClick: (event: MouseEvent | undefined) => {
if (event?.target instanceof HTMLAnchorElement) {