"use client"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { format } from "date-fns"; import { FileText, Loader2, MoreHorizontal, Plus, Search, Trash2, X } from "lucide-react"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import { useCallback, useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from "@/components/ui/sheet"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { useDebouncedValue } from "@/hooks/use-debounced-value"; import { documentsApiService } from "@/lib/apis/documents-api.service"; import { notesApiService } from "@/lib/apis/notes-api.service"; import { cn } from "@/lib/utils"; interface AllNotesSidebarProps { open: boolean; onOpenChange: (open: boolean) => void; searchSpaceId: string; onAddNote?: () => void; } export function AllNotesSidebar({ open, onOpenChange, searchSpaceId, onAddNote, }: AllNotesSidebarProps) { const t = useTranslations("sidebar"); const router = useRouter(); const queryClient = useQueryClient(); const [deletingNoteId, setDeletingNoteId] = useState(null); const [searchQuery, setSearchQuery] = useState(""); const debouncedSearchQuery = useDebouncedValue(searchQuery, 300); // Fetch all notes (when no search query) const { data: notesData, error: notesError, isLoading: isLoadingNotes, } = useQuery({ queryKey: ["all-notes", searchSpaceId], queryFn: () => notesApiService.getNotes({ search_space_id: Number(searchSpaceId), page_size: 1000, }), enabled: !!searchSpaceId && open && !debouncedSearchQuery, }); // Search notes (when there's a search query) const { data: searchData, error: searchError, isLoading: isSearching, } = useQuery({ queryKey: ["search-notes", searchSpaceId, debouncedSearchQuery], queryFn: () => documentsApiService.searchDocuments({ queryParams: { search_space_id: Number(searchSpaceId), document_types: ["NOTE"], title: debouncedSearchQuery, page_size: 100, }, }), enabled: !!searchSpaceId && open && !!debouncedSearchQuery, }); // Handle note navigation const handleNoteClick = useCallback( (noteId: number, noteSearchSpaceId: number) => { router.push(`/dashboard/${noteSearchSpaceId}/editor/${noteId}`); onOpenChange(false); }, [router, onOpenChange] ); // Handle note deletion const handleDeleteNote = useCallback( async (noteId: number, noteSearchSpaceId: number) => { setDeletingNoteId(noteId); try { await notesApiService.deleteNote({ search_space_id: noteSearchSpaceId, note_id: noteId, }); // Invalidate queries to refresh the list queryClient.invalidateQueries({ queryKey: ["all-notes", searchSpaceId] }); queryClient.invalidateQueries({ queryKey: ["notes", searchSpaceId] }); queryClient.invalidateQueries({ queryKey: ["search-notes", searchSpaceId] }); } catch (error) { console.error("Error deleting note:", error); } finally { setDeletingNoteId(null); } }, [queryClient, searchSpaceId] ); // Clear search const handleClearSearch = useCallback(() => { setSearchQuery(""); }, []); // Determine which data to show const isSearchMode = !!debouncedSearchQuery; const isLoading = isSearchMode ? isSearching : isLoadingNotes; const error = isSearchMode ? searchError : notesError; // Transform and sort notes data - handle both regular notes and search results const notes = useMemo(() => { let notesList: { id: number; title: string; search_space_id: number; created_at: string; updated_at?: string | null; }[]; if (isSearchMode && searchData?.items) { notesList = searchData.items.map((doc) => ({ id: doc.id, title: doc.title, search_space_id: doc.search_space_id, created_at: doc.created_at, updated_at: doc.updated_at, })); } else { notesList = notesData?.items ?? []; } // Sort notes by updated_at (most recent first), fallback to created_at return [...notesList].sort((a, b) => { const dateA = a.updated_at ? new Date(a.updated_at).getTime() : new Date(a.created_at).getTime(); const dateB = b.updated_at ? new Date(b.updated_at).getTime() : new Date(b.created_at).getTime(); return dateB - dateA; // Descending order (most recent first) }); }, [isSearchMode, searchData, notesData]); return ( {t("all_notes") || "All Notes"} {t("all_notes_description") || "Browse and manage all your notes"} {/* Search Input */}
setSearchQuery(e.target.value)} className="pl-9 pr-8 h-9" /> {searchQuery && ( )}
{isLoading ? (
) : error ? (
{t("error_loading_notes") || "Error loading notes"}
) : notes.length > 0 ? (
{notes.map((note) => { const isDeleting = deletingNoteId === note.id; return (
{/* Main clickable area for navigation */}

{t("created") || "Created"}:{" "} {format(new Date(note.created_at), "MMM d, yyyy 'at' h:mm a")}

{note.updated_at && (

{t("updated") || "Updated"}:{" "} {format(new Date(note.updated_at), "MMM d, yyyy 'at' h:mm a")}

)}
{/* Actions dropdown - separate from main click area */} handleDeleteNote(note.id, note.search_space_id)} className="text-destructive focus:text-destructive" > {t("delete") || "Delete"}
); })}
) : isSearchMode ? (

{t("no_results_found") || "No notes found"}

{t("try_different_search") || "Try a different search term"}

) : (

{t("no_notes") || "No notes yet"}

{onAddNote && ( )}
)}
{/* Footer with Add Note button */} {onAddNote && notes.length > 0 && (
)}
); }