fix(coach): collapse the evidence disclosure to closed-by-default

The assistant bubble's evidence `<details>` mounted open whenever the
user had `coachPrefs.showEvidenceByDefault = true`, which surfaced the
raw measurement values (e.g. "Blutdruck letzte 30 Tage: 226") under
every reply. The disclosure is now always collapsed; the user expands
by click. The persisted pref stays in the schema for backward
compatibility but no UI reads or writes it, so the value is dead-code-
tolerated for one release and can be dropped in the next migration.

Drops the `evidenceDefaultOpen` prop wiring from `<MessageThread>` and
the corresponding `<Switch>` in the Coach settings sheet. Adjusts the
sheet test that previously asserted the toggle was rendered.
This commit is contained in:
Marc-André Bombeck committed 2026-05-15 16:45:55 +02:00
1 parent 053d678ac7
commit 4d88764e4f
3 files changed
+21 -44

No files matched your search

@@ -91,7 +91,8 @@ describe("<CoachSettingsSheet>", () => {
);
expect(html).not.toContain('data-slot="coach-prefs-skeleton"');
expect(html).toContain('data-slot="coach-prefs-tone"');
expect(html).toContain('data-slot="coach-prefs-evidence"');
// v1.4.27 F14 — the `showEvidenceByDefault` switch is retired.
expect(html).not.toContain('data-slot="coach-prefs-evidence"');
});
it("fires a sonner toast on save success (Design-H4)", () => {
@@ -323,31 +323,13 @@ export function CoachSettingsSheet({
</ul>
</div>
{/* Show evidence by default */}
<div className="flex items-start justify-between gap-3">
<div className="flex flex-col gap-1">
<Label
htmlFor="coach-prefs-evidence"
className="cursor-pointer text-xs font-medium"
>
{t("insights.coach.settingsEvidenceLabel")}
</Label>
<p className="text-muted-foreground text-[11px] leading-relaxed">
{t("insights.coach.settingsEvidenceHint")}
</p>
</div>
<Switch
id="coach-prefs-evidence"
data-slot="coach-prefs-evidence"
checked={draft.showEvidenceByDefault}
onCheckedChange={(next) =>
setDraft((prev) => ({
...prev,
showEvidenceByDefault: next,
}))
}
/>
</div>
{/* v1.4.27 F14 — the `showEvidenceByDefault` switch was
retired. The evidence disclosure is now always closed
by default and the user expands by click; surfacing
raw values automatically created an UX trap. The
persisted `coachPrefs.showEvidenceByDefault` field
stays in the schema for backward compatibility, but no
UI reads or writes it. */}
</div>
)}
@@ -15,7 +15,6 @@ import { cn } from "@/lib/utils";
import { useTranslations } from "@/lib/i18n/context";
import { stripChartTokens } from "@/lib/insights/chart-tokens";
import { useAuth } from "@/hooks/use-auth";
import { useCoachPrefs } from "@/hooks/use-coach-prefs";
import { SourceChips } from "./source-chips";
import type {
@@ -105,13 +104,13 @@ export function MessageThread({
const scrollerRef = useRef<HTMLDivElement | null>(null);
const wasPinnedRef = useRef(true);
// v1.4.23 H4 — read the user's Coach prefs so the evidence
// disclosure honours `showEvidenceByDefault`. Cached at the
// queryClient level via the shared `useCoachPrefs` hook; the
// settings sheet writes the same key after a successful save so the
// new default takes effect on the next reply without a page reload.
const { data: coachPrefs } = useCoachPrefs();
const evidenceDefaultOpen = coachPrefs?.showEvidenceByDefault ?? false;
// v1.4.27 F14 — the evidence disclosure used to honour an opt-in
// `coachPrefs.showEvidenceByDefault` flag that surfaced the raw
// measurement values unconditionally. The flag created an UX trap
// (Marc 2026-05-15: "literal metric values exposed under every
// bubble") so the disclosure is now collapsed by default for every
// reply. The user expands by click; the pref is retired in the
// settings sheet so nothing flips it back on.
const messages: CoachMessageDTO[] = useMemo(
() => conversation?.messages ?? [],
@@ -239,7 +238,6 @@ export function MessageThread({
content={m.content}
metricSource={m.metricSource}
providerType={m.providerType}
evidenceDefaultOpen={evidenceDefaultOpen}
messageId={m.id}
/>
);
@@ -269,7 +267,6 @@ export function MessageThread({
providerType={streaming.inProgress ? "streaming" : null}
inProgress={streaming.inProgress}
errorCode={streaming.errorCode}
evidenceDefaultOpen={evidenceDefaultOpen}
/>
</div>
)}
@@ -299,8 +296,6 @@ interface ChatBubbleProps {
providerType?: string | null;
inProgress?: boolean;
errorCode?: string | null;
/** v1.4.23 H4 — when true the evidence `<details>` mounts open. */
evidenceDefaultOpen?: boolean;
/**
* v1.4.23 H7 — present only on persisted assistant messages.
* Streaming bubbles (no message id yet) skip the thumbs row so the
@@ -316,7 +311,6 @@ function ChatBubble({
providerType,
inProgress,
errorCode,
evidenceDefaultOpen,
messageId,
}: ChatBubbleProps) {
const { t } = useTranslations();
@@ -428,11 +422,11 @@ function ChatBubble({
{keyValues.length > 0 && (
<details
data-slot="coach-evidence"
// v1.4.23 H4 — open by default when the user's prefs ask
// for it. Browsers honour the boolean `open` attribute on
// initial mount; toggling at runtime keeps the user's
// current expansion state intact.
open={evidenceDefaultOpen ? true : undefined}
// v1.4.27 F14 — always closed by default. The `open`
// attribute was previously tied to a per-user pref that
// surfaced raw values unconditionally; that pref is now
// retired and the disclosure is a true progressive-
// disclosure surface — the user clicks to expand.
className={cn(
"border-border/50 bg-muted/30 group rounded-md border",
"px-2.5 py-1.5 text-xs",