From bbe3c0a5d63520e57bfb62c828b0f7a399468c29 Mon Sep 17 00:00:00 2001 From: Timothy Carambat <rambat1010@gmail.com> Date: Tue, 27 Feb 2024 11:53:42 -0800 Subject: [PATCH] Bulk remove files from file picker (#830) Do bulk deletion on backend --- .../Documents/Directory/index.jsx | 51 +++++++++++++++++-- .../Documents/UploadFile/index.jsx | 2 +- frontend/src/models/system.js | 12 +++++ server/endpoints/system.js | 15 ++++++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx index 158719445..95a1ecd07 100644 --- a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx @@ -3,6 +3,7 @@ import PreLoader from "@/components/Preloader"; import { memo, useEffect, useState } from "react"; import FolderRow from "./FolderRow"; import pluralize from "pluralize"; +import System from "@/models/system"; function Directory({ files, @@ -19,6 +20,40 @@ function Directory({ }) { const [amountSelected, setAmountSelected] = useState(0); + const deleteFiles = async (event) => { + event.stopPropagation(); + if ( + !window.confirm( + "Are you sure you want to delete these files?\nThis will remove the files from the system and remove them from any existing workspaces automatically.\nThis action is not reversible." + ) + ) { + return false; + } + + try { + const toRemove = []; + for (const itemId of Object.keys(selectedItems)) { + for (const folder of files.items) { + const foundItem = folder.items.find((file) => file.id === itemId); + if (foundItem) { + toRemove.push(`${folder.name}/${foundItem.name}`); + break; + } + } + } + setLoading(true); + setLoadingMessage(`Removing ${toRemove.length} documents. Please wait.`); + await System.deleteDocuments(toRemove); + await fetchKeys(true); + setSelectedItems({}); + } catch (error) { + console.error("Failed to delete the document:", error); + } finally { + setLoading(false); + setSelectedItems({}); + } + }; + const toggleSelection = (item) => { setSelectedItems((prevSelectedItems) => { const newSelectedItems = { ...prevSelectedItems }; @@ -119,18 +154,24 @@ function Directory({ </div> {amountSelected !== 0 && ( - <div className="absolute bottom-0 left-0 w-full flex justify-center items-center h-9 bg-white rounded-b-2xl"> - <div className="flex gap-x-5"> - <div + <div className="absolute bottom-0 left-0 w-full flex justify-between items-center h-9 bg-white rounded-b-2xl"> + <div className="flex gap-x-5 w-[80%] justify-center"> + <button onMouseEnter={() => setHighlightWorkspace(true)} onMouseLeave={() => setHighlightWorkspace(false)} onClick={moveToWorkspace} - className="text-sm font-semibold h-7 px-2.5 rounded-lg transition-all duration-300 hover:text-white hover:bg-neutral-800/80 cursor-pointer flex items-center" + className="border-none text-sm font-semibold h-7 px-2.5 rounded-lg hover:text-white hover:bg-neutral-800/80 flex items-center" > Move {amountSelected} {pluralize("file", amountSelected)} to workspace - </div> + </button> </div> + <button + onClick={deleteFiles} + className="border-none text-red-500/50 text-sm font-semibold h-7 px-2.5 rounded-lg hover:text-red-500/80 flex items-center" + > + Delete + </button> </div> )} </div> diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/UploadFile/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/UploadFile/index.jsx index a03cefa94..2ed911026 100644 --- a/frontend/src/components/Modals/MangeWorkspace/Documents/UploadFile/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/Documents/UploadFile/index.jsx @@ -35,7 +35,7 @@ export default function UploadFile({ workspace, fetchKeys, setLoading }) { const handleUploadSuccess = () => { fetchKeys(true); - showToast("File uploaded successfully", "success"); + showToast("File uploaded successfully", "success", { clear: true }); }; const handleUploadError = (message) => { diff --git a/frontend/src/models/system.js b/frontend/src/models/system.js index 70e756856..89deda75d 100644 --- a/frontend/src/models/system.js +++ b/frontend/src/models/system.js @@ -152,6 +152,18 @@ const System = { return false; }); }, + deleteDocuments: async (names = []) => { + return await fetch(`${API_BASE}/system/remove-documents`, { + method: "DELETE", + headers: baseHeaders(), + body: JSON.stringify({ names }), + }) + .then((res) => res.ok) + .catch((e) => { + console.error(e); + return false; + }); + }, deleteFolder: async (name) => { return await fetch(`${API_BASE}/system/remove-folder`, { method: "DELETE", diff --git a/server/endpoints/system.js b/server/endpoints/system.js index 11aeb8cbb..a36777c8f 100644 --- a/server/endpoints/system.js +++ b/server/endpoints/system.js @@ -260,6 +260,21 @@ function systemEndpoints(app) { } ); + app.delete( + "/system/remove-documents", + [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])], + async (request, response) => { + try { + const { names } = reqBody(request); + for await (const name of names) await purgeDocument(name); + response.sendStatus(200).end(); + } catch (e) { + console.log(e.message, e); + response.sendStatus(500).end(); + } + } + ); + app.delete( "/system/remove-folder", [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],