import { useState, useEffect, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Plus, MessageSquare, Bookmark, Trash2, X, Database, ChevronRight, Sparkles, Search, PanelLeftClose, PanelLeft, Settings, ArrowRight, Hash } from 'lucide-react'; import useChatStore from '../../store/useChatStore'; const TABLE_GROUPS = { 'accounts': 'Core SaaS', 'contacts': 'Core SaaS', 'workspaces': 'Core SaaS', 'workspace_users': 'Core SaaS', 'departments': 'HR & Staff', 'employees': 'HR & Staff', 'plans': 'Billing', 'products': 'Billing', 'feature_catalog': 'Billing', 'subscriptions': 'Billing', 'invoices': 'Billing', 'payments': 'Billing', 'opportunities': 'Sales', 'support_tickets': 'Support & Ops', 'ticket_events': 'Support & Ops', 'incidents': 'Support & Ops', 'product_usage_daily': 'Support & Ops', 'query_audit_log': 'Support & Ops', }; const MOCK_COLUMNS = { departments: ['id', 'name', 'manager_id', 'location'], employees: ['id', 'first_name', 'last_name', 'email', 'department_id', 'salary', 'hire_date'], plans: ['id', 'name', 'price', 'billing_interval', 'features'], products: ['id', 'name', 'sku', 'price', 'status'], feature_catalog: ['id', 'name', 'code', 'status'], accounts: ['id', 'name', 'domain', 'industry', 'owner_id', 'created_at'], contacts: ['id', 'account_id', 'first_name', 'last_name', 'email', 'phone'], workspaces: ['id', 'account_id', 'name', 'slug', 'created_at'], workspace_users: ['workspace_id', 'user_id', 'role', 'joined_at'], subscriptions: ['id', 'account_id', 'plan_id', 'status', 'start_date', 'end_date'], invoices: ['id', 'subscription_id', 'amount', 'due_date', 'status'], payments: ['id', 'invoice_id', 'amount', 'payment_method', 'status', 'created_at'], opportunities: ['id', 'account_id', 'name', 'amount', 'stage', 'closed_at'], product_usage_daily: ['date', 'workspace_id', 'queries_run', 'active_users'], support_tickets: ['id', 'account_id', 'subject', 'status', 'priority', 'created_at'], ticket_events: ['id', 'ticket_id', 'event_type', 'created_at'], incidents: ['id', 'title', 'severity', 'status', 'created_at'], query_audit_log: ['id', 'user_id', 'query_text', 'execution_time_ms', 'status'], }; function SectionHeader({ title, count, collapsed, onToggle, isSidebarCollapsed }) { if (isSidebarCollapsed) return
; return ( ); } function EmptyChats({ onNewChat }) { return (

No conversations

Start your first AI analysis

); } export default function Sidebar({ open, onClose }) { const chats = useChatStore(s => s.chats); const activeChatId = useChatStore(s => s.activeChatId); const savedQueries = useChatStore(s => s.savedQueries); const health = useChatStore(s => s.health); const schemaTables = useChatStore(s => s.schemaTables); const selectedSchema = useChatStore(s => s.selectedSchema); const newChat = useChatStore(s => s.newChat); const selectChat = useChatStore(s => s.selectChat); const deleteChat = useChatStore(s => s.deleteChat); const setSelectedSchema = useChatStore(s => s.setSelectedSchema); const sidebarCollapsed = useChatStore(s => s.sidebarCollapsed); const setSidebarCollapsed = useChatStore(s => s.setSidebarCollapsed); const [searchQuery, setSearchQuery] = useState(''); const [hoveredChat, setHoveredChat] = useState(null); const [schemaCollapsed, setSchemaCollapsed] = useState(false); const [savedCollapsed, setSavedCollapsed] = useState(false); const searchInputRef = useRef(null); // Global Ctrl+K shortcut to focus search useEffect(() => { const handleKeyDown = (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); if (sidebarCollapsed) { setSidebarCollapsed(false); } setTimeout(() => searchInputRef.current?.focus(), 50); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [sidebarCollapsed, setSidebarCollapsed]); const handleSavedQuery = (query) => { window.dispatchEvent(new CustomEvent('plainsql:submit', { detail: { query } })); if (open) onClose?.(); }; const handleNewChat = () => { newChat(); if (open) onClose?.(); }; // Filter logic const filteredTables = schemaTables.filter(t => t.toLowerCase().includes(searchQuery.toLowerCase()) ); const filteredChats = chats.filter(c => c.title.toLowerCase().includes(searchQuery.toLowerCase()) ); const filteredSaved = savedQueries.filter(q => q.toLowerCase().includes(searchQuery.toLowerCase()) ); // Group schema tables const groupedTables = {}; filteredTables.forEach(t => { const groupName = TABLE_GROUPS[t] || 'Other'; if (!groupedTables[groupName]) groupedTables[groupName] = []; groupedTables[groupName].push(t); }); return ( <> {/* Mobile scrim */} {open && ( )} {/* Brand + Mobile close */}
sidebarCollapsed && setSidebarCollapsed(false)} className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 shadow-lg cursor-pointer bg-glow border-glow" style={{ background: 'linear-gradient(135deg, var(--brand), var(--brand-cyan))' }} > S
{!sidebarCollapsed && (

PlainSQL

AI Data Platform

)}
{!sidebarCollapsed && (
{/* New chat */} {/* Mobile close button */}
)}
{/* Search Input Area */} {!sidebarCollapsed ? (
setSearchQuery(e.target.value)} className="bg-transparent border-0 outline-none text-xs w-full text-white placeholder-t4" /> {searchQuery ? ( ) : ( ⌘K )}
) : (
)} {/* Scrollable body */}
{/* New Chat Button (Standard size) */} {!sidebarCollapsed ? (
) : (
)} {/* Schema Explorer */} {!sidebarCollapsed ? ( <> setSchemaCollapsed(v => !v)} /> {!schemaCollapsed && (
{/* All tables selector */} {/* Grouped Table Explorer */}
{Object.entries(groupedTables).map(([group, tables]) => (
{group}
{tables.map(t => { const isSel = selectedSchema === t; const cols = MOCK_COLUMNS[t]?.length || 4; return ( ); })}
))}
)}
) : (
)} {/* History */} {!sidebarCollapsed ? ( <>
{filteredChats.length === 0 && searchQuery && (

No results match query

)} {filteredChats.length === 0 && !searchQuery && ( )} {filteredChats.map(chat => { const isActive = chat.id === activeChatId; const queryCount = chat.messages.filter(m => m.role === 'user').length; return ( setHoveredChat(chat.id)} onHoverEnd={() => setHoveredChat(null)} onClick={() => { selectChat(chat.id); if (open) onClose?.(); }} className={` nav-item relative group flex items-center gap-2.5 px-3 py-2 cursor-pointer ${isActive ? 'active text-white font-medium' : 'text-t2'} `} >
{chat.title}
{queryCount > 0 && hoveredChat !== chat.id && ( {queryCount} )} {hoveredChat === chat.id && ( )}
); })}
) : (
{filteredChats.slice(0, 8).map(chat => { const isActive = chat.id === activeChatId; return ( ); })}
)} {/* Saved queries */} {!sidebarCollapsed ? ( <> setSavedCollapsed(v => !v)} /> {!savedCollapsed && (
{filteredSaved.map((q, i) => ( ))}
)}
) : (
)}
{/* Footer — Profile, Settings, and Sidebar Collapse Trigger */}
{/* Health status indicator */} {!sidebarCollapsed ? (
{health.status === 'healthy' ? 'All systems active' : health.status === 'degraded' ? 'Degraded service' : 'Connecting...'} {health.latency && ( {health.latency}ms )}
) : (
)} {/* Profile row */} {!sidebarCollapsed ? (
LC
Lalit Chaudhari Developer
) : (
LC
)}
); }