Fix: add persisted project folder deletion

This commit is contained in:
MangoPig
2026-06-24 16:15:30 +01:00
parent 0b368b09fa
commit c64a7b8d44
6 changed files with 292 additions and 33 deletions
@@ -658,28 +658,65 @@ export const ProjectSelector = (props: ProjectSelectorProps): JSX.Element => {
}
};
const deletePersistedFolder = async (folderId: string): Promise<void> => {
const projectId = selectedProject().id;
if (!folderId || !isUuidString(projectId)) {
return;
}
try {
const response = await fetch(
`${resolveAPIBase()}/projects/${projectId}/folders?folderId=${encodeURIComponent(folderId)}`,
{
method: "DELETE",
headers: {
Accept: "application/json",
},
},
);
const body = (await response.json()) as ProjectFoldersResponse;
if (!response.ok) {
throw new Error(body.message || "Failed to delete project folder.");
}
setPersistedFolders(readPersistedFolders(body));
setCollapsedFolderIds((current) => current.filter((id) => id !== folderId));
} catch (error) {
console.error(error);
}
};
const cancelPendingFolder = (): void => {
setPendingFolderDraft(null);
setPendingFolderName("");
};
const handleContextActionSelect = (action: { id: string; label: string }, target: ProjectMenuTarget): void => {
if (action.id !== "new-folder") {
return;
}
switch (target.kind) {
case "surface":
beginFolderDraft(null, 0);
switch (action.id) {
case "new-folder":
switch (target.kind) {
case "surface":
beginFolderDraft(null, 0);
return;
case "folder":
beginFolderDraft(target.id, (findTreeNodeDepth(projectTreeNodes(), target.id, projectTreeAdapter) ?? 0) + 1);
return;
case "project": {
const parentId = findTreeNodeLocation(projectTreeNodes(), target.id, projectTreeAdapter)?.parentId ?? null;
beginFolderDraft(parentId, parentId ? (findTreeNodeDepth(projectTreeNodes(), parentId, projectTreeAdapter) ?? 0) + 1 : 0);
return;
}
}
return;
case "folder":
beginFolderDraft(target.id, (findTreeNodeDepth(projectTreeNodes(), target.id, projectTreeAdapter) ?? 0) + 1);
case "delete-folder":
if (target.kind === "folder") {
void deletePersistedFolder(target.id);
}
return;
case "project": {
const parentId = findTreeNodeLocation(projectTreeNodes(), target.id, projectTreeAdapter)?.parentId ?? null;
beginFolderDraft(parentId, parentId ? (findTreeNodeDepth(projectTreeNodes(), parentId, projectTreeAdapter) ?? 0) + 1 : 0);
default:
return;
}
}
};
@@ -581,6 +581,36 @@ export const WorkspaceSidebar = (props: WorkspaceSidebarProps): JSX.Element => {
}
};
const deletePersistedFolder = async (folderId: string): Promise<void> => {
const projectId = activeProject()?.id ?? "";
if (!folderId || !projectId || !isUuidString(projectId)) {
return;
}
try {
const response = await fetch(
`${resolveAPIBase()}/projects/${projectId}/tree/folders?folderId=${encodeURIComponent(folderId)}`,
{
method: "DELETE",
headers: {
Accept: "application/json",
},
},
);
const body = (await response.json()) as WorkspaceFoldersResponse;
if (!response.ok) {
throw new Error(body.message || "Failed to delete project tree folder.");
}
setPersistedFolders(readPersistedWorkspaceFolders(body));
setCollapsedFolderIds((current) => current.filter((id) => id !== folderId));
} catch (error) {
console.error(error);
}
};
const cancelPendingFolder = (): void => {
setPendingFolderDraft(null);
setPendingFolderName("");
@@ -639,20 +669,27 @@ export const WorkspaceSidebar = (props: WorkspaceSidebarProps): JSX.Element => {
};
const handleContextActionSelect = (action: WorkspaceContextMenuAction, target: WorkspaceContextMenuTarget): void => {
if (action.id !== "new-folder") {
return;
}
switch (target.kind) {
case "workspace":
case "home":
beginFolderDraft(null, 0);
switch (action.id) {
case "new-folder":
switch (target.kind) {
case "workspace":
case "home":
beginFolderDraft(null, 0);
return;
case "folder":
beginFolderDraft(target.id, (findTreeNodeDepth(workspaceTreeNodes(), target.id, workspaceTreeAdapter) ?? 0) + 1);
return;
case "settings":
case "item":
return;
}
return;
case "folder":
beginFolderDraft(target.id, (findTreeNodeDepth(workspaceTreeNodes(), target.id, workspaceTreeAdapter) ?? 0) + 1);
case "delete-folder":
if (target.kind === "folder") {
void deletePersistedFolder(target.id);
}
return;
case "settings":
case "item":
default:
return;
}
};
@@ -597,6 +597,11 @@ const getProjectCreateActions = (): readonly ProjectContextMenuAction[] =>
{ id: "new-folder", label: "New folder" },
] as const;
const getProjectFolderDangerActions = (): readonly ProjectContextMenuAction[] =>
[
{ id: "delete-folder", label: "Delete", shortcut: { modifiers: ["meta"], key: "delete" }, tone: "danger" },
] as const;
export const createProjectSurfaceTarget = (label = "Projects"): ProjectMenuTarget => ({
id: "project-surface",
label,
@@ -643,6 +648,10 @@ export const getProjectContextMenuSections = (target: ProjectMenuTarget): readon
id: "create",
items: createActions,
},
{
id: "organize",
items: getProjectFolderDangerActions(),
},
] as const;
case "project":
return [