fix(coach): count distinct sources in the finished steps header

This commit is contained in:
Marc-André Bombeck committed 2026-09-27 13:03:28 +02:00
1 parent bc6c782745
commit 7c32dca7ab
2 files changed
+34 -2

No files matched your search

@@ -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("<CoachTurnSteps>", () => {
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(
<CoachTurnSteps steps={[BP, bpPrevious, SLEEP]} active={false} />,
);
expect(html).toContain("Looked at 2 sources");
});
it("carries a polite status region that starts empty", () => {
const html = render(<CoachTurnSteps steps={[BP]} active={false} />);
expect(html).toMatch(
@@ -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) {
</span>
) : (
<span data-slot="coach-turn-steps-done" className="min-w-0 truncate">
{tCount("coach.step.headerDone", steps.length)}
{tCount("coach.step.headerDone", countSources(steps))}
</span>
)}
</button>