import { useCallback, useEffect, useState } from 'react' import { Activity, ChevronRight, Database, FolderTree, Loader2, Network, Play, RefreshCw, Server, Table2, TerminalSquare, } from 'lucide-react' import { Badge } from '../ui/Badge' import { DbBrandIcon, dbBrandColor } from '../ui/DbBrandIcon' import { DataBrowserGrid } from './DataBrowserGrid' import { DataGenPanel } from './DataGenPanel' import { DbShell } from './DbShell' import { Neo4jGraphView } from './Neo4jGraphView' import { SqlWorkbench } from './SqlWorkbench' import { LakehouseWorkbench } from './SparkView' import { getSourceMeta, SOURCE_CATALOG, type CatalogObject, type SourceEngine, type SourceSubTab, } from '../../lib/dataSourceCatalog' import { cn } from '../../lib/utils' import { subTabActive, subTabIdle } from '../../lib/tabActive' type HealthMap = Record type CatalogResponse = { engine: string version?: string total_nodes?: number objects: CatalogObject[] } type SampleResponse = { ok: boolean columns?: string[] rows?: unknown[][] row_count?: number elapsed_ms?: number error?: string primary_keys?: string[] cdc?: boolean editable?: boolean offset?: number limit?: number total_count?: number | null } type Props = { focusEngine?: SourceEngine | null onPulse?: () => void } const ENGINE_CATALOG: Record = { postgres: 'postgres_sales', mysql: 'mysql_hr', mongodb: 'mongodb_supplychain', cassandra: 'cassandra_telemetry', neo4j: undefined, } const SUB_TABS: { id: SourceSubTab; label: string; icon: typeof FolderTree; neo4jOnly?: boolean; hideNeo4j?: boolean }[] = [ { id: 'browser', label: 'Browser', icon: FolderTree }, { id: 'graph', label: 'Graph', icon: Network, neo4jOnly: true }, { id: 'console', label: 'Query Console', icon: Database }, { id: 'workbench', label: 'Workbench', icon: Activity, hideNeo4j: true }, { id: 'generate', label: 'Generate', icon: Play }, { id: 'shell', label: 'Shell', icon: TerminalSquare }, ] function fmtCount(n?: number | null) { if (n == null) return '—' if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M` if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K` return String(n) } export function DataSourcesView({ focusEngine, onPulse }: Props) { const [active, setActive] = useState(focusEngine || 'postgres') const [subTab, setSubTab] = useState('browser') const [health, setHealth] = useState({}) const [catalog, setCatalog] = useState(null) const [catalogLoading, setCatalogLoading] = useState(false) const [selectedObject, setSelectedObject] = useState(null) const [sample, setSample] = useState(null) const [sampleLoading, setSampleLoading] = useState(false) const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(100) const meta = getSourceMeta(active) useEffect(() => { if (focusEngine) setActive(focusEngine) }, [focusEngine]) useEffect(() => { if (active !== 'neo4j' && subTab === 'graph') setSubTab('browser') }, [active, subTab]) const loadHealth = useCallback(async () => { try { const r = await fetch('/api/sql/health') if (r.ok) setHealth(await r.json()) } catch { /* */ } }, []) const loadCatalog = useCallback(async (engine: SourceEngine) => { setCatalogLoading(true) setCatalog(null) setSelectedObject(null) setSample(null) try { const r = await fetch(`/api/sql/catalog/${engine}`) if (r.ok) { const j: CatalogResponse = await r.json() setCatalog(j) const first = j.objects?.find((o) => o.type === 'table' || o.type === 'collection' || o.type === 'node_label') if (first) setSelectedObject(first) } } catch { /* */ } finally { setCatalogLoading(false) } }, []) const loadSample = useCallback(async (engine: SourceEngine, obj: CatalogObject, pg = page, ps = pageSize) => { setSampleLoading(true) setSample(null) const offset = (pg - 1) * ps try { const r = await fetch( `/api/sql/sample/${engine}?object=${encodeURIComponent(obj.fqn)}&limit=${ps}&offset=${offset}`, ) const j = await r.json() setSample(j) } catch { setSample({ ok: false, error: 'Sample API unavailable' }) } finally { setSampleLoading(false) } }, [page, pageSize]) useEffect(() => { loadHealth() }, [loadHealth]) useEffect(() => { if (subTab === 'browser') loadCatalog(active) }, [active, subTab, loadCatalog]) useEffect(() => { setPage(1) }, [selectedObject?.fqn, active]) useEffect(() => { if (selectedObject && subTab === 'browser') loadSample(active, selectedObject, page, pageSize) }, [selectedObject, active, subTab, page, pageSize, loadSample]) const refreshAll = () => { loadHealth() if (subTab === 'browser') { loadCatalog(active) if (selectedObject) loadSample(active, selectedObject, page, pageSize) } else if (subTab === 'graph' && active === 'neo4j') { /* Neo4jGraphView reloads itself */ } else if (selectedObject) loadSample(active, selectedObject, page, pageSize) } const visibleSubTabs = SUB_TABS.filter((t) => (!t.neo4jOnly || active === 'neo4j') && !(t.hideNeo4j && active === 'neo4j')) return (

Data Sources UI

Browse, filter, edit & query all source databases — edits on CDC sources flow to Live Changes

{/* Horizontal database selector */}

Source Databases

{SOURCE_CATALOG.map((src) => { const up = health[src.engine]?.ok const selected = active === src.engine const brand = dbBrandColor(src.engine) return ( ) })}
{/* Main panel — full width */}

{meta.label} {catalog?.version && ( v{catalog.version.split(' ')[0]?.slice(0, 20)} )}

{meta.host}:{meta.port} · {meta.database} · container {meta.container}

{visibleSubTabs.map(({ id, label, icon: Icon }) => ( ))}
{subTab === 'browser' && (
Objects {catalogLoading && }
{catalog?.objects?.map((obj) => ( ))} {!catalogLoading && !catalog?.objects?.length && (

No objects found

)}
{selectedObject ? ( <>Data: {selectedObject.fqn} ) : 'Select an object'}
{ setPageSize(ps); setPage(1) }} onReload={() => selectedObject && loadSample(active, selectedObject, page, pageSize)} />
)} {subTab === 'graph' && active === 'neo4j' && } {subTab === 'console' && } {subTab === 'workbench' && (
)} {subTab === 'generate' && onPulse && ( )} {subTab === 'shell' && }
) }