"use client"; import { AlertCircle, Clock, Download, Eye, MoreHorizontal, Move, PenLine, Trash2, } from "lucide-react"; import React, { useCallback, useRef, useState } from "react"; import { useDrag } from "react-dnd"; import { getDocumentTypeIcon } from "@/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentTypeIcon"; import { ExportContextItems, ExportDropdownItems } from "@/components/shared/ExportMenuItems"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, } from "@/components/ui/context-menu"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Spinner } from "@/components/ui/spinner"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import type { DocumentTypeEnum } from "@/contracts/types/document.types"; import { cn } from "@/lib/utils"; import { DND_TYPES } from "./FolderNode"; const EDITABLE_DOCUMENT_TYPES = new Set(["FILE", "NOTE"]); export interface DocumentNodeDoc { id: number; title: string; document_type: string; folderId: number | null; status?: { state: string; reason?: string | null }; } interface DocumentNodeProps { doc: DocumentNodeDoc; depth: number; isMentioned: boolean; onToggleChatMention: (doc: DocumentNodeDoc, isMentioned: boolean) => void; onPreview: (doc: DocumentNodeDoc) => void; onEdit: (doc: DocumentNodeDoc) => void; onDelete: (doc: DocumentNodeDoc) => void; onMove: (doc: DocumentNodeDoc) => void; onExport?: (doc: DocumentNodeDoc, format: string) => void; contextMenuOpen?: boolean; onContextMenuOpenChange?: (open: boolean) => void; } export const DocumentNode = React.memo(function DocumentNode({ doc, depth, isMentioned, onToggleChatMention, onPreview, onEdit, onDelete, onMove, onExport, contextMenuOpen, onContextMenuOpenChange, }: DocumentNodeProps) { const statusState = doc.status?.state ?? "ready"; const isSelectable = statusState !== "pending" && statusState !== "processing"; const isEditable = EDITABLE_DOCUMENT_TYPES.has(doc.document_type) && statusState !== "pending" && statusState !== "processing"; const handleCheckChange = useCallback(() => { if (isSelectable) { onToggleChatMention(doc, isMentioned); } }, [doc, isMentioned, isSelectable, onToggleChatMention]); const [{ isDragging }, drag] = useDrag( () => ({ type: DND_TYPES.DOCUMENT, item: { id: doc.id }, collect: (monitor) => ({ isDragging: monitor.isDragging() }), }), [doc.id] ); const isProcessing = statusState === "pending" || statusState === "processing"; const [dropdownOpen, setDropdownOpen] = useState(false); const [exporting, setExporting] = useState(null); const rowRef = useRef(null); const handleExport = useCallback( (format: string) => { if (!onExport) return; setExporting(format); onExport(doc, format); setTimeout(() => setExporting(null), 2000); }, [doc, onExport] ); const attachRef = useCallback( (node: HTMLDivElement | null) => { (rowRef as React.MutableRefObject).current = node; drag(node); }, [drag] ); return ( {/* biome-ignore lint/a11y/useSemanticElements: contains nested interactive children (Checkbox) that render as e.stopPropagation()}> onPreview(doc)}> Open {isEditable && ( onEdit(doc)}> Edit )} onMove(doc)}> Move to... {onExport && ( Export )} onDelete(doc)} > Delete {contextMenuOpen && ( e.stopPropagation()}> onPreview(doc)}> Open {isEditable && ( onEdit(doc)}> Edit )} onMove(doc)}> Move to... {onExport && ( Export )} onDelete(doc)} > Delete )} ); });