"use client"; import { ChevronRight, FileText, FolderOpen, Loader2, type LucideIcon, MoreHorizontal, Plus, Trash2, } from "lucide-react"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import { useCallback, useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, } from "@/components/ui/sidebar"; import { useIsMobile } from "@/hooks/use-mobile"; import { cn } from "@/lib/utils"; import { AllNotesSidebar } from "./all-notes-sidebar"; interface NoteAction { name: string; icon: string; onClick: () => void; } interface NoteItem { name: string; url: string; icon: LucideIcon; id?: number; search_space_id?: number; actions?: NoteAction[]; } interface NavNotesProps { notes: NoteItem[]; onAddNote?: () => void; defaultOpen?: boolean; searchSpaceId?: string; isSourcesExpanded?: boolean; } // Map of icon names to their components const actionIconMap: Record = { FileText, Trash2, MoreHorizontal, }; export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId, isSourcesExpanded = false, }: NavNotesProps) { const t = useTranslations("sidebar"); const router = useRouter(); const isMobile = useIsMobile(); const [isDeleting, setIsDeleting] = useState(null); const [isOpen, setIsOpen] = useState(defaultOpen); const [isAllNotesSidebarOpen, setIsAllNotesSidebarOpen] = useState(false); // Auto-collapse on smaller screens when Sources is expanded useEffect(() => { if (isSourcesExpanded && isMobile) { setIsOpen(false); } }, [isSourcesExpanded, isMobile]); // Handle note deletion with loading state const handleDeleteNote = useCallback(async (noteId: number, deleteAction: () => void) => { setIsDeleting(noteId); try { await deleteAction(); } finally { setIsDeleting(null); } }, []); // Handle note navigation const handleNoteClick = useCallback( (url: string) => { router.push(url); }, [router] ); return (
{t("notes") || "Notes"} {/* Action buttons - always visible on hover */}
{searchSpaceId && notes.length > 0 && ( )} {onAddNote && ( )}
{notes.length > 0 ? ( notes.map((note) => { const isDeletingNote = isDeleting === note.id; return ( {/* Main navigation button */} handleNoteClick(note.url)} disabled={isDeletingNote} className={cn( "pr-8", // Make room for the action button isDeletingNote && "opacity-50" )} > {note.name} {/* Actions dropdown - positioned absolutely */} {note.actions && note.actions.length > 0 && (
{note.actions.map((action, actionIndex) => { const ActionIcon = actionIconMap[action.icon] || FileText; const isDeleteAction = action.name.toLowerCase().includes("delete"); return ( { if (isDeleteAction) { handleDeleteNote(note.id || 0, action.onClick); } else { action.onClick(); } }} disabled={isDeletingNote} className={ isDeleteAction ? "text-destructive focus:text-destructive" : "" } > {isDeletingNote && isDeleteAction ? t("deleting") || "Deleting..." : action.name} ); })}
)}
); }) ) : ( {onAddNote ? ( {t("create_new_note") || "Create a new note"} ) : ( {t("no_notes") || "No notes yet"} )} )}
{/* All Notes Sheet */} {searchSpaceId && ( )}
); }