import { html, api, useState, num } from './core.js'; import { Card, Pill, Empty } from './ui.js'; const SAMPLES = [ { label: 'live prediction maturity', db: 'intelligence', sql: `SELECT horizon_days, COUNT(*) AS n, MIN(date(information_cutoff, '+' || horizon_days || ' days')) AS first_matures FROM autonomy_predictions WHERE origin='live' AND status='open' GROUP BY horizon_days ORDER BY horizon_days` }, { label: 'accuracy by origin', db: 'intelligence', sql: `SELECT p.origin, COUNT(*) AS n, ROUND(100.0*AVG(o.direction_correct),1) AS acc_pct, ROUND(100.0*AVG(o.excess_return),2) AS mean_excess_pct FROM autonomy_predictions p JOIN autonomy_outcomes o ON o.prediction_id=p.id GROUP BY p.origin` }, { label: 'dead letters', db: 'intelligence', sql: `SELECT job_type, lane, COUNT(*) AS n, substr(MAX(last_error),1,80) AS err FROM autonomy_jobs WHERE status='dead_letter' GROUP BY job_type, lane` }, { label: 'content backlog', db: 'archive', sql: `SELECT COALESCE(content_status,'unfetched') AS status, COUNT(*) AS n FROM articles GROUP BY status ORDER BY n DESC` }, ]; export function Sql({ notify }) { const [sql, setSql] = useState(SAMPLES[0].sql); const [database, setDatabase] = useState('intelligence'); const [result, setResult] = useState(null); const [busy, setBusy] = useState(false); const run = async () => { setBusy(true); const started = performance.now(); try { const response = await api('/admin/api/sql', { method: 'POST', body: { sql, database } }); setResult({ ...response, clientMs: Math.round(performance.now() - started) }); } catch (error) { console.error('[ops] sql failed:', error.message); notify({ tone: 'bad', message: error.message, sticky: true }); setResult(null); } finally { setBusy(false); } }; const first = result && result.results && result.results[0]; const rows = (first && first.rows) || []; const columns = rows.length ? Object.keys(rows[0]) : []; return html`
<${Card} title="Query" right=${html`
`}>
samples: ${SAMPLES.map((s) => html` `)} ⌘/ctrl + enter
${result && html` <${Card} title="Result" right=${html` <${Pill}>${num(rows.length)} rows <${Pill}>${num(result.elapsed)}ms server <${Pill}>${num(result.clientMs)}ms total `}> ${first && first.error ? html`
${first.error}
` : !rows.length ? html`<${Empty}>No rows.` : html`
${columns.map((c) => html``)} ${rows.slice(0, 500).map((row, i) => html` ${columns.map((c) => html` `)} `)}
${c}
${row[c] === null ? '—' : String(row[c])}
${rows.length > 500 && html`
Showing the first 500 of ${num(rows.length)} rows.
`}`} `}
`; }