import { useState, type ReactNode } from 'react'; import { useRows, useSummary } from '../api/hooks'; import { useI18n } from '../i18n'; import { useWorkspace } from '../state/workspace'; import { useToasts } from '../state/toasts'; /** * The signature: the statement the server actually ran for the current view, * with literals inlined. Not an illustration — this is the query, echoed back * from the response that produced the rows above it. * * Collapsed by default. Shown once on request it is a proof that the grid is a * real query; shown always it was a truncated ribbon nobody could read. */ export function SqlBar(): ReactNode { const ws = useWorkspace(); const toasts = useToasts(); const { t, locale } = useI18n(); const [open, setOpen] = useState(false); const { sql, tokens, total } = useRows({ table: ws.tableName, viewId: ws.viewId, filters: ws.adHocFilters, sort: ws.sortOverride, hidden: ws.hiddenOverride, search: ws.search, enabled: Boolean(ws.tableName), }); // The aggregate row is a second statement, so it gets a second line. Hiding // it would make the footer the one number on this screen with no working out // shown — which is the opposite of what this bar is for. const summary = useSummary({ table: ws.tableName, viewId: ws.viewId, filters: ws.adHocFilters, hidden: ws.hiddenOverride, search: ws.search, summaries: ws.summaries, enabled: Boolean(ws.tableName), }); if (!sql) return null; return (
{/* The whole line toggles — a thin dark strip whose only clickable part is one small label invites misses. The buttons stop propagation so they keep their own meaning. */}
setOpen(!open)}> {t('grid.sqlRowCount', total, locale)} {open && ( )}
{open && ( // One scroll container around both statements: two panes that scroll // apart would make the pair read as two bars instead of one proof.
{tokens.map((token, i) => ( {token.s} ))} {summary.sql && ( {summary.tokens.map((token, i) => ( {token.s} ))} )}
)}
); }