import React, { useEffect, useState, useMemo } from 'react'; import { Box, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography, Divider, CircularProgress, Stack, TextField, InputAdornment } from '@mui/material'; import { Storage, Search, FilterList, ChevronRight, ExpandMore, TableChart, Folder } from '@mui/icons-material'; import { useAppStore } from '../store/useAppStore'; import { SchemaService } from '../services/api'; const Sidebar: React.FC = () => { const { activeDatabase, setActiveDatabase, setActiveTable, activeTable } = useAppStore(); const [databases, setDatabases] = useState([]); const [tables, setTables] = useState([]); const [loading, setLoading] = useState(true); const [expandedDbs, setExpandedDbs] = useState([]); const [dbSearch, setDbSearch] = useState(''); const [tableSearch, setTableSearch] = useState(''); const [tablesLoading, setTablesLoading] = useState(false); // Fetch databases on mount useEffect(() => { const fetchDatabases = async () => { try { const response = await SchemaService.getDatabases(); setDatabases(response.data); } catch (error) { console.error('Failed to fetch databases', error); } finally { setLoading(false); } }; fetchDatabases(); }, []); // Fetch tables when activeDatabase changes useEffect(() => { const fetchTables = async () => { if (!activeDatabase) { setTables([]); return; } setTablesLoading(true); try { const response = await SchemaService.getTables(activeDatabase); setTables(response.data); } catch (error) { console.error('Failed to fetch tables', error); setTables([]); } finally { setTablesLoading(false); } }; fetchTables(); }, [activeDatabase]); // Auto-expand when a database is active useEffect(() => { if (activeDatabase && !expandedDbs.includes(activeDatabase)) { setExpandedDbs(prev => [...prev, activeDatabase]); } }, [activeDatabase]); const toggleExpansion = (e: React.MouseEvent, db: string) => { e.stopPropagation(); setExpandedDbs(prev => prev.includes(db) ? prev.filter(d => d !== db) : [...prev, db] ); }; const handleDatabaseClick = (db: string) => { setActiveDatabase(db); setActiveTable(null); setTableSearch(''); // Also ensure it's expanded if (!expandedDbs.includes(db)) { setExpandedDbs(prev => [...prev, db]); } }; const filteredDatabases = useMemo(() => { return databases.filter(db => db.toLowerCase().includes(dbSearch.toLowerCase())); }, [databases, dbSearch]); const filteredTables = useMemo(() => { return tables.filter(table => table.toLowerCase().includes(tableSearch.toLowerCase())); }, [tables, tableSearch]); return ( Explorer setDbSearch(e.target.value)} sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2, bgcolor: 'rgba(0,0,0,0.03)', fontSize: '0.8rem' } }} slotProps={{ input: { startAdornment: , } }} /> {loading ? ( ) : ( {filteredDatabases.map((db) => ( handleDatabaseClick(db)} selected={activeDatabase === db} sx={{ py: 0.5, px: 1 }} > toggleExpansion(e, db)}> {expandedDbs.includes(db) ? : } {db}} /> {expandedDbs.includes(db) && ( setTableSearch(e.target.value)} sx={{ '& .MuiOutlinedInput-root': { borderRadius: 1, bgcolor: 'background.paper', fontSize: '0.7rem', height: 28 } }} slotProps={{ input: { startAdornment: , } }} /> {tablesLoading ? ( ) : filteredTables.length === 0 ? ( No tables found ) : filteredTables.map((table) => ( setActiveTable(table)} sx={{ py: 0.4, px: 2, borderRadius: '4px', mx: 0.5, mb: 0.1, '&.Mui-selected': { bgcolor: 'primary.main', color: 'white', '&:hover': { bgcolor: 'primary.dark' } } }} > {table} } /> ))} )} ))} )} Connected: 127.0.0.1 ); }; export default Sidebar;