diff --git a/deploy/compose.psd-local.yaml.example b/deploy/compose.psd-local.yaml.example
index 52d6a932..2a52b893 100644
--- a/deploy/compose.psd-local.yaml.example
+++ b/deploy/compose.psd-local.yaml.example
@@ -32,6 +32,10 @@ services:
- ${THT_PSD_WORKSPACE_HOST_PATH:?set THT_PSD_WORKSPACE_HOST_PATH}:/data/workspaces/psd
frontend:
+ build:
+ args:
+ VITE_BASE: /
+ VITE_BACKEND_URL: /api
ports:
- "127.0.0.1:8099:8080"
networks: !override
diff --git a/frontend/src/widgets/MultiselectWidget.test.tsx b/frontend/src/widgets/MultiselectWidget.test.tsx
index 925e1659..dea75230 100644
--- a/frontend/src/widgets/MultiselectWidget.test.tsx
+++ b/frontend/src/widgets/MultiselectWidget.test.tsx
@@ -46,6 +46,56 @@ test("uses memory-specific selection copy without changing selected ids", async
expect(onRespond).toHaveBeenCalledWith({ id: "u-memory", kind: "multiselect", choices: ["recommended"] });
});
+test("shows the exact content and provenance for every memory option", () => {
+ render(
+
+ );
+
+ expect(screen.getByText("flag_attivo = TRUE")).toBeInTheDocument();
+ expect(screen.getByText(/Confermato dal reviewer/)).toBeInTheDocument();
+ expect(screen.getByText(/Conta i pazienti attivi/)).toBeInTheDocument();
+});
+
+test("renders memory detail as human-friendly Markdown", () => {
+ render(
+
+ );
+
+ expect(screen.getByText("Definizione").tagName).toBe("STRONG");
+ expect(screen.getByText("flag_attivo = TRUE").tagName).toBe("CODE");
+ expect(screen.getByText("record non annullato").closest("li")).not.toBeNull();
+ expect(screen.queryByText("**Definizione**")).not.toBeInTheDocument();
+});
+
test("select-all checks all options", async () => {
const onRespond = vi.fn();
render(
diff --git a/frontend/src/widgets/MultiselectWidget.tsx b/frontend/src/widgets/MultiselectWidget.tsx
index c6594690..f38991da 100644
--- a/frontend/src/widgets/MultiselectWidget.tsx
+++ b/frontend/src/widgets/MultiselectWidget.tsx
@@ -1,4 +1,6 @@
import { useState } from "react";
+import ReactMarkdown from "react-markdown";
+import remarkGfm from "remark-gfm";
import type { WidgetProps } from "./types";
import { ReservedControls } from "./ReservedControls";
@@ -56,7 +58,24 @@ export function MultiselectWidget({ descriptor, onRespond }: WidgetProps) {
checked={checked.has(o.id)}
onChange={() => toggle(o.id)}
/>
- {o.label}
+
+ {o.label}
+ {o.detail && (
+
+ {o.detail}
+
+ )}
+ {o.rationale && (
+
+ Motivo: {o.rationale}
+
+ )}
+ {typeof o.meta?.question_context === "string" && o.meta.question_context && (
+
+ Domanda di contesto: {o.meta.question_context}
+
+ )}
+
))}
diff --git a/frontend/src/widgets/SchemaLinkingGateWidget.test.tsx b/frontend/src/widgets/SchemaLinkingGateWidget.test.tsx
index 372bd734..4650d586 100644
--- a/frontend/src/widgets/SchemaLinkingGateWidget.test.tsx
+++ b/frontend/src/widgets/SchemaLinkingGateWidget.test.tsx
@@ -33,6 +33,13 @@ const descriptor: WidgetDescriptor = {
reserved: ["back", "exit", "other"],
};
+test("starts include rows checked and exclude rows unchecked even for legacy payloads", () => {
+ render();
+
+ expect(screen.getByRole("checkbox", { name: "dim_patient" })).toBeChecked();
+ expect(screen.getByRole("checkbox", { name: "fact_sostituzione" })).not.toBeChecked();
+});
+
test("Confirm emits enacted tables with suggested columns (catalog order)", async () => {
const onRespond = vi.fn();
render();
@@ -42,7 +49,7 @@ test("Confirm emits enacted tables with suggested columns (catalog order)", asyn
kind: "schema-linking",
tables: [
{ id: "t-pat", enacted: true, columns: ["cod_paz"] },
- { id: "x-sub", enacted: true },
+ { id: "x-sub", enacted: false },
],
});
});
@@ -60,7 +67,7 @@ test("selecting a column in the modal adds it to the response", async () => {
kind: "schema-linking",
tables: [
{ id: "t-pat", enacted: true, columns: ["cod_paz", "nome"] },
- { id: "x-sub", enacted: true },
+ { id: "x-sub", enacted: false },
],
});
});
@@ -75,7 +82,7 @@ test("declining a table's enact checkbox drops its columns", async () => {
kind: "schema-linking",
tables: [
{ id: "t-pat", enacted: false },
- { id: "x-sub", enacted: true },
+ { id: "x-sub", enacted: false },
],
});
});
diff --git a/frontend/src/widgets/SchemaLinkingGateWidget.tsx b/frontend/src/widgets/SchemaLinkingGateWidget.tsx
index 2c680bd7..353facfd 100644
--- a/frontend/src/widgets/SchemaLinkingGateWidget.tsx
+++ b/frontend/src/widgets/SchemaLinkingGateWidget.tsx
@@ -7,7 +7,7 @@ import { ReservedControls } from "./ReservedControls";
export function SchemaLinkingGateWidget({ descriptor, onRespond }: WidgetProps) {
const tables: SchemaTable[] = descriptor.tables ?? [];
const [enacted, setEnacted] = useState>(
- () => new Set(tables.filter((t) => t.recommended).map((t) => t.id))
+ () => new Set(tables.filter((t) => t.kind === "promote" && t.recommended).map((t) => t.id))
);
const [selected, setSelected] = useState