"use client"; import { ChevronRight, FolderOpen, Loader2, type LucideIcon, MessageCircleMore, MoreHorizontal, RefreshCw, Trash2, } from "lucide-react"; import { usePathname, 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 { AllChatsSidebar } from "./all-chats-sidebar"; interface ChatAction { name: string; icon: string; onClick: () => void; } interface ChatItem { name: string; url: string; icon: LucideIcon; id?: number; search_space_id?: number; actions?: ChatAction[]; } interface NavChatsProps { chats: ChatItem[]; defaultOpen?: boolean; searchSpaceId?: string; isSourcesExpanded?: boolean; } // Map of icon names to their components const actionIconMap: Record = { MessageCircleMore, Trash2, MoreHorizontal, RefreshCw, }; export function NavChats({ chats, defaultOpen = true, searchSpaceId, isSourcesExpanded = false, }: NavChatsProps) { const t = useTranslations("sidebar"); const router = useRouter(); const pathname = usePathname(); const isMobile = useIsMobile(); const [isDeleting, setIsDeleting] = useState(null); const [isOpen, setIsOpen] = useState(defaultOpen); const [isAllChatsSidebarOpen, setIsAllChatsSidebarOpen] = useState(false); // Auto-collapse on smaller screens when Sources is expanded useEffect(() => { if (isSourcesExpanded && isMobile) { setIsOpen(false); } }, [isSourcesExpanded, isMobile]); // Handle chat deletion with loading state const handleDeleteChat = useCallback(async (chatId: number, deleteAction: () => void) => { setIsDeleting(chatId); try { await deleteAction(); } finally { setIsDeleting(null); } }, []); // Handle chat navigation const handleChatClick = useCallback( (url: string) => { router.push(url); }, [router] ); return (
{t("recent_chats") || "Recent Chats"} {/* Action buttons - always visible on hover */}
{searchSpaceId && chats.length > 0 && ( )}
{chats.length > 0 ? ( {chats.map((chat) => { const isDeletingChat = isDeleting === chat.id; const isActive = pathname === chat.url; return ( {/* Main navigation button */} handleChatClick(chat.url)} disabled={isDeletingChat} className={cn( "pr-8", // Make room for the action button isActive && "bg-sidebar-accent text-sidebar-accent-foreground", isDeletingChat && "opacity-50" )} > {chat.name} {/* Actions dropdown - positioned absolutely */} {chat.actions && chat.actions.length > 0 && (
{chat.actions.map((action, actionIndex) => { const ActionIcon = actionIconMap[action.icon] || MessageCircleMore; const isDeleteAction = action.name.toLowerCase().includes("delete"); return ( { if (isDeleteAction) { handleDeleteChat(chat.id || 0, action.onClick); } else { action.onClick(); } }} disabled={isDeletingChat} className={ isDeleteAction ? "text-destructive focus:text-destructive" : "" } > {isDeletingChat && isDeleteAction ? t("deleting") || "Deleting..." : action.name} ); })}
)}
); })}
) : (
{t("no_recent_chats") || "No recent chats"}
)}
{/* All Chats Sheet */} {searchSpaceId && ( )}
); }