Files
ThothII/frontend/src/viewers/ResultsPanel.tsx
T
marcopanandClaude Opus 4.8 bcaf6708de feat(frontend): English UI + 'Datamart Builder with Human In The Loop' tagline
The interface is now fully English regardless of the chat language (session
titles stay in whatever language the user typed). Changed the right-rail
subtitle to 'Datamart Builder with Human In The Loop'. Translated all chrome:
new-question/settings dialogs, workflow phase strip (F1-F8), session rail,
steer input, widgets (select/multiselect/freetext/fallback/reserved controls)
and viewers (SQL, results, schema linking). Test matchers updated in lockstep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 18:27:11 +02:00

82 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { AgGridReact } from "ag-grid-react";
import { AllCommunityModule, ModuleRegistry } from "ag-grid-community";
import { sqlPreview, sqlExport } from "../api/sql";
// Register AG Grid modules once at module load time
ModuleRegistry.registerModules([AllCommunityModule]);
interface Props {
sessionId: string;
}
export function ResultsPanel({ sessionId }: Props) {
const [limit, setLimit] = useState<number>(10);
const { data, isLoading, error } = useQuery({
queryKey: ["preview", sessionId, limit],
queryFn: () => sqlPreview(sessionId, { limit, offset: 0 }),
});
async function handleExport() {
await sqlExport(sessionId);
}
const isScalar =
data != null && data.columns.length === 1 && data.rows.length === 1;
const columnDefs = data
? data.columns.map((col) => ({ field: col, headerName: col }))
: [];
const rowData = data
? data.rows.map((row) => {
const obj: Record<string, unknown> = {};
data.columns.forEach((col, i) => {
obj[col] = row[i];
});
return obj;
})
: [];
return (
<div>
{/* Limit selector */}
<div style={{ marginBottom: 8, display: "flex", gap: 8, alignItems: "center" }}>
<select
value={limit}
onChange={(e) =>
setLimit(e.target.value === "all" ? 1_000_000 : Number(e.target.value))
}
aria-label="Rows to display"
>
<option value={10}>10</option>
<option value="all">all</option>
</select>
<button onClick={handleExport}>Export CSV</button>
</div>
{/* Loading / error states */}
{isLoading && <div>Loading…</div>}
{error && <div>Error: {String(error)}</div>}
{/* Scalar result: 1 col × 1 row → bold value */}
{data && isScalar && (
<strong data-testid="scalar-value">{String(data.rows[0][0])}</strong>
)}
{/* Grid for everything else */}
{data && !isScalar && (
<>
<div className="ag-theme-alpine" style={{ height: 400, width: "100%" }}>
<AgGridReact columnDefs={columnDefs} rowData={rowData} />
</div>
{data.truncated && <div>(results truncated)</div>}
</>
)}
</div>
);
}