From 7c32dca7aba2bbc1928b7fa8bbbc12f8f9812104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 27 Sep 2026 13:03:28 +0200 Subject: [PATCH] fix(coach): count distinct sources in the finished steps header --- .../coach-panel/__tests__/turn-steps.test.tsx | 25 ++++++++++++++++++- .../insights/coach-panel/turn-steps.tsx | 11 +++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/components/insights/coach-panel/__tests__/turn-steps.test.tsx b/src/components/insights/coach-panel/__tests__/turn-steps.test.tsx index 67ae6e88b..6028f9140 100644 --- a/src/components/insights/coach-panel/__tests__/turn-steps.test.tsx +++ b/src/components/insights/coach-panel/__tests__/turn-steps.test.tsx @@ -10,6 +10,7 @@ import type { CoachStep } from "@/lib/ai/coach/types"; import { CoachTurnStepList, CoachTurnSteps, + countSources, currentStep, describeStep, nextAnnouncement, @@ -110,7 +111,7 @@ describe("describeStep", () => { it("renders in the reader's language from the keys, not the server label", () => { const de = translators("de"); - expect(stepLabel(BP, de.t)).toBe("Lese Blutdruck, letzte 90 Tage"); + expect(stepLabel(BP, de.t)).toBe("Prüfe: Blutdruck, letzte 90 Tage"); expect(describeStep(BP, de.t, de.tCount).meta).toEqual([ "letzte 90 Tage", "142 Messwerte", @@ -167,6 +168,28 @@ describe("", () => { expect(html).not.toContain('data-slot="coach-turn-steps-list"'); }); + it("counts distinct sources: a second read of the same domain is not a new source", () => { + const bpPrevious: CoachStep = { + ...BP, + id: "s4", + period: "previous", + count: 120, + }; + const snapshot: CoachStep = { + id: "s5", + tool: "snapshot", + labelKey: "coach.step.snapshot", + label: "Overview", + status: "done", + }; + expect(countSources([BP, bpPrevious, SLEEP])).toBe(2); + expect(countSources([BP, snapshot, snapshot])).toBe(2); + const html = render( + , + ); + expect(html).toContain("Looked at 2 sources"); + }); + it("carries a polite status region that starts empty", () => { const html = render(); expect(html).toMatch( diff --git a/src/components/insights/coach-panel/turn-steps.tsx b/src/components/insights/coach-panel/turn-steps.tsx index ae7b6adc6..4c05a067c 100644 --- a/src/components/insights/coach-panel/turn-steps.tsx +++ b/src/components/insights/coach-panel/turn-steps.tsx @@ -118,6 +118,15 @@ function rowText(step: CoachStep, t: Translate, tCount: TranslateCount) { return [title, ...meta].join(" · "); } +/** + * How many sources the turn looked at: one per domain (a step without one, + * the snapshot, counts by its tool). A second read of the same domain, for + * the period before or a retry, is not a new source. + */ +export function countSources(steps: readonly CoachStep[]): number { + return new Set(steps.map((step) => step.domain ?? step.tool)).size; +} + /** * The step the active header names: the latest one still running, else the * latest one overall (between two rounds nothing is running). @@ -336,7 +345,7 @@ export function CoachTurnSteps({ steps, active }: CoachTurnStepsProps) { ) : ( - {tCount("coach.step.headerDone", steps.length)} + {tCount("coach.step.headerDone", countSources(steps))} )}