mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-08 22:22:17 +02:00
feat(sidebar): enhance document management UI and improve layout structure
- Updated the FolderTreeView component to clarify upload instructions. - Introduced a new renderDocumentTree function in DocumentsSidebar for better organization and readability. - Refactored the Sidebar component to utilize SidebarSection for improved layout consistency. - Added contentClassName prop to SidebarSection for flexible styling options.
This commit is contained in:
parent
f11cc3868c
commit
62c0573b1c
4 changed files with 126 additions and 72 deletions
|
|
@ -336,7 +336,7 @@ export function FolderTreeView({
|
|||
<div className="flex flex-1 flex-col items-center justify-center gap-1 px-4 py-12 text-muted-foreground select-none">
|
||||
<p className="text-sm font-medium">No documents found</p>
|
||||
<p className="text-xs text-muted-foreground/70">
|
||||
Use the upload button or connect a source above
|
||||
Use the plus menu to upload files or manage connectors
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1097,6 +1097,81 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
currentFilesystemTab === "cloud" &&
|
||||
(zeroFoldersResult.type !== "complete" || zeroAllDocsResult.type !== "complete");
|
||||
|
||||
const renderDocumentTree = ({
|
||||
documents = searchFilteredDocuments,
|
||||
activeTypesForTree = activeTypes,
|
||||
searchQuery = debouncedSearch.trim() || undefined,
|
||||
}: {
|
||||
documents?: DocumentNodeDoc[];
|
||||
activeTypesForTree?: DocumentTypeEnum[];
|
||||
searchQuery?: string;
|
||||
} = {}) => (
|
||||
<div className="relative flex-1 min-h-0 overflow-auto">
|
||||
{deletableSelectedIds.length > 0 && (
|
||||
<div className="absolute inset-x-0 top-0 z-10 flex items-center justify-center px-4 py-1.5 animate-in fade-in duration-150 pointer-events-none">
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={() => setBulkDeleteConfirmOpen(true)}
|
||||
className="pointer-events-auto h-auto gap-1.5 px-3 py-1 text-xs shadow-lg"
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
Delete {deletableSelectedIds.length}{" "}
|
||||
{deletableSelectedIds.length === 1 ? "item" : "items"}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showCloudSkeleton ? (
|
||||
<CloudDocumentsSkeleton />
|
||||
) : (
|
||||
<FolderTreeView
|
||||
folders={treeFolders}
|
||||
documents={documents}
|
||||
expandedIds={expandedIds}
|
||||
onToggleExpand={toggleFolderExpand}
|
||||
mentionedDocKeys={mentionedDocKeys}
|
||||
onToggleChatMention={handleToggleChatMention}
|
||||
onToggleFolderSelect={handleToggleFolderSelect}
|
||||
onRenameFolder={handleRenameFolder}
|
||||
onDeleteFolder={handleDeleteFolder}
|
||||
onMoveFolder={handleMoveFolder}
|
||||
onCreateFolder={handleCreateFolder}
|
||||
searchQuery={searchQuery}
|
||||
onPreviewDocument={(doc) => {
|
||||
if (openMemoryDocument(doc)) return;
|
||||
openEditorPanel({
|
||||
documentId: doc.id,
|
||||
searchSpaceId,
|
||||
title: doc.title,
|
||||
});
|
||||
}}
|
||||
onEditDocument={(doc) => {
|
||||
if (openMemoryDocument(doc)) return;
|
||||
openEditorPanel({
|
||||
documentId: doc.id,
|
||||
searchSpaceId,
|
||||
title: doc.title,
|
||||
});
|
||||
}}
|
||||
onDeleteDocument={(doc) => handleDeleteDocument(doc.id)}
|
||||
onMoveDocument={handleMoveDocument}
|
||||
onResetDocument={handleResetMemoryDocument}
|
||||
onExportDocument={handleExportDocument}
|
||||
onVersionHistory={(doc) => setVersionDocId(doc.id)}
|
||||
activeTypes={activeTypesForTree}
|
||||
onDropIntoFolder={handleDropIntoFolder}
|
||||
onReorderFolder={handleReorderFolder}
|
||||
watchedFolderIds={watchedFolderIds}
|
||||
onRescanFolder={handleRescanFolder}
|
||||
onStopWatchingFolder={handleStopWatching}
|
||||
onExportFolder={handleExportFolder}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const cloudContent = (
|
||||
<>
|
||||
{isElectron && (
|
||||
|
|
@ -1124,70 +1199,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className="relative flex-1 min-h-0 overflow-auto">
|
||||
{deletableSelectedIds.length > 0 && (
|
||||
<div className="absolute inset-x-0 top-0 z-10 flex items-center justify-center px-4 py-1.5 animate-in fade-in duration-150 pointer-events-none">
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={() => setBulkDeleteConfirmOpen(true)}
|
||||
className="pointer-events-auto h-auto gap-1.5 px-3 py-1 text-xs shadow-lg"
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
Delete {deletableSelectedIds.length}{" "}
|
||||
{deletableSelectedIds.length === 1 ? "item" : "items"}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showCloudSkeleton ? (
|
||||
<CloudDocumentsSkeleton />
|
||||
) : (
|
||||
<FolderTreeView
|
||||
folders={treeFolders}
|
||||
documents={searchFilteredDocuments}
|
||||
expandedIds={expandedIds}
|
||||
onToggleExpand={toggleFolderExpand}
|
||||
mentionedDocKeys={mentionedDocKeys}
|
||||
onToggleChatMention={handleToggleChatMention}
|
||||
onToggleFolderSelect={handleToggleFolderSelect}
|
||||
onRenameFolder={handleRenameFolder}
|
||||
onDeleteFolder={handleDeleteFolder}
|
||||
onMoveFolder={handleMoveFolder}
|
||||
onCreateFolder={handleCreateFolder}
|
||||
searchQuery={debouncedSearch.trim() || undefined}
|
||||
onPreviewDocument={(doc) => {
|
||||
if (openMemoryDocument(doc)) return;
|
||||
openEditorPanel({
|
||||
documentId: doc.id,
|
||||
searchSpaceId,
|
||||
title: doc.title,
|
||||
});
|
||||
}}
|
||||
onEditDocument={(doc) => {
|
||||
if (openMemoryDocument(doc)) return;
|
||||
openEditorPanel({
|
||||
documentId: doc.id,
|
||||
searchSpaceId,
|
||||
title: doc.title,
|
||||
});
|
||||
}}
|
||||
onDeleteDocument={(doc) => handleDeleteDocument(doc.id)}
|
||||
onMoveDocument={handleMoveDocument}
|
||||
onResetDocument={handleResetMemoryDocument}
|
||||
onExportDocument={handleExportDocument}
|
||||
onVersionHistory={(doc) => setVersionDocId(doc.id)}
|
||||
activeTypes={activeTypes}
|
||||
onDropIntoFolder={handleDropIntoFolder}
|
||||
onReorderFolder={handleReorderFolder}
|
||||
watchedFolderIds={watchedFolderIds}
|
||||
onRescanFolder={handleRescanFolder}
|
||||
onStopWatchingFolder={handleStopWatching}
|
||||
onExportFolder={handleExportFolder}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{renderDocumentTree()}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
@ -1459,8 +1471,12 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
|
||||
if (embedded) {
|
||||
return (
|
||||
<div className="flex h-full flex-col bg-sidebar text-sidebar-foreground">
|
||||
{documentsContent}
|
||||
<div className="flex h-full min-h-0 flex-col text-sidebar-foreground">
|
||||
{renderDocumentTree({
|
||||
documents: treeDocumentsWithMemory,
|
||||
activeTypesForTree: [],
|
||||
searchQuery: undefined,
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1830,8 +1846,37 @@ function AnonymousDocumentsSidebar({
|
|||
|
||||
if (embedded) {
|
||||
return (
|
||||
<div className="flex h-full flex-col bg-sidebar text-sidebar-foreground">
|
||||
{documentsContent}
|
||||
<div className="flex h-full min-h-0 flex-col text-sidebar-foreground">
|
||||
<FolderTreeView
|
||||
folders={[]}
|
||||
documents={treeDocuments}
|
||||
expandedIds={new Set()}
|
||||
onToggleExpand={() => {}}
|
||||
mentionedDocKeys={mentionedDocKeys}
|
||||
onToggleChatMention={handleToggleChatMention}
|
||||
onToggleFolderSelect={() => {}}
|
||||
onRenameFolder={() => gate("rename folders")}
|
||||
onDeleteFolder={() => gate("delete folders")}
|
||||
onMoveFolder={() => gate("organize folders")}
|
||||
onCreateFolder={() => gate("create folders")}
|
||||
onPreviewDocument={() => gate("preview documents")}
|
||||
onEditDocument={() => gate("edit documents")}
|
||||
onDeleteDocument={async () => {
|
||||
handleRemoveDoc();
|
||||
setSidebarDocs((prev) => prev.filter((d) => d.kind !== "doc" || d.id !== -1));
|
||||
return true;
|
||||
}}
|
||||
onMoveDocument={() => gate("organize documents")}
|
||||
onExportDocument={() => gate("export documents")}
|
||||
onVersionHistory={() => gate("view version history")}
|
||||
activeTypes={[]}
|
||||
onDropIntoFolder={async () => gate("organize documents")}
|
||||
onReorderFolder={async () => gate("organize folders")}
|
||||
watchedFolderIds={new Set()}
|
||||
onRescanFolder={() => gate("watch local folders")}
|
||||
onStopWatchingFolder={() => gate("watch local folders")}
|
||||
onExportFolder={() => gate("export folders")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -336,13 +336,18 @@ export function Sidebar({
|
|||
)}
|
||||
</SidebarSection>
|
||||
{documentsPanel?.open ? (
|
||||
<div className="min-h-0 flex-1 border-t">
|
||||
<SidebarSection
|
||||
title={documentsItem?.title ?? "Documents"}
|
||||
defaultOpen={true}
|
||||
fillHeight
|
||||
contentClassName="px-0"
|
||||
>
|
||||
<DocumentsSidebar
|
||||
open={documentsPanel.open}
|
||||
onOpenChange={documentsPanel.onOpenChange}
|
||||
embedded
|
||||
/>
|
||||
</div>
|
||||
</SidebarSection>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ interface SidebarSectionProps {
|
|||
alwaysShowAction?: boolean;
|
||||
persistentAction?: React.ReactNode;
|
||||
className?: string;
|
||||
contentClassName?: string;
|
||||
fillHeight?: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -24,6 +25,7 @@ export function SidebarSection({
|
|||
alwaysShowAction = false,
|
||||
persistentAction,
|
||||
className,
|
||||
contentClassName,
|
||||
fillHeight = false,
|
||||
}: SidebarSectionProps) {
|
||||
const [isOpen, setIsOpen] = useState(defaultOpen);
|
||||
|
|
@ -69,7 +71,9 @@ export function SidebarSection({
|
|||
</div>
|
||||
|
||||
<CollapsibleContent className={cn("overflow-hidden flex-1 flex flex-col min-h-0")}>
|
||||
<div className={cn("px-2 flex-1 flex flex-col min-h-0 overflow-hidden")}>{children}</div>
|
||||
<div className={cn("px-2 flex-1 flex flex-col min-h-0 overflow-hidden", contentClassName)}>
|
||||
{children}
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue