diff --git a/CHANGELOG.md b/CHANGELOG.md index 27531353a..04387ea8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - A failed load of your share links now says so, with a way to retry, instead of quietly showing "no active share links" as if you had never shared anything. The same held for the API and connector token lists on the security screens. - A read that fails now shows a clear, recoverable error across the app instead of an empty list or a silent gap. The sharing and token screens, the ECG detail and list, the cycle and mood insights, the dashboard preventive-care tile, the checkups list, and the admin console all say when a read failed and offer a retry, rather than reading as "nothing here". - The AI consent and connector notes in Settings render as normal text now, not faint fine print, so the copy you are meant to read before turning a feature on is legible. +- The record-a-visit action on the checkups list now appears only where there is a real practice visit behind the reminder. A reminder you satisfy yourself in the app, like weighing in or a mood check-in, no longer offers to file a visit it never involved. - The respiratory-rate tile on the dashboard shows its unit in your own language instead of a fixed English label, and the personalized greeting carries its comma. ### Removed diff --git a/src/components/measurement-reminders/__tests__/vorsorge-section.test.tsx b/src/components/measurement-reminders/__tests__/vorsorge-section.test.tsx index 691508ff2..5e6a9f1df 100644 --- a/src/components/measurement-reminders/__tests__/vorsorge-section.test.tsx +++ b/src/components/measurement-reminders/__tests__/vorsorge-section.test.tsx @@ -277,6 +277,83 @@ describe(" loading + empty", () => { expect(html).toContain("text-warning"); }); + it("offers the file-visit action on a free-text appointment reminder", () => { + // A free-text reminder (no measurementType) stands for a real practice + // visit: "you went" is only ever recorded by filing a visit, so the + // affordance belongs here. + const reminder: MeasurementReminder = { + id: "appointment", + label: "Dentist appointment", + measurementType: null, + intervalDays: 365, + rrule: null, + anchorDate: null, + endsOn: null, + origin: "VORSORGE", + nextDueAt: null, + notifyHour: 9, + location: null, + lastSatisfiedAt: null, + enabled: true, + createdAt: "2030-01-01T00:00:00.000Z", + updatedAt: "2030-01-01T00:00:00.000Z", + } as MeasurementReminder; + remindersMock.mockReturnValue({ data: [reminder], isLoading: false }); + const html = render(); + expect(html).toContain('data-slot="vorsorge-file-visit"'); + }); + + it("hides the file-visit action on a self-measurement reminder", () => { + // A typed reminder (Gewicht, Blutdruck, a screening check-in) is + // satisfied by a reading or a check-in inside the app — there is no + // appointment behind it, so the visit affordance must not appear. + const reminder: MeasurementReminder = { + id: "self-measure", + label: "Measure blood pressure", + measurementType: "BLOOD_PRESSURE_SYS", + intervalDays: 7, + rrule: null, + anchorDate: null, + endsOn: null, + origin: "VORSORGE", + nextDueAt: null, + notifyHour: 9, + location: null, + lastSatisfiedAt: null, + enabled: true, + createdAt: "2030-01-01T00:00:00.000Z", + updatedAt: "2030-01-01T00:00:00.000Z", + } as MeasurementReminder; + remindersMock.mockReturnValue({ data: [reminder], isLoading: false }); + const html = render(); + expect(html).not.toContain('data-slot="vorsorge-file-visit"'); + }); + + it("hides the file-visit action on a screening check-in reminder", () => { + // A screening reminder (PHQ-9 / GAD-7) is a self-administered check-in, + // not a practice visit. + const reminder: MeasurementReminder = { + id: "screening-visit", + label: "PHQ-9 check-in", + measurementType: "PHQ9_SCORE", + intervalDays: 28, + rrule: null, + anchorDate: null, + endsOn: null, + origin: "VORSORGE", + nextDueAt: null, + notifyHour: 9, + location: null, + lastSatisfiedAt: null, + enabled: true, + createdAt: "2030-01-01T00:00:00.000Z", + updatedAt: "2030-01-01T00:00:00.000Z", + } as MeasurementReminder; + remindersMock.mockReturnValue({ data: [reminder], isLoading: false }); + const html = render(); + expect(html).not.toContain('data-slot="vorsorge-file-visit"'); + }); + it("translates a COACH-origin label i18n key and shows the neutral badge", () => { const reminder: MeasurementReminder = { id: "c1", diff --git a/src/components/measurement-reminders/vorsorge-section.tsx b/src/components/measurement-reminders/vorsorge-section.tsx index 118560d83..66b0b30dd 100644 --- a/src/components/measurement-reminders/vorsorge-section.tsx +++ b/src/components/measurement-reminders/vorsorge-section.tsx @@ -940,19 +940,26 @@ function VorsorgeCard({ * checkbox uses, so a person who does both gets one satisfaction and no * error. */ - const fileVisitButton = canManage ? ( - - ) : null; + // The file-visit action belongs only to a free-text reminder — a real + // practice visit, where "you went" is recorded by filing a visit. A typed + // reminder (Gewicht, Blutdruck, a screening check-in) is satisfied by a + // reading or a check-in inside the app, so it carries no visit affordance. + // `resolve.ts` states the same boundary server-side: an appointment reminder + // is free-text by construction. + const fileVisitButton = + canManage && !isLinked ? ( + + ) : null; const primaryButton = canManage ? (