feat(ui): Data Sources UI — enterprise browser for all 5 source databases

New "Data Sources UI" tab with per-database Browser (catalog + sample data),
Query Console (SqlWorkbench) and embedded interactive Shell (DbShell via SSH).

Backend:
- Extend sql_console.py with Cassandra (CQL) + Neo4j (Cypher) engines
- Add GET /api/sql/catalog/{engine} and GET /api/sql/sample/{engine}
- ssh_terminal: optional initial_command for auto-launching DB CLIs

Frontend:
- DataSourcesView with 5-DB rail, health dots, Browser/Console/Shell sub-tabs
- DbShell embedded xterm terminal with docker exec CLI per engine
- Deep-link topology DB nodes to Data Sources UI (no SQL dock on platform)
- WorkbenchPanel restricted to agent mode only — frees dashboard space
This commit is contained in:
mo
2026-06-27 15:42:37 +00:00
parent b8a5b10bc4
commit a4c9b60079
10 changed files with 1027 additions and 76 deletions
+7 -3
View File
@@ -21,6 +21,7 @@ import { DataGenView } from './components/features/DataGenView'
import { ChangesView } from './components/features/ChangesView'
import { DataFlowView } from './components/features/DataFlowView'
import { SearchView } from './components/features/SearchView'
import { DataSourcesView } from './components/features/DataSourcesView'
import { SshTerminal } from './components/features/SshTerminal'
import { TerminalDock } from './components/features/TerminalDock'
import { WorkbenchPanel } from './components/features/WorkbenchPanel'
@@ -38,6 +39,7 @@ export default function App() {
const mainScrollRef = useRef<HTMLDivElement>(null)
const isPlatform = cc.mainView === 'platform'
const isDataSources = cc.mainView === 'datasources'
const openApprovals = () => {
cc.setMainView('approvals')
@@ -79,9 +81,9 @@ export default function App() {
onOpenSsh={() => setSshOpen(true)}
/>
<div ref={mainScrollRef} className={cn('flex min-h-0 min-w-0 flex-1 flex-col bg-surface', isPlatform ? 'overflow-hidden' : 'scrollbar-thin overflow-y-auto')}>
<div ref={mainScrollRef} className={cn('flex min-h-0 min-w-0 flex-1 flex-col bg-surface', (isPlatform || isDataSources) ? 'overflow-hidden' : 'scrollbar-thin overflow-y-auto')}>
<div className={cn('flex min-h-0 flex-1', isPlatform ? '' : 'flex-col')}>
<div className={cn('flex min-h-0 min-w-0 flex-1 flex-col', isPlatform ? 'gap-1 overflow-hidden p-1.5' : 'min-h-0 gap-2 p-3')}>
<div className={cn('flex min-h-0 min-w-0 flex-1 flex-col', (isPlatform || isDataSources) ? 'gap-1 overflow-hidden p-1.5' : 'min-h-0 gap-2 p-3')}>
{isPlatform && (
<>
<div className="grid shrink-0 grid-cols-1 gap-2 xl:grid-cols-[1fr_auto]">
@@ -119,7 +121,7 @@ export default function App() {
</>
)}
<div className={cn('flex min-h-0 flex-col', isPlatform ? 'min-h-0 flex-1 overflow-hidden' : 'min-h-0 flex-1')}>
<div className={cn('flex min-h-0 flex-col', (isPlatform || isDataSources) ? 'min-h-0 flex-1 overflow-hidden' : 'min-h-0 flex-1')}>
{cc.mainView === 'platform' ? (
<PlatformTopology
workload={cc.workload}
@@ -128,6 +130,8 @@ export default function App() {
onNodeClick={cc.selectNode}
pulse={cc.genPulse}
/>
) : cc.mainView === 'datasources' ? (
<DataSourcesView focusEngine={cc.dataSourceFocus} />
) : cc.mainView === 'datagen' ? (
<DataGenView onPulse={cc.pulseFlow} onOpenPlatform={() => cc.setMainView('platform')} />
) : cc.mainView === 'changes' ? (