import { useState } from 'react' import { Plus, Pin, Trash2, MessageSquare, History, X } from 'lucide-react' import { cn } from '@/lib/utils' import type { ChatListItem } from '@/types/assistant-chat' interface ChatSidebarProps { chats: ChatListItem[] activeChatId: string | null onSelectChat: (id: string) => void onNewChat: () => void onDeleteChat: (id: string) => void onTogglePin: (id: string, pinned: boolean) => void mobileOpen?: boolean onMobileClose?: () => void collapsed?: boolean onToggleCollapse?: () => void } export function ChatSidebar({ chats, activeChatId, onSelectChat, onNewChat, onDeleteChat, onTogglePin, mobileOpen = false, onMobileClose, collapsed = false, onToggleCollapse, }: ChatSidebarProps) { const pinnedChats = chats.filter(c => c.pinned) const unpinnedChats = chats.filter(c => !c.pinned) const handleSelectChat = (id: string) => { onSelectChat(id) onMobileClose?.() } const handleNewChat = () => { onNewChat() onMobileClose?.() } // When collapsed on desktop, render nothing — parent renders the top bar if (collapsed && !mobileOpen) { return null } return ( <> {/* Mobile overlay */} {mobileOpen && (
)}
{/* Header */}
{onToggleCollapse && ( )}
{/* Chat list */}
{pinnedChats.length > 0 && (
Pinned
)} {pinnedChats.map(chat => ( handleSelectChat(chat.id)} onDelete={() => onDeleteChat(chat.id)} onTogglePin={() => onTogglePin(chat.id, !chat.pinned)} /> ))} {pinnedChats.length > 0 && unpinnedChats.length > 0 && (
)} {unpinnedChats.map(chat => ( handleSelectChat(chat.id)} onDelete={() => onDeleteChat(chat.id)} onTogglePin={() => onTogglePin(chat.id, !chat.pinned)} /> ))} {chats.length === 0 && (
No conversations yet
)}
) } /** Collapsed top bar — rendered by the parent page above the chat area */ export function ChatSidebarCollapsedBar({ chats, activeChatId, onNewChat, onExpand, }: { chats: ChatListItem[] activeChatId: string | null onNewChat: () => void onExpand: () => void }) { return (
{activeChatId && ( {chats.find(c => c.id === activeChatId)?.title} )}
) } function ChatItem({ chat, isActive, onSelect, onDelete, onTogglePin, }: { chat: ChatListItem isActive: boolean onSelect: () => void onDelete: () => void onTogglePin: () => void }) { const [confirming, setConfirming] = useState(false) return (
e.stopPropagation() : onSelect} className={cn( 'group flex items-center gap-2 px-3 py-2.5 mx-1.5 rounded-lg cursor-pointer transition-colors', confirming ? 'bg-danger-dim border border-danger/20' : isActive ? 'bg-accent-dim text-foreground' : 'text-muted-foreground hover:bg-input hover:text-foreground' )} >
{confirming ? (
Delete?
) : ( <>
{chat.title}
{chat.psa_ticket_id && ( #{chat.psa_ticket_id} )} {(chat.status === 'escalated' || chat.status === 'requesting_escalation') && ( Escalated )}
{/* Secondary line: problem snippet when the title doesn't already carry it, otherwise the message count. Keeps untitled sessions from collapsing into identical-looking rows. */} {chat.problem_summary && chat.problem_summary !== chat.title ? (
{chat.problem_summary}
) : (
{chat.message_count} messages
)} )}
{!confirming && (
)}
) }