mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
fix(checkups): offer the record-a-visit action only for real visits
The file-a-visit affordance painted on every checkup reminder, including the self-measurement ones (weigh-in, blood pressure, a screening check-in) where there is no appointment and 'you went' is never a reading. Gate both the card and the dense-list branch on the same free-text predicate the mark-done action already uses: a reminder with a measurement type is satisfied in the app, a free-text reminder stands for a practice visit. The server states the same boundary in resolve.ts.
This commit is contained in:
1 parent
4335b38e02
commit
9aefce1625
3 files changed
+101
-15
No files matched your search
@@ -277,6 +277,83 @@ describe("<VorsorgeSection> 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(<VorsorgeSection />);
|
||||
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(<VorsorgeSection />);
|
||||
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(<VorsorgeSection />);
|
||||
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",
|
||||
|
||||
@@ -940,19 +940,26 @@ function VorsorgeCard({
|
||||
* checkbox uses, so a person who does both gets one satisfaction and no
|
||||
* error.
|
||||
*/
|
||||
const fileVisitButton = canManage ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="min-h-11 w-full"
|
||||
data-slot="vorsorge-file-visit"
|
||||
onClick={onFileVisit}
|
||||
disabled={busy}
|
||||
>
|
||||
<CalendarPlus className="h-4 w-4" />
|
||||
{t("measurementReminders.fileVisit")}
|
||||
</Button>
|
||||
) : 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 ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="min-h-11 w-full"
|
||||
data-slot="vorsorge-file-visit"
|
||||
onClick={onFileVisit}
|
||||
disabled={busy}
|
||||
>
|
||||
<CalendarPlus className="h-4 w-4" />
|
||||
{t("measurementReminders.fileVisit")}
|
||||
</Button>
|
||||
) : null;
|
||||
|
||||
const primaryButton = canManage ? (
|
||||
<Button
|
||||
@@ -1047,8 +1054,9 @@ function VorsorgeCard({
|
||||
) : null}
|
||||
{/* Icon-only in the dense list, labelled in the cards branch —
|
||||
the same action either way, never one branch offering what
|
||||
the other withholds. */}
|
||||
{canManage ? (
|
||||
the other withholds. Free-text reminders only: a typed /
|
||||
screening reminder has no practice visit behind it. */}
|
||||
{canManage && !isLinked ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
|
||||
Reference in new issue
Block a user