import { useState, useCallback } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import Editor from '@monaco-editor/react'; import { apiGet, apiPost, apiDelete } from '../api/client'; import { LoadingSpinner, Card } from '../components/ui'; import { BarChart, Bar, LineChart, Line, ScatterChart, Scatter, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid, } from 'recharts'; interface QueryResult { columns: Array<{ name: string; type: string }>; rows: unknown[][]; row_count: number; elapsed_ms: number; } interface SavedQuery { id: string; name: string; description: string; sql_text: string; created_at: string; } interface SchemaInfo { catalog: string; schema: string; tables: Array<{ name: string; columns: Array<{ name: string; type: string; nullable?: boolean }> }>; } type ChartType = 'none' | 'bar' | 'line' | 'scatter'; export function SqlExplorerPage() { const qc = useQueryClient(); const [sql, setSql] = useState('SELECT 1 AS test'); const [result, setResult] = useState(null); const [error, setError] = useState(null); const [chartType, setChartType] = useState('none'); const [xCol, setXCol] = useState(0); const [yCol, setYCol] = useState(1); const { data: schema } = useQuery({ queryKey: ['pg-schema'], queryFn: () => apiGet('query', '/api/analytics/pg-schema'), }); const { data: savedQueries } = useQuery({ queryKey: ['saved-queries'], queryFn: () => apiGet('query', '/api/analytics/saved-queries'), }); const executeMutation = useMutation({ mutationFn: (sqlText: string) => apiPost('query', '/api/analytics/pg-query', { sql: sqlText, limit: 1000 }), onSuccess: (data) => { setResult(data); setError(null); }, onError: (err: Error) => { setError(err.message); setResult(null); }, }); const saveMutation = useMutation({ mutationFn: (body: { name: string; sql_text: string }) => apiPost('query', '/api/analytics/saved-queries', body), onSuccess: () => qc.invalidateQueries({ queryKey: ['saved-queries'] }), }); const deleteMutation = useMutation({ mutationFn: (id: string) => apiDelete('query', `/api/analytics/saved-queries/${id}`), onSuccess: () => qc.invalidateQueries({ queryKey: ['saved-queries'] }), }); const handleExecute = useCallback(() => { if (sql.trim()) executeMutation.mutate(sql); }, [sql, executeMutation]); const handleSave = useCallback(() => { const name = prompt('Query name:'); if (name) saveMutation.mutate({ name, sql_text: sql }); }, [sql, saveMutation]); const handleQuickInsert = useCallback((tableName: string) => { const query = `SELECT * FROM ${tableName} LIMIT 100`; setSql(query); }, []); // Build chart data from result const chartData = result && result.columns.length >= 2 ? result.rows.map((row) => ({ x: row[xCol], y: Number(row[yCol]) || 0, label: String(row[xCol]), })) : []; // Split saved queries into pre-built (no id-based delete) and user-saved const preBuiltNames = new Set([ 'Company Overview', 'Recent Recommendations', 'High Confidence Buys', 'Trend Windows Summary', 'Market Prices', 'Document Counts by Type', 'Global Events', 'Trading Decisions', 'Ingestion Health', 'Reserve Pool Ledger', 'Sector Exposure', 'Source Coverage Matrix', ]); const preBuiltQueries = (savedQueries ?? []).filter((sq) => preBuiltNames.has(sq.name)); const userQueries = (savedQueries ?? []).filter((sq) => !preBuiltNames.has(sq.name)); return (
{/* Schema browser sidebar */}

Schema

{schema?.tables.map((t) => (
{t.columns.map((c) => (
{c.name} {c.type}
))}
))} {/* Pre-built queries */} {preBuiltQueries.length > 0 && ( <>

Pre-built Queries

{preBuiltQueries.map((sq) => (
{sq.description && (

{sq.description}

)}
))} )} {/* User saved queries */}

Saved Queries

{userQueries.length === 0 && (

No saved queries yet

)} {userQueries.map((sq) => (
))}
{/* Main area */}
{/* Editor */}
setSql(v ?? '')} theme="vs-dark" options={{ minimap: { enabled: false }, fontSize: 13, lineNumbers: 'on', scrollBeyondLastLine: false }} />
{/* Controls */}
{result && ( {result.row_count} rows in {result.elapsed_ms}ms )} {error && {error}}
{/* Results */} {executeMutation.isPending && } {result && (
{result.columns.map((col, i) => ( ))} {result.rows.map((row, ri) => ( {row.map((cell, ci) => ( ))} ))}
{col.name} ({col.type})
{cell == null ? NULL : String(cell)}
)} {/* Chart builder */} {result && result.columns.length >= 2 && (
Chart: {(['none', 'bar', 'line', 'scatter'] as ChartType[]).map((ct) => ( ))} {chartType !== 'none' && ( <> )}
{chartType !== 'none' && chartData.length > 0 && ( {chartType === 'bar' ? ( ) : chartType === 'line' ? ( ) : ( )} )}
)}
); }