mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-08 22:22:17 +02:00
refactor(documents): remove unused document editing functionality and streamline components
- Eliminated references to document editing in DocumentNode, FolderTreeView, and DocumentsSidebar for a cleaner interface. - Updated DocumentNode to enhance accessibility with keyboard interactions and improved click handling. - Adjusted layout in AllChatsSidebar and MobileSidebar for better user experience and consistency across components.
This commit is contained in:
parent
556f02c5be
commit
4088289f95
7 changed files with 66 additions and 83 deletions
|
|
@ -1,5 +1,4 @@
|
|||
const STORE_KEY = 'activeWorkspaceId';
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let store: any = null;
|
||||
|
||||
async function getStore() {
|
||||
|
|
@ -12,7 +11,6 @@ async function getStore() {
|
|||
// One-time migration from the legacy `active-search-space` store so the
|
||||
// user's last-selected workspace survives the rename.
|
||||
if (store.get(STORE_KEY) == null) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const legacy: any = new Store({
|
||||
name: 'active-search-space',
|
||||
defaults: { activeSearchSpaceId: null as string | null },
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ export const globalDocumentsQueryParamsAtom = atom<GetDocumentsRequest["queryPar
|
|||
page: 0,
|
||||
});
|
||||
|
||||
export const documentsSidebarOpenAtom = atom(false);
|
||||
|
||||
export interface AgentCreatedDocument {
|
||||
id: number;
|
||||
title: string;
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@ import {
|
|||
AlertCircle,
|
||||
Clock,
|
||||
Download,
|
||||
Eye,
|
||||
History,
|
||||
MoreHorizontal,
|
||||
Move,
|
||||
Pencil,
|
||||
RotateCcw,
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
|
|
@ -44,8 +42,6 @@ import { SidebarListItem } from "../layout/ui/sidebar/SidebarListItem";
|
|||
import { DND_TYPES } from "./FolderNode";
|
||||
import { isVersionableType } from "./version-history";
|
||||
|
||||
const EDITABLE_DOCUMENT_TYPES = new Set(["FILE", "NOTE"]);
|
||||
|
||||
export interface DocumentNodeDoc {
|
||||
id: number;
|
||||
title: string;
|
||||
|
|
@ -60,7 +56,6 @@ interface DocumentNodeProps {
|
|||
isMentioned: boolean;
|
||||
onToggleChatMention: (doc: DocumentNodeDoc, isMentioned: boolean) => void;
|
||||
onPreview: (doc: DocumentNodeDoc) => void;
|
||||
onEdit: (doc: DocumentNodeDoc) => void;
|
||||
onDelete: (doc: DocumentNodeDoc) => void;
|
||||
onMove: (doc: DocumentNodeDoc) => void;
|
||||
onReset?: (doc: DocumentNodeDoc) => void;
|
||||
|
|
@ -69,7 +64,6 @@ interface DocumentNodeProps {
|
|||
canDelete?: boolean;
|
||||
canMove?: boolean;
|
||||
canMention?: boolean;
|
||||
canEdit?: boolean;
|
||||
contextMenuOpen?: boolean;
|
||||
onContextMenuOpenChange?: (open: boolean) => void;
|
||||
}
|
||||
|
|
@ -80,7 +74,6 @@ export const DocumentNode = React.memo(function DocumentNode({
|
|||
isMentioned,
|
||||
onToggleChatMention,
|
||||
onPreview,
|
||||
onEdit,
|
||||
onDelete,
|
||||
onMove,
|
||||
onReset,
|
||||
|
|
@ -89,7 +82,6 @@ export const DocumentNode = React.memo(function DocumentNode({
|
|||
canDelete = true,
|
||||
canMove = true,
|
||||
canMention = true,
|
||||
canEdit = true,
|
||||
contextMenuOpen,
|
||||
onContextMenuOpenChange,
|
||||
}: DocumentNodeProps) {
|
||||
|
|
@ -100,10 +92,6 @@ export const DocumentNode = React.memo(function DocumentNode({
|
|||
const isMemoryDocument =
|
||||
doc.document_type === "USER_MEMORY" || doc.document_type === "TEAM_MEMORY";
|
||||
const isSelectable = canMention && !isUnavailable;
|
||||
const isEditable =
|
||||
canEdit &&
|
||||
(isMemoryDocument || EDITABLE_DOCUMENT_TYPES.has(doc.document_type)) &&
|
||||
!isUnavailable;
|
||||
|
||||
const handleCheckChange = useCallback(() => {
|
||||
if (isSelectable) {
|
||||
|
|
@ -112,12 +100,20 @@ export const DocumentNode = React.memo(function DocumentNode({
|
|||
}, [doc, isMentioned, isSelectable, onToggleChatMention]);
|
||||
|
||||
const handlePrimaryClick = useCallback(() => {
|
||||
if (canMention) {
|
||||
handleCheckChange();
|
||||
return;
|
||||
}
|
||||
if (isUnavailable) return;
|
||||
onPreview(doc);
|
||||
}, [canMention, doc, handleCheckChange, onPreview]);
|
||||
}, [doc, isUnavailable, onPreview]);
|
||||
|
||||
const handlePrimaryKeyDown = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.currentTarget !== event.target) return;
|
||||
if (event.key !== "Enter" && event.key !== " ") return;
|
||||
|
||||
event.preventDefault();
|
||||
handlePrimaryClick();
|
||||
},
|
||||
[handlePrimaryClick]
|
||||
);
|
||||
|
||||
const [{ isDragging }, drag] = useDrag(
|
||||
() => ({
|
||||
|
|
@ -172,6 +168,11 @@ export const DocumentNode = React.memo(function DocumentNode({
|
|||
dragging={isDragging}
|
||||
className="gap-2.5 px-1"
|
||||
style={{ paddingLeft: `${depth * 16 + 4}px` }}
|
||||
role="button"
|
||||
tabIndex={isUnavailable ? -1 : 0}
|
||||
aria-disabled={isUnavailable}
|
||||
onClick={handlePrimaryClick}
|
||||
onKeyDown={handlePrimaryKeyDown}
|
||||
>
|
||||
{(() => {
|
||||
if (statusState === "pending") {
|
||||
|
|
@ -248,17 +249,11 @@ export const DocumentNode = React.memo(function DocumentNode({
|
|||
onOpenChange={handleTitleTooltipOpenChange}
|
||||
>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
aria-disabled={canMention ? !isSelectable : false}
|
||||
onClick={handlePrimaryClick}
|
||||
className="h-full min-w-0 flex-1 justify-start bg-transparent px-0 py-0 text-left font-normal text-inherit hover:bg-transparent hover:text-inherit"
|
||||
>
|
||||
<span className="flex h-full min-w-0 flex-1 items-center text-left font-normal text-inherit">
|
||||
<span ref={titleRef} className="min-w-0 flex-1 truncate">
|
||||
{doc.title}
|
||||
</span>
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="max-w-xs break-words">
|
||||
{doc.title}
|
||||
|
|
@ -304,16 +299,6 @@ export const DocumentNode = React.memo(function DocumentNode({
|
|||
className="w-40"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<DropdownMenuItem onClick={() => onPreview(doc)} disabled={isUnavailable}>
|
||||
<Eye className="mr-2 h-4 w-4" />
|
||||
Open
|
||||
</DropdownMenuItem>
|
||||
{isEditable && (
|
||||
<DropdownMenuItem onClick={() => onEdit(doc)}>
|
||||
<Pencil className="mr-2 h-4 w-4" />
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canMove && (
|
||||
<DropdownMenuItem onClick={() => onMove(doc)}>
|
||||
<Move className="mr-2 h-4 w-4" />
|
||||
|
|
@ -362,16 +347,6 @@ export const DocumentNode = React.memo(function DocumentNode({
|
|||
|
||||
{contextMenuOpen && (
|
||||
<ContextMenuContent className="w-40" onClick={(e) => e.stopPropagation()}>
|
||||
<ContextMenuItem onClick={() => onPreview(doc)} disabled={isUnavailable}>
|
||||
<Eye className="mr-2 h-4 w-4" />
|
||||
Open
|
||||
</ContextMenuItem>
|
||||
{isEditable && (
|
||||
<ContextMenuItem onClick={() => onEdit(doc)}>
|
||||
<Pencil className="mr-2 h-4 w-4" />
|
||||
Edit
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
{canMove && (
|
||||
<ContextMenuItem onClick={() => onMove(doc)}>
|
||||
<Move className="mr-2 h-4 w-4" />
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ interface FolderTreeViewProps {
|
|||
onMoveFolder: (folder: FolderDisplay) => void;
|
||||
onCreateFolder: (parentId: number | null) => void;
|
||||
onPreviewDocument: (doc: DocumentNodeDoc) => void;
|
||||
onEditDocument: (doc: DocumentNodeDoc) => void;
|
||||
onDeleteDocument: (doc: DocumentNodeDoc) => void;
|
||||
onMoveDocument: (doc: DocumentNodeDoc) => void;
|
||||
onResetDocument?: (doc: DocumentNodeDoc) => void;
|
||||
|
|
@ -72,7 +71,6 @@ export function FolderTreeView({
|
|||
onMoveFolder,
|
||||
onCreateFolder,
|
||||
onPreviewDocument,
|
||||
onEditDocument,
|
||||
onDeleteDocument,
|
||||
onMoveDocument,
|
||||
onResetDocument,
|
||||
|
|
@ -215,7 +213,6 @@ export function FolderTreeView({
|
|||
isMentioned={!isMemoryDocument && mentionedDocKeys.has(getMentionDocKey(d))}
|
||||
onToggleChatMention={onToggleChatMention}
|
||||
onPreview={onPreviewDocument}
|
||||
onEdit={onEditDocument}
|
||||
onDelete={onDeleteDocument}
|
||||
onMove={onMoveDocument}
|
||||
onReset={onResetDocument}
|
||||
|
|
@ -224,7 +221,6 @@ export function FolderTreeView({
|
|||
canDelete={!isMemoryDocument}
|
||||
canMove={!isMemoryDocument}
|
||||
canMention={!isMemoryDocument}
|
||||
canEdit
|
||||
contextMenuOpen={openContextMenuId === `doc-${d.id}`}
|
||||
onContextMenuOpenChange={(open) => setOpenContextMenuId(open ? `doc-${d.id}` : null)}
|
||||
/>
|
||||
|
|
@ -233,7 +229,6 @@ export function FolderTreeView({
|
|||
[
|
||||
mentionedDocKeys,
|
||||
onDeleteDocument,
|
||||
onEditDocument,
|
||||
onExportDocument,
|
||||
onMoveDocument,
|
||||
onPreviewDocument,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import {
|
|||
RotateCcwIcon,
|
||||
Search,
|
||||
Trash2,
|
||||
Users,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
|
|
@ -19,6 +18,7 @@ import { useTranslations } from "next-intl";
|
|||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { removeChatTabAtom } from "@/atoms/tabs/tabs.atom";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -301,7 +301,7 @@ function AllChatsContent({ workspaceId, className }: AllChatsContentProps) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden p-1.5">
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden px-3 py-1.5">
|
||||
{isLoading ? (
|
||||
<div className="space-y-1">
|
||||
{[75, 90, 55, 80, 65, 85].map((titleWidth) => (
|
||||
|
|
@ -319,15 +319,21 @@ function AllChatsContent({ workspaceId, className }: AllChatsContentProps) {
|
|||
{t("error_loading_chats") || "Error loading chats"}
|
||||
</div>
|
||||
) : threads.length > 0 ? (
|
||||
<div className="space-y-1">
|
||||
{threads.map((thread) => {
|
||||
<div>
|
||||
{threads.map((thread, index) => {
|
||||
const isDeleting = deletingThreadId === thread.id;
|
||||
const isArchiving = archivingThreadId === thread.id;
|
||||
const isBusy = isDeleting || isArchiving;
|
||||
const isActive = currentChatId === thread.id;
|
||||
|
||||
return (
|
||||
<div key={thread.id} className="group/item relative w-full">
|
||||
<div
|
||||
key={thread.id}
|
||||
className={cn(
|
||||
"group/item relative w-full hover:border-t-transparent [&:hover+div]:border-t-transparent",
|
||||
index > 0 && "border-t border-border/60"
|
||||
)}
|
||||
>
|
||||
{isMobile ? (
|
||||
<Button
|
||||
type="button"
|
||||
|
|
@ -349,7 +355,7 @@ function AllChatsContent({ workspaceId, className }: AllChatsContentProps) {
|
|||
"h-auto w-full justify-start gap-2.5 overflow-hidden px-3 py-2.5 text-left text-base font-normal",
|
||||
"group-hover/item:bg-accent group-hover/item:text-accent-foreground",
|
||||
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
||||
thread.visibility === "SEARCH_SPACE" && "pr-10",
|
||||
thread.visibility === "SEARCH_SPACE" && "pr-16",
|
||||
isActive && "bg-accent text-accent-foreground",
|
||||
isBusy && "opacity-50 pointer-events-none"
|
||||
)}
|
||||
|
|
@ -370,7 +376,7 @@ function AllChatsContent({ workspaceId, className }: AllChatsContentProps) {
|
|||
"h-auto w-full justify-start gap-2.5 overflow-hidden px-3 py-2.5 text-left text-base font-normal",
|
||||
"group-hover/item:bg-accent group-hover/item:text-accent-foreground",
|
||||
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
||||
thread.visibility === "SEARCH_SPACE" && "pr-10",
|
||||
thread.visibility === "SEARCH_SPACE" && "pr-16",
|
||||
isActive && "bg-accent text-accent-foreground",
|
||||
isBusy && "opacity-50 pointer-events-none"
|
||||
)}
|
||||
|
|
@ -401,18 +407,20 @@ function AllChatsContent({ workspaceId, className }: AllChatsContentProps) {
|
|||
: "opacity-0 group-hover/item:opacity-100"
|
||||
)}
|
||||
>
|
||||
<div className="relative flex h-7 w-7 items-center justify-center">
|
||||
<div className="relative flex h-7 w-14 items-center justify-end">
|
||||
{thread.visibility === "SEARCH_SPACE" ? (
|
||||
<Users
|
||||
aria-label={t("shared_chat") || "Shared chat"}
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn(
|
||||
"absolute left-1/2 top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 text-muted-foreground/50",
|
||||
"absolute right-0 h-5 shrink-0 rounded-sm border-0 bg-popover-foreground/10 px-1.5 text-[11px] text-popover-foreground transition-opacity hover:bg-popover-foreground/10",
|
||||
!isMobile &&
|
||||
(openDropdownId === thread.id
|
||||
? "opacity-0"
|
||||
: "opacity-100 group-hover/item:opacity-0")
|
||||
)}
|
||||
/>
|
||||
>
|
||||
Shared
|
||||
</Badge>
|
||||
) : null}
|
||||
<DropdownMenu
|
||||
open={openDropdownId === thread.id}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,10 @@ import { useTranslations } from "next-intl";
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { makeFolderMention, mentionedDocumentsAtom } from "@/atoms/chat/mentioned-documents.atom";
|
||||
import { connectorDialogOpenAtom } from "@/atoms/connector-dialog/connector-dialog.atoms";
|
||||
import { deleteDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms";
|
||||
import { expandedFolderIdsAtom } from "@/atoms/documents/folder.atoms";
|
||||
import { agentCreatedDocumentsAtom } from "@/atoms/documents/ui.atoms";
|
||||
import { openEditorPanelAtom } from "@/atoms/editor/editor-panel.atom";
|
||||
import {
|
||||
folderWatchDialogOpenAtom,
|
||||
folderWatchInitialFolderAtom,
|
||||
} from "@/atoms/folder-sync/folder-sync.atoms";
|
||||
import { workspacesAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { CreateFolderDialog } from "@/components/documents/CreateFolderDialog";
|
||||
import type { DocumentNodeDoc } from "@/components/documents/DocumentNode";
|
||||
import { getDocumentTypeIcon } from "@/components/documents/DocumentTypeIcon";
|
||||
|
|
@ -57,7 +51,6 @@ import type { DocumentTypeEnum } from "@/contracts/types/document.types";
|
|||
import { useElectronAPI, usePlatform } from "@/hooks/use-platform";
|
||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||
import { foldersApiService } from "@/lib/apis/folders-api.service";
|
||||
import { workspacesApiService } from "@/lib/apis/workspaces-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-fetch";
|
||||
import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
|
||||
import { getDocumentTypeLabel } from "@/lib/documents/document-type-labels";
|
||||
|
|
@ -242,7 +235,6 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
const electronAPI = desktopFeaturesEnabled ? platformElectronAPI : null;
|
||||
const { etlService } = useRuntimeConfig();
|
||||
const workspaceId = getWorkspaceIdNumber(params) ?? 0;
|
||||
const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom);
|
||||
const openEditorPanel = useSetAtom(openEditorPanelAtom);
|
||||
|
||||
const [activeTypes, setActiveTypes] = useState<DocumentTypeEnum[]>([]);
|
||||
|
|
@ -1024,14 +1016,6 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
title: doc.title,
|
||||
});
|
||||
}}
|
||||
onEditDocument={(doc) => {
|
||||
if (openMemoryDocument(doc)) return;
|
||||
openEditorPanel({
|
||||
documentId: doc.id,
|
||||
workspaceId: workspaceId,
|
||||
title: doc.title,
|
||||
});
|
||||
}}
|
||||
onDeleteDocument={(doc) => handleDeleteDocument(doc.id)}
|
||||
onMoveDocument={handleMoveDocument}
|
||||
onResetDocument={handleResetMemoryDocument}
|
||||
|
|
@ -1251,7 +1235,6 @@ function AnonymousDocumentsSidebar({ embedded = false }: DocumentsSidebarProps)
|
|||
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));
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
|
|||
import type { ChatItem, NavItem, PageUsage, User, Workspace } from "../../types/layout.types";
|
||||
import { WorkspaceAvatar } from "../icon-rail/WorkspaceAvatar";
|
||||
import { Sidebar } from "./Sidebar";
|
||||
import { SidebarUserProfile } from "./SidebarUserProfile";
|
||||
|
||||
interface MobileSidebarProps {
|
||||
isOpen: boolean;
|
||||
|
|
@ -137,6 +138,30 @@ export function MobileSidebar({
|
|||
</Button>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
<SidebarUserProfile
|
||||
user={user}
|
||||
onUserSettings={
|
||||
onUserSettings
|
||||
? () => {
|
||||
onOpenChange(false);
|
||||
onUserSettings();
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onAnnouncements={
|
||||
onAnnouncements
|
||||
? () => {
|
||||
onOpenChange(false);
|
||||
onAnnouncements();
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
announcementUnreadCount={announcementUnreadCount}
|
||||
onLogout={onLogout}
|
||||
isCollapsed
|
||||
theme={theme}
|
||||
setTheme={setTheme}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Sidebar Content - right side */}
|
||||
|
|
@ -209,6 +234,7 @@ export function MobileSidebar({
|
|||
className="w-full border-none"
|
||||
isLoadingChats={isLoadingChats}
|
||||
disableTooltips
|
||||
renderUserProfile={false}
|
||||
/>
|
||||
</div>
|
||||
</SheetContent>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue