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>
82 lines
2.3 KiB
TypeScript
82 lines
2.3 KiB
TypeScript
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>
|
||
);
|
||
}
|