mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 04:32:42 +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
@@ -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
|
||||
|
||||
@@ -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