From bcaf6708deab14992d9259bf9751051f27ada7fd Mon Sep 17 00:00:00 2001 From: mptyl Date: Sun, 28 Jun 2026 18:27:11 +0200 Subject: [PATCH] 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 --- frontend/src/App.test.tsx | 2 +- frontend/src/shell/AppShell.tsx | 9 +++++---- frontend/src/shell/NavSessions.tsx | 2 +- frontend/src/shell/NewSessionDialog.test.tsx | 18 +++++++++--------- frontend/src/shell/NewSessionDialog.tsx | 16 ++++++++-------- frontend/src/shell/SettingsDialog.test.tsx | 6 +++--- frontend/src/shell/SettingsDialog.tsx | 16 ++++++++-------- frontend/src/shell/SteerInput.tsx | 4 ++-- frontend/src/shell/WidgetHost.tsx | 4 ++-- frontend/src/shell/WorkflowBar.tsx | 16 ++++++++-------- frontend/src/shell/f1-loop.test.tsx | 6 +++--- frontend/src/viewers/ResultsPanel.test.tsx | 12 ++++++------ frontend/src/viewers/ResultsPanel.tsx | 14 +++++++------- .../src/viewers/SchemaLinkingViewer.test.tsx | 4 ++-- frontend/src/viewers/SchemaLinkingViewer.tsx | 18 +++++++++--------- frontend/src/viewers/SqlViewer.test.tsx | 6 +++--- frontend/src/viewers/SqlViewer.tsx | 2 +- .../src/widgets/ArtifactGateWidget.test.tsx | 2 +- frontend/src/widgets/FallbackWidget.tsx | 4 ++-- frontend/src/widgets/FreetextWidget.test.tsx | 2 +- frontend/src/widgets/FreetextWidget.tsx | 2 +- .../src/widgets/MultiselectWidget.test.tsx | 18 +++++++++--------- frontend/src/widgets/MultiselectWidget.tsx | 4 ++-- frontend/src/widgets/ReservedControls.tsx | 2 +- frontend/src/widgets/SelectWidget.test.tsx | 2 +- frontend/src/widgets/linkage.test.tsx | 2 +- frontend/src/widgets/registry.test.tsx | 2 +- 27 files changed, 98 insertions(+), 97 deletions(-) diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index d1e90fd9..0ad1a8e5 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -3,5 +3,5 @@ import { App } from "./App"; test("App renders the shell with create affordance", () => { render(); - expect(screen.getByRole("button", { name: /nuova/i })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /new/i })).toBeInTheDocument(); }); diff --git a/frontend/src/shell/AppShell.tsx b/frontend/src/shell/AppShell.tsx index 67754427..5fbe99ac 100644 --- a/frontend/src/shell/AppShell.tsx +++ b/frontend/src/shell/AppShell.tsx @@ -61,7 +61,7 @@ export function AppShell() { ThothII

- Query in linguaggio naturale + Datamart Builder with Human In The Loop

@@ -72,7 +72,7 @@ export function AppShell() {
- Sessioni + Sessions
@@ -95,8 +95,9 @@ function EmptyState() { ThothII

- Interroga il tuo data warehouse in linguaggio naturale. Crea una nuova - domanda dal pannello a destra per avviare una sessione guidata. + Build datamarts from your data warehouse through a guided, + human-in-the-loop workflow. Start a new question from the panel on the + right to begin a session.

); diff --git a/frontend/src/shell/NavSessions.tsx b/frontend/src/shell/NavSessions.tsx index 0512ae4b..f35f288c 100644 --- a/frontend/src/shell/NavSessions.tsx +++ b/frontend/src/shell/NavSessions.tsx @@ -26,7 +26,7 @@ export function NavSessions({ activeSessionId, onSessionSelect }: Props) { if (sessions.length === 0) { return (

- Nessuna sessione. Inizia con Nuova domanda. + No sessions yet. Start with New question.

); } diff --git a/frontend/src/shell/NewSessionDialog.test.tsx b/frontend/src/shell/NewSessionDialog.test.tsx index 09338401..aa276a3e 100644 --- a/frontend/src/shell/NewSessionDialog.test.tsx +++ b/frontend/src/shell/NewSessionDialog.test.tsx @@ -19,10 +19,10 @@ function renderDialog() { test("the form has only a question field (no workspace/model/provider/thinking)", async () => { renderDialog(); - await userEvent.click(screen.getByRole("button", { name: /nuova/i })); - expect(await screen.findByLabelText(/domanda/i)).toBeInTheDocument(); + await userEvent.click(screen.getByRole("button", { name: /new/i })); + expect(await screen.findByLabelText(/question/i)).toBeInTheDocument(); expect(screen.queryByLabelText(/workspace/i)).not.toBeInTheDocument(); - expect(screen.queryByLabelText(/modello/i)).not.toBeInTheDocument(); + expect(screen.queryByLabelText(/model/i)).not.toBeInTheDocument(); expect(screen.queryByLabelText(/provider/i)).not.toBeInTheDocument(); expect(screen.queryByLabelText(/thinking/i)).not.toBeInTheDocument(); }); @@ -36,9 +36,9 @@ test("submitting posts only { question } and calls onCreated", async () => { }), ); const { onCreated } = renderDialog(); - await userEvent.click(screen.getByRole("button", { name: /nuova/i })); - await userEvent.type(await screen.findByLabelText(/domanda/i), "Quante vendite nel 2025?"); - await userEvent.click(screen.getByRole("button", { name: /^crea$/i })); + await userEvent.click(screen.getByRole("button", { name: /new/i })); + await userEvent.type(await screen.findByLabelText(/question/i), "Quante vendite nel 2025?"); + await userEvent.click(screen.getByRole("button", { name: /^create$/i })); await waitFor(() => expect(body).toEqual({ question: "Quante vendite nel 2025?" })); await waitFor(() => expect(onCreated).toHaveBeenCalledWith("s1")); @@ -46,7 +46,7 @@ test("submitting posts only { question } and calls onCreated", async () => { test("empty question shows a validation error and does not submit", async () => { renderDialog(); - await userEvent.click(screen.getByRole("button", { name: /nuova/i })); - await userEvent.click(screen.getByRole("button", { name: /^crea$/i })); - expect(await screen.findByRole("alert")).toHaveTextContent(/vuota/i); + await userEvent.click(screen.getByRole("button", { name: /new/i })); + await userEvent.click(screen.getByRole("button", { name: /^create$/i })); + expect(await screen.findByRole("alert")).toHaveTextContent(/empty/i); }); diff --git a/frontend/src/shell/NewSessionDialog.tsx b/frontend/src/shell/NewSessionDialog.tsx index a21752f5..3a8053a0 100644 --- a/frontend/src/shell/NewSessionDialog.tsx +++ b/frontend/src/shell/NewSessionDialog.tsx @@ -23,7 +23,7 @@ export function NewSessionDialog({ onCreated }: Props) { e.preventDefault(); setError(null); if (!question.trim()) { - setError("La domanda non può essere vuota."); + setError("The question cannot be empty."); return; } setBusy(true); @@ -33,7 +33,7 @@ export function NewSessionDialog({ onCreated }: Props) { setQuestion(""); onCreated(id); } catch (err) { - setError(err instanceof Error ? err.message : "Errore nella creazione della sessione."); + setError(err instanceof Error ? err.message : "Failed to create the session."); } finally { setBusy(false); } @@ -47,16 +47,16 @@ export function NewSessionDialog({ onCreated }: Props) { className="w-full" onClick={() => setOpen(true)} > - Nuova domanda + New question - Nuova sessione + New session