diff --git a/frontend/src/api/inbox.ts b/frontend/src/api/inbox.ts index be2ca3b5..95e542d6 100644 --- a/frontend/src/api/inbox.ts +++ b/frontend/src/api/inbox.ts @@ -177,3 +177,14 @@ function deleteFromFolder( } } } + +export function* walkFolder(folder: Folder): Generator { + yield folder; + for (const child of folder.children) { + if (child.type === "directory") { + yield* walkFolder(child); + } else { + yield child; + } + } +} diff --git a/frontend/src/components/inbox/actions2/deleteFolders.tsx b/frontend/src/components/inbox/actions2/deleteFolders.tsx new file mode 100644 index 00000000..b5c8f2c9 --- /dev/null +++ b/frontend/src/components/inbox/actions2/deleteFolders.tsx @@ -0,0 +1,188 @@ +import { Trash2Icon } from "lucide-react"; +import { useMemo, useState } from "react"; +import { + Box, + Button, + DialogContent, + Divider, + Typography, + useTheme, +} from "@mui/material"; +import { useMutation, useQueries } from "@tanstack/react-query"; + +import { deleteFoldersMutationOptions, walkFolder } from "@/api/inbox"; +import { sessionQueryOptions } from "@/api/session"; +import { FolderStatusChip } from "@/components/common/chips"; +import { Dialog } from "@/components/common/dialogs"; +import { FolderTypeIcon } from "@/components/common/icons"; +import { Folder, Progress } from "@/pythonTypes"; + +/** + * A button component that allows bulk deletion of imported folders. + * + * Features: + * - Lists all imported folders (with `IMPORT_COMPLETED` status) under the given root folder. + * - Provides a confirmation dialog before deletion. + * - Supports shift+click to skip confirmation and delete immediately. + * + */ +export function DeleteImportedFoldersButton({ folder }: { folder: Folder }) { + const theme = useTheme(); + const [open, setOpen] = useState(false); + + /** Get all folders that have a session with + * `status.progress` equal to `Progress.IMPORT_COMPLETED`. + */ + const folders = useMemo(() => { + const fs = []; + for (const f of walkFolder(folder)) { + if (f.type === "file") continue; // skip files + if (f.full_path === folder.full_path) continue; // skip the root folder + fs.push(f); + } + return fs; + }, [folder]); + + const sessions = useQueries({ + queries: folders.map((f) => + sessionQueryOptions({ folderHash: f.hash, folderPath: f.full_path }) + ), + }); + + const importedFolders = useMemo(() => { + return folders.filter((f, i) => { + const session = sessions[i]; + return session.data?.status.progress === Progress.IMPORT_COMPLETED; + }); + }, [folders, sessions]); + + /** Delete folder mutation */ + const { mutateAsync: deleteFolders, isPending } = useMutation( + deleteFoldersMutationOptions + ); + + return ( + <> + + + setOpen(false)} + title="Delete all imported folders? " + title_icon={} + color="secondary" + > + + + Are you sure you want to delete all imported folders? This will + delete the following folders: + + + {importedFolders.map((f, i) => ( + + + + + {f.full_path} + + + + + ))} + {importedFolders.length === 0 && ( + + No imported folders to delete! + + )} + + + This action cannot be undone! All files inside the folders will + be permanently deleted. + + + + + + + + + + ); +} diff --git a/frontend/src/components/inbox/cards/inboxCard.tsx b/frontend/src/components/inbox/cards/inboxCard.tsx index 2b56653b..3a0a2783 100644 --- a/frontend/src/components/inbox/cards/inboxCard.tsx +++ b/frontend/src/components/inbox/cards/inboxCard.tsx @@ -12,6 +12,8 @@ import { } from "@/components/inbox/fileTree"; import { Folder } from "@/pythonTypes"; +import { DeleteImportedFoldersButton } from "../actions2/deleteFolders"; + export function InboxCard({ folder }: { folder: Folder }) { const config = useConfig(); @@ -158,6 +160,7 @@ export function InboxCard({ folder }: { folder: Folder }) { + {/* */} );