From 074088d3cbd2e987be3827ddaa6c12f95ec5c116 Mon Sep 17 00:00:00 2001 From: Sean Hatfield Date: Wed, 25 Sep 2024 13:04:42 -0700 Subject: [PATCH] Bulk document removal from workspace * wip improve remove document ux * fix border ui bugs when adding files to workspace * sort workspacedirectory put adding files at top * fix workspace file row ui shifting * fix selected items bug when adding another item with items already selected on workspace * fix tooltip * lint * refactor * fix bug where unadding single item while selected would stay selected --------- Co-authored-by: timothycarambat --- .../WorkspaceFileRow/index.jsx | 47 ++++- .../Documents/WorkspaceDirectory/index.jsx | 196 +++++++++++++++--- 2 files changed, 206 insertions(+), 37 deletions(-) diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx index 177d9b823..cc267170b 100644 --- a/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx @@ -19,8 +19,13 @@ export default function WorkspaceFileRow({ fetchKeys, hasChanges, movedItems, + selected, + toggleSelection, + disableSelection, + setSelectedItems, }) { - const onRemoveClick = async () => { + const onRemoveClick = async (e) => { + e.stopPropagation(); setLoading(true); try { @@ -33,24 +38,49 @@ export default function WorkspaceFileRow({ } catch (error) { console.error("Failed to remove document:", error); } - + setSelectedItems({}); setLoadingMessage(""); setLoading(false); }; + function toggleRowSelection(e) { + if (disableSelection) return; + e.stopPropagation(); + toggleSelection(); + } + + function handleRowSelection(e) { + e.stopPropagation(); + toggleSelection(); + } + const isMovedItem = movedItems?.some((movedItem) => movedItem.id === item.id); return (
+
+ {!disableSelection ? ( +
+ {selected &&
} +
+ ) : null} +

@@ -105,8 +135,9 @@ const PinItemToWorkspace = memo(({ workspace, docPath, item }) => { const [hover, setHover] = useState(false); const pinEvent = new CustomEvent("pinned_document"); - const updatePinStatus = async () => { + const updatePinStatus = async (e) => { try { + e.stopPropagation(); if (!pinned) window.dispatchEvent(pinEvent); const success = await Workspace.setPinForDocument( workspace.slug, diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx index 4ec3a86ed..e9864f12c 100644 --- a/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx @@ -7,6 +7,7 @@ import { Eye, PushPin } from "@phosphor-icons/react"; import { SEEN_DOC_PIN_ALERT, SEEN_WATCH_ALERT } from "@/utils/constants"; import paths from "@/utils/paths"; import { Link } from "react-router-dom"; +import Workspace from "@/models/workspace"; function WorkspaceDirectory({ workspace, @@ -22,6 +23,66 @@ function WorkspaceDirectory({ embeddingCosts, movedItems, }) { + const [selectedItems, setSelectedItems] = useState({}); + + const toggleSelection = (item) => { + setSelectedItems((prevSelectedItems) => { + const newSelectedItems = { ...prevSelectedItems }; + if (newSelectedItems[item.id]) { + delete newSelectedItems[item.id]; + } else { + newSelectedItems[item.id] = true; + } + return newSelectedItems; + }); + }; + + const toggleSelectAll = () => { + const allItems = files.items.flatMap((folder) => folder.items); + const allSelected = allItems.every((item) => selectedItems[item.id]); + if (allSelected) { + setSelectedItems({}); + } else { + const newSelectedItems = {}; + allItems.forEach((item) => { + newSelectedItems[item.id] = true; + }); + setSelectedItems(newSelectedItems); + } + }; + + const removeSelectedItems = async () => { + setLoading(true); + setLoadingMessage("Removing selected files from workspace"); + + const itemsToRemove = Object.keys(selectedItems).map((itemId) => { + const folder = files.items.find((f) => + f.items.some((i) => i.id === itemId) + ); + const item = folder.items.find((i) => i.id === itemId); + return `${folder.name}/${item.name}`; + }); + + try { + await Workspace.modifyEmbeddings(workspace.slug, { + adds: [], + deletes: itemsToRemove, + }); + await fetchKeys(true); + setSelectedItems({}); + } catch (error) { + console.error("Failed to remove documents:", error); + } + + setLoadingMessage(""); + setLoading(false); + }; + + const handleSaveChanges = (e) => { + setSelectedItems({}); + saveChanges(e); + }; + if (loading) { return (

@@ -31,11 +92,14 @@ function WorkspaceDirectory({
-
-

Name

+
+
+
+

Name

+

-
+

{loadingMessage} @@ -54,24 +118,50 @@ function WorkspaceDirectory({ {workspace.name}

-
-
-

Name

-

-

-
- {Object.values(files.items).some( - (folder) => folder.items.length > 0 - ) || movedItems.length > 0 ? ( - <> - {files.items.map((folder) => - folder.items.map((item, index) => ( +
+
+
+
+
+ {!hasChanges && + files.items.some((folder) => folder.items.length > 0) ? ( +
sum + folder.items.length, + 0 + ) + } + tabIndex={0} + onClick={toggleSelectAll} + > + {Object.keys(selectedItems).length === + files.items.reduce( + (sum, folder) => sum + folder.items.length, + 0 + ) &&
} +
+ ) : ( +
+ )} +

Name

+
+

+

+
+ {files.items.some((folder) => folder.items.length > 0) || + movedItems.length > 0 ? ( + + {({ item, folder }) => ( toggleSelection(item)} + disableSelection={hasChanges} + setSelectedItems={setSelectedItems} /> - )) - )} - - ) : ( -
-

- No Documents -

+ )} + + ) : ( +
+

+ No Documents +

+
+ )} +
+ {Object.keys(selectedItems).length > 0 && !hasChanges && ( +
+
+
+ + +
+
)}
@@ -111,7 +231,7 @@ function WorkspaceDirectory({