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>
This commit is contained in:
@@ -48,8 +48,8 @@ test("(a) renders grid cells for a 2×2 preview result", async () => {
|
||||
// Grid must be present, not bold scalar
|
||||
expect(screen.queryByTestId("scalar-value")).not.toBeInTheDocument();
|
||||
|
||||
// truncated:true → show the "risultati troncati" indicator
|
||||
expect(screen.getByText(/risultati troncati/i)).toBeInTheDocument();
|
||||
// truncated:true → show the "results truncated" indicator
|
||||
expect(screen.getByText(/results truncated/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// (b) 1×1 preview → bold scalar shown, no grid
|
||||
@@ -105,7 +105,7 @@ test("(c) Esporta CSV button calls the export endpoint", async () => {
|
||||
// Wait for preview to load (use role to avoid ambiguity with paging panel)
|
||||
await waitFor(() => expect(screen.getAllByRole("gridcell")).not.toHaveLength(0));
|
||||
|
||||
const btn = screen.getByRole("button", { name: /esporta csv/i });
|
||||
const btn = screen.getByRole("button", { name: /export csv/i });
|
||||
await user.click(btn);
|
||||
|
||||
await waitFor(() => expect(exportCalled).toBe(true));
|
||||
@@ -136,9 +136,9 @@ test("(d) selecting tutti re-fetches preview with the large limit", async () =>
|
||||
// Initial fetch uses limit 10
|
||||
await waitFor(() => expect(limits).toContain(10));
|
||||
|
||||
// Select "tutti"
|
||||
const select = screen.getByLabelText(/righe da visualizzare/i);
|
||||
await user.selectOptions(select, "tutti");
|
||||
// Select "all"
|
||||
const select = screen.getByLabelText(/rows to display/i);
|
||||
await user.selectOptions(select, "all");
|
||||
|
||||
// A re-fetch with the large/unlimited limit must occur
|
||||
await waitFor(() => expect(limits.some((l) => (l as number) > 10)).toBe(true));
|
||||
|
||||
@@ -47,20 +47,20 @@ export function ResultsPanel({ sessionId }: Props) {
|
||||
<select
|
||||
value={limit}
|
||||
onChange={(e) =>
|
||||
setLimit(e.target.value === "tutti" ? 1_000_000 : Number(e.target.value))
|
||||
setLimit(e.target.value === "all" ? 1_000_000 : Number(e.target.value))
|
||||
}
|
||||
aria-label="Righe da visualizzare"
|
||||
aria-label="Rows to display"
|
||||
>
|
||||
<option value={10}>10</option>
|
||||
<option value="tutti">tutti</option>
|
||||
<option value="all">all</option>
|
||||
</select>
|
||||
|
||||
<button onClick={handleExport}>Esporta CSV</button>
|
||||
<button onClick={handleExport}>Export CSV</button>
|
||||
</div>
|
||||
|
||||
{/* Loading / error states */}
|
||||
{isLoading && <div>Caricamento...</div>}
|
||||
{error && <div>Errore: {String(error)}</div>}
|
||||
{isLoading && <div>Loading…</div>}
|
||||
{error && <div>Error: {String(error)}</div>}
|
||||
|
||||
{/* Scalar result: 1 col × 1 row → bold value */}
|
||||
{data && isScalar && (
|
||||
@@ -73,7 +73,7 @@ export function ResultsPanel({ sessionId }: Props) {
|
||||
<div className="ag-theme-alpine" style={{ height: 400, width: "100%" }}>
|
||||
<AgGridReact columnDefs={columnDefs} rowData={rowData} />
|
||||
</div>
|
||||
{data.truncated && <div>(risultati troncati)</div>}
|
||||
{data.truncated && <div>(results truncated)</div>}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -42,7 +42,7 @@ test("(b) clicking toggle switches to table listing a promoted table name", asyn
|
||||
// wait for mermaid to load
|
||||
await waitFor(() => expect(screen.getByTestId("mm")).toBeInTheDocument());
|
||||
|
||||
const toggle = screen.getByRole("button", { name: /tabella/i });
|
||||
const toggle = screen.getByRole("button", { name: /table/i });
|
||||
await userEvent.click(toggle);
|
||||
|
||||
expect(screen.getByText("orders")).toBeInTheDocument();
|
||||
@@ -52,7 +52,7 @@ test("(b) clicking toggle switches to table listing a promoted table name", asyn
|
||||
test("(c) oversized linking (>45) shows cap notice", () => {
|
||||
render(<SchemaLinkingViewer linking={bigLinking} />);
|
||||
expect(
|
||||
screen.getByText(/troppi elementi per il grafico/i)
|
||||
screen.getByText(/too many elements for the graph/i)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -109,19 +109,19 @@ export function SchemaLinkingViewer({ linking }: { linking: SchemaLinking }) {
|
||||
disabled={oversized}
|
||||
className={view === "chart" ? "font-bold" : ""}
|
||||
>
|
||||
Grafico
|
||||
Graph
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setView("table")}
|
||||
className={view === "table" ? "font-bold" : ""}
|
||||
>
|
||||
Tabella
|
||||
Table
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{oversized && (
|
||||
<p className="text-sm text-amber-600">
|
||||
Troppi elementi per il grafico, usa la tabella
|
||||
Too many elements for the graph, use the table
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -137,9 +137,9 @@ export function SchemaLinkingViewer({ linking }: { linking: SchemaLinking }) {
|
||||
<table className="w-full text-sm border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-left border-b py-1">Nome</th>
|
||||
<th className="text-left border-b py-1">Tipo</th>
|
||||
<th className="text-left border-b py-1">Perché</th>
|
||||
<th className="text-left border-b py-1">Name</th>
|
||||
<th className="text-left border-b py-1">Type</th>
|
||||
<th className="text-left border-b py-1">Why</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -147,7 +147,7 @@ export function SchemaLinkingViewer({ linking }: { linking: SchemaLinking }) {
|
||||
<React.Fragment key={table.name}>
|
||||
<tr className="font-semibold bg-muted/30">
|
||||
<td className="py-1 pl-1">{table.name}</td>
|
||||
<td>tabella</td>
|
||||
<td>table</td>
|
||||
<td className="text-xs text-muted-foreground">
|
||||
{reasonFor(table)}
|
||||
</td>
|
||||
@@ -157,7 +157,7 @@ export function SchemaLinkingViewer({ linking }: { linking: SchemaLinking }) {
|
||||
<td className="py-0.5 pl-4 text-muted-foreground">
|
||||
{col.name}
|
||||
</td>
|
||||
<td>colonna</td>
|
||||
<td>column</td>
|
||||
<td className="text-xs text-muted-foreground">
|
||||
{reasonFor(col)}
|
||||
</td>
|
||||
@@ -170,7 +170,7 @@ export function SchemaLinkingViewer({ linking }: { linking: SchemaLinking }) {
|
||||
<td className="py-0.5 pl-1 text-muted-foreground">
|
||||
{col.name}
|
||||
</td>
|
||||
<td>colonna</td>
|
||||
<td>column</td>
|
||||
<td className="text-xs text-muted-foreground">
|
||||
{reasonFor(col)}
|
||||
</td>
|
||||
|
||||
@@ -71,16 +71,16 @@ test("(c) layout toggle flips its label between Orizzontale and Verticale", asyn
|
||||
await waitFor(() => expect(screen.getAllByTestId("hl")).toHaveLength(2));
|
||||
|
||||
// Default layout is vertical, so the toggle offers "Orizzontale".
|
||||
const toggle = screen.getByRole("button", { name: /orizzontale/i });
|
||||
const toggle = screen.getByRole("button", { name: /horizontal/i });
|
||||
expect(toggle).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(toggle);
|
||||
|
||||
// After switching to horizontal, the toggle now offers "Verticale".
|
||||
expect(
|
||||
screen.getByRole("button", { name: /verticale/i })
|
||||
screen.getByRole("button", { name: /vertical/i })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole("button", { name: /orizzontale/i })
|
||||
screen.queryByRole("button", { name: /horizontal/i })
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -101,7 +101,7 @@ export function SqlViewer({ blocks }: { blocks: SqlBlock[] }) {
|
||||
setLayout((l) => (l === "vertical" ? "horizontal" : "vertical"))
|
||||
}
|
||||
>
|
||||
{layout === "vertical" ? "Orizzontale" : "Verticale"}
|
||||
{layout === "vertical" ? "Horizontal" : "Vertical"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user