import { RefreshCw, Plus, Search, Pencil, Check, AlertCircle, X, } from "lucide-react"; import { useState, useCallback, useRef, useEffect } from "react"; import { SelectDropdown } from "../ui/SelectDropdown"; import { Tooltip } from "../ui/Tooltip"; import { useDbViewerStore } from "../../stores/dbViewerStore"; import * as cmd from "../../lib/commands"; import { SchemaMenu } from "./SchemaMenu"; import { getCapabilities } from "../../lib/dbCapabilities"; import type { DbType } from "../../lib/types"; export function DbViewerToolbar({ databases, currentDatabase, setCurrentDatabase, schemas, currentSchema, setCurrentSchema, onEdit, connectionId, dbType, searchQuery, onSearchChange, }: { databases: string[]; currentDatabase: string | null; setCurrentDatabase: (db: string | null) => void; schemas: string[]; currentSchema: string | null; setCurrentSchema: (schema: string | null) => void; onEdit?: () => void; connectionId?: string; dbType?: DbType; searchQuery: string; onSearchChange: (q: string) => void; }) { const [searchOpen, setSearchOpen] = useState(false); const [refreshing, setRefreshing] = useState(false); const [result, setResult] = useState<"idle" | "success" | "error">("idle"); const resultTimer = useRef | null>(null); const searchInputRef = useRef(null); const searchContainerRef = useRef(null); const populate = useDbViewerStore((s) => s.populate); const schemaTreeLoading = useDbViewerStore((s) => s.schemaTreeLoading); // Focus input when search opens useEffect(() => { if (searchOpen && searchInputRef.current) { searchInputRef.current.focus(); } }, [searchOpen]); // Auto-hide on blur when empty const handleSearchBlur = useCallback(() => { // Small delay to allow clicks on clear button / search icon setTimeout(() => { if (!searchQuery.trim()) { setSearchOpen(false); } }, 150); }, [searchQuery]); const toggleSearch = useCallback(() => { setSearchOpen((prev) => { const next = !prev; if (!next) onSearchChange(""); // clear when closing return next; }); }, [onSearchChange]); // Cleanup result timer on unmount useEffect(() => { return () => { if (resultTimer.current) clearTimeout(resultTimer.current); }; }, []); const handleCreateTable = useCallback(() => { const schema = currentSchema ?? "public"; useDbViewerStore.getState().openFormTab({ kind: "table", schema, name: "", title: "Create Table", description: "Create Table", mode: "create", params: { schema, name: "", action: { op: "create", columns: [] } }, }); }, [currentSchema]); const showCreateTable = getCapabilities(dbType ?? "postgresql").tableManagement; const handleRefresh = useCallback(async () => { if (!connectionId || refreshing) return; setRefreshing(true); setResult("idle"); try { const dbs = await cmd.getDatabases(connectionId); const scs = await cmd.getSchemas(connectionId); const tbls = await cmd.getTables(connectionId); populate(dbs, scs, tbls); setResult("success"); } catch { setResult("error"); } finally { setRefreshing(false); resultTimer.current = setTimeout(() => setResult("idle"), 1500); } }, [connectionId, refreshing, populate]); const hasBelow = searchOpen || databases.length > 1 || schemas.length > 1 || schemaTreeLoading; return (
Tables
{onEdit && ( )} {showCreateTable && ( )}
{/* Search input */}
onSearchChange(e.target.value)} onBlur={handleSearchBlur} placeholder="Filter tables…" className="w-full bg-transparent border-0 border-b border-border pl-8 pr-7 py-1.5 text-xs text-text placeholder:text-text-muted/60 outline-none focus:border-accent/50 transition-colors" /> {searchQuery && ( )}
{(databases.length > 1 || schemas.length > 1 || schemaTreeLoading) && (
{databases.length > 1 && ( ({ value: d, label: d, }))} placeholder="Select database" aria-label="Select database" variant="ghost" /> )} {databases.length > 1 && (schemas.length > 1 || schemaTreeLoading) && ( | )} {(schemas.length > 1 || schemaTreeLoading) && ( {} : setCurrentSchema} options={ schemaTreeLoading ? [{ value: "", label: "Loading…" }] : schemas.map((s) => ({ value: s, label: s })) } placeholder={schemaTreeLoading ? "Loading…" : "Select schema"} aria-label="Select schema" variant="ghost" disabled={schemaTreeLoading} /> )}
)}
); }