From a3410efb5ce791b013dfb126c031ef943d8fc384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 7 Jun 2026 01:05:59 +0200 Subject: [PATCH] Unify medication card next/last labels across both variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GLP-1 card overrode the shared next/last slot with appointment-phrased labels (Next / Last, "Nächster Termin" / "Letzter Termin") while the generic card used Next intake / Last intake. Drop the override so both cards name the concept identically; the GLP-1 relative-day value phrasing (in N days / today) is unchanged. Remove the now-dead label override props from the shared slot and the orphaned glp1NextLabel / glp1LastLabel strings from all locales. --- messages/de.json | 3 +- messages/en.json | 3 +- messages/es.json | 3 +- messages/fr.json | 3 +- messages/it.json | 3 +- messages/pl.json | 3 +- .../__tests__/medication-card-glp1.test.tsx | 14 ++-- .../card-parts/medication-next-last-slot.tsx | 77 +++++++------------ .../medications/glp1-medication-card.tsx | 25 ++++-- 9 files changed, 60 insertions(+), 74 deletions(-) diff --git a/messages/de.json b/messages/de.json index b3cb6f540..5aa7eb089 100644 --- a/messages/de.json +++ b/messages/de.json @@ -700,6 +700,7 @@ "compliance7d": "7-Tage-Compliance", "compliance30d": "30-Tage-Compliance", "complianceWindow": "{days}-Tage-Compliance", + "complianceDoses": "{count} Dosen", "lastIntake": "Letzte Einnahme:", "nextIntake": "Nächste Einnahme:", "dayStreak": "Tage Serie", @@ -745,8 +746,6 @@ "glp1DosesPerUnit": "Dosen pro Pen / Ampulle", "glp1DosesPerUnitPlaceholder": "z. B. 4 für einen Mounjaro KwikPen", "glp1DosesPerUnitHelp": "Optional — Mounjaro KwikPen = 4, Ozempic 0,25/0,5 mg = 4, Wegovy Einmal-Pen = 1. Leer lassen, wenn keine Dosis-pro-Pen-Verfolgung gewünscht ist.", - "glp1NextLabel": "Nächster Termin:", - "glp1LastLabel": "Letzter Termin:", "glp1LastInjection": "{label}", "glp1LastInjectionWithSite": "{label} · {site}", "glp1NextInjectionDays": "{label} (in {days} Tagen)", diff --git a/messages/en.json b/messages/en.json index 659f3aa2b..bcfa155bf 100644 --- a/messages/en.json +++ b/messages/en.json @@ -700,6 +700,7 @@ "compliance7d": "7-day compliance", "compliance30d": "30-day compliance", "complianceWindow": "{days}-day compliance", + "complianceDoses": "{count} doses", "lastIntake": "Last intake:", "nextIntake": "Next intake:", "dayStreak": "day streak", @@ -745,8 +746,6 @@ "glp1DosesPerUnit": "Doses per pen / vial", "glp1DosesPerUnitPlaceholder": "e.g. 4 for a Mounjaro KwikPen", "glp1DosesPerUnitHelp": "Optional — Mounjaro KwikPen = 4, Ozempic 0.25/0.5 mg = 4, Wegovy single-dose pen = 1. Leave blank if you don't track per-pen dosing.", - "glp1NextLabel": "Next:", - "glp1LastLabel": "Last:", "glp1LastInjection": "{label}", "glp1LastInjectionWithSite": "{label} · {site}", "glp1NextInjectionDays": "{label} (in {days} days)", diff --git a/messages/es.json b/messages/es.json index b8fb454ac..6e5146b72 100644 --- a/messages/es.json +++ b/messages/es.json @@ -700,6 +700,7 @@ "compliance7d": "Cumplimiento 7 días", "compliance30d": "Cumplimiento 30 días", "complianceWindow": "Cumplimiento {days} días", + "complianceDoses": "{count} dosis", "lastIntake": "Última toma:", "nextIntake": "Próxima toma:", "dayStreak": "días de racha", @@ -745,8 +746,6 @@ "glp1DosesPerUnit": "Dosis por pluma / vial", "glp1DosesPerUnitPlaceholder": "p. ej. 4 para una pluma Mounjaro KwikPen", "glp1DosesPerUnitHelp": "Opcional — Mounjaro KwikPen = 4, Ozempic 0,25/0,5 mg = 4, Wegovy pluma de un solo uso = 1. Déjalo vacío si no quieres llevar el conteo de dosis por pluma.", - "glp1NextLabel": "Próxima cita:", - "glp1LastLabel": "Última cita:", "glp1LastInjection": "{label}", "glp1LastInjectionWithSite": "{label} · {site}", "glp1NextInjectionDays": "{label} (en {days} días)", diff --git a/messages/fr.json b/messages/fr.json index 5d034153b..c422e5303 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -700,6 +700,7 @@ "compliance7d": "Observance 7 jours", "compliance30d": "Observance 30 jours", "complianceWindow": "Observance {days} jours", + "complianceDoses": "{count} doses", "lastIntake": "Dernière prise :", "nextIntake": "Prochaine prise :", "dayStreak": "jours de série", @@ -745,8 +746,6 @@ "glp1DosesPerUnit": "Doses par stylo / flacon", "glp1DosesPerUnitPlaceholder": "p. ex. 4 pour un stylo Mounjaro KwikPen", "glp1DosesPerUnitHelp": "Optionnel — Mounjaro KwikPen = 4, Ozempic 0,25/0,5 mg = 4, Wegovy stylo à usage unique = 1. Laisse vide si tu ne veux pas suivre la dose par stylo.", - "glp1NextLabel": "Prochain rendez-vous :", - "glp1LastLabel": "Dernier rendez-vous :", "glp1LastInjection": "{label}", "glp1LastInjectionWithSite": "{label} · {site}", "glp1NextInjectionDays": "{label} (dans {days} jours)", diff --git a/messages/it.json b/messages/it.json index 97b5284fb..112f8c9ba 100644 --- a/messages/it.json +++ b/messages/it.json @@ -700,6 +700,7 @@ "compliance7d": "Aderenza 7 giorni", "compliance30d": "Aderenza 30 giorni", "complianceWindow": "Aderenza {days} giorni", + "complianceDoses": "{count} dosi", "lastIntake": "Ultima assunzione:", "nextIntake": "Prossima assunzione:", "dayStreak": "giorni di serie", @@ -745,8 +746,6 @@ "glp1DosesPerUnit": "Dosi per penna / fiala", "glp1DosesPerUnitPlaceholder": "es. 4 per una penna Mounjaro KwikPen", "glp1DosesPerUnitHelp": "Opzionale — Mounjaro KwikPen = 4, Ozempic 0,25/0,5 mg = 4, Wegovy penna monouso = 1. Lascia vuoto se non vuoi monitorare le dosi per penna.", - "glp1NextLabel": "Prossimo appuntamento:", - "glp1LastLabel": "Ultimo appuntamento:", "glp1LastInjection": "{label}", "glp1LastInjectionWithSite": "{label} · {site}", "glp1NextInjectionDays": "{label} (tra {days} giorni)", diff --git a/messages/pl.json b/messages/pl.json index d238c5397..fa0e5dead 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -700,6 +700,7 @@ "compliance7d": "Przestrzeganie 7 dni", "compliance30d": "Przestrzeganie 30 dni", "complianceWindow": "Przestrzeganie {days} dni", + "complianceDoses": "{count} dawek", "lastIntake": "Ostatnie przyjęcie:", "nextIntake": "Następne przyjęcie:", "dayStreak": "dni serii", @@ -745,8 +746,6 @@ "glp1DosesPerUnit": "Dawki na pen / fiolkę", "glp1DosesPerUnitPlaceholder": "np. 4 dla pena Mounjaro KwikPen", "glp1DosesPerUnitHelp": "Opcjonalne — Mounjaro KwikPen = 4, Ozempic 0,25/0,5 mg = 4, Wegovy pen jednorazowy = 1. Zostaw puste, jeśli nie chcesz śledzić dawek na pen.", - "glp1NextLabel": "Następny termin:", - "glp1LastLabel": "Ostatni termin:", "glp1LastInjection": "{label}", "glp1LastInjectionWithSite": "{label} · {site}", "glp1NextInjectionDays": "{label} (za {days} dni)", diff --git a/src/components/medications/__tests__/medication-card-glp1.test.tsx b/src/components/medications/__tests__/medication-card-glp1.test.tsx index c77f983ea..242391403 100644 --- a/src/components/medications/__tests__/medication-card-glp1.test.tsx +++ b/src/components/medications/__tests__/medication-card-glp1.test.tsx @@ -320,14 +320,16 @@ describe(" — GLP-1 variant rendering", () => { client, ); - // The last-line renders the "Last:" label in its own left column and + // v1.15.8 — the GLP-1 card unified onto the generic "Next intake" / + // "Last intake" labels (the appointment-phrased override is gone). The + // last-line renders the "Last intake:" label in its own left column and // the value (label · site) flush right; both halves show in the SSR. - expect(html).toContain("Last:"); + expect(html).toContain("Last intake:"); expect(html).toContain("Abdomen, lower left"); - // The next-line carries the "Next:" label in its left column, + // The next-line carries the "Next intake:" label in its left column, // independent of which of the three value variants (today / tomorrow / // "in N days") the helper produced. - expect(html).toMatch(/Next:/); + expect(html).toMatch(/Next intake:/); }); it("no longer renders the inline dose-history disclosure (retired v1.4.28)", () => { @@ -606,7 +608,9 @@ describe(" — GLP-1 variant rendering", () => { // visually symmetric with the generic card on the medications list. expect(html).toContain("Sonstiges"); expect(html).not.toContain("GLP-1-Injektion"); - expect(html).toContain("Letzter Termin:"); + // v1.15.8 — unified onto the generic "Letzte Einnahme:" label (the + // appointment-phrased "Letzter Termin:" override is gone). + expect(html).toContain("Letzte Einnahme:"); expect(html).toContain("Bauch, unten links"); // v1.4.28 retired the "Dosis-Historie" disclosure on the GLP-1 card. expect(html).not.toContain("Dosis-Historie"); diff --git a/src/components/medications/card-parts/medication-next-last-slot.tsx b/src/components/medications/card-parts/medication-next-last-slot.tsx index 9c77b61f2..5232f57b7 100644 --- a/src/components/medications/card-parts/medication-next-last-slot.tsx +++ b/src/components/medications/card-parts/medication-next-last-slot.tsx @@ -22,16 +22,18 @@ import { useTranslations } from "@/lib/i18n/context"; * that renders both lines, so the dose rows line up across the grid. * * Each card still builds the value *content* itself (the generic card emits a - * day-label + window-range; the GLP-1 card emits a weekly-cadence string), - * because the two cadences read differently — but the wrapper, order, colour, - * label/value layout, and gating are now guaranteed identical. The GLP-1 card - * overrides the left-column label with its appointment-phrased wording. + * day-label + window-range; the GLP-1 card emits a relative-day cadence + * string), because the two cadences read differently — but the wrapper, + * order, colour, label/value layout, and gating are guaranteed identical. + * v1.15.8 dropped the GLP-1-specific appointment-phrased label override so + * both cards name the concept identically ("Next intake" / "Last intake"); + * only the value content stays per-card. * - * Layout: when `labelled`, each line is a `flex justify-between` row with the - * bold label pinned left and the value flush right (`text-right`), so the - * label and the time read as two distinct columns rather than crammed onto - * one run of text. The label truncates first on a narrow viewport; the value - * (the time / cadence) never truncates. + * Layout: each line is a `flex justify-between` row with the bold label + * pinned left and the value flush right (`text-right`), so the label and the + * time read as two distinct columns rather than crammed onto one run of text. + * The label truncates first on a narrow viewport; the value (the time / + * cadence) never truncates. */ interface MedicationNextLastSlotProps { /** @@ -42,57 +44,32 @@ interface MedicationNextLastSlotProps { next: React.ReactNode | null; /** The last-intake line content, or null when the med has never been taken. */ last: React.ReactNode | null; - /** - * When true the part renders each line as a two-column row: a bold label - * pinned left and the value flush right. The generic card uses the default - * oral "Next intake" / "Last intake" labels; the GLP-1 card overrides them - * with its appointment-phrased labels (see `nextLabel` / `lastLabel`). - * When false the line renders as a single full-width run of text. - */ - labelled?: boolean; - /** - * Override for the left-column label on the next-line. Defaults to the - * oral `medications.nextIntake` string. The GLP-1 card passes its own - * "next appointment" wording so the value column carries only the time. - */ - nextLabel?: React.ReactNode; - /** Override for the left-column label on the last-line. */ - lastLabel?: React.ReactNode; } export function MedicationNextLastSlot({ next, last, - labelled = true, - nextLabel, - lastLabel, }: MedicationNextLastSlotProps) { const { t } = useTranslations(); return (
- {next && - (labelled ? ( -
- - {nextLabel ?? t("medications.nextIntake")} - - {next} -
- ) : ( -

{next}

- ))} - {last && - (labelled ? ( -
- - {lastLabel ?? t("medications.lastIntake")} - - {last} -
- ) : ( -

{last}

- ))} + {next && ( +
+ + {t("medications.nextIntake")} + + {next} +
+ )} + {last && ( +
+ + {t("medications.lastIntake")} + + {last} +
+ )}
); } diff --git a/src/components/medications/glp1-medication-card.tsx b/src/components/medications/glp1-medication-card.tsx index df75a98a8..8bfdbae2a 100644 --- a/src/components/medications/glp1-medication-card.tsx +++ b/src/components/medications/glp1-medication-card.tsx @@ -285,6 +285,13 @@ export function Glp1MedicationCard({ const rate7 = display?.short.rate ?? compliance?.compliance7?.rate ?? 0; const rate30 = display?.long.rate ?? compliance?.compliance30?.rate ?? 0; const streak = display?.short.streak ?? compliance?.compliance7?.streak ?? 0; + // v1.15.8 — taken-of-expected counts, shown after each percentage so two + // identical rates stay distinguishable (a weekly GLP-1 reading 100% on + // both windows shows e.g. `100% · 4 / 4` vs `100% · 13 / 13`). + const takenShort = display?.short.taken; + const expectedShort = display?.expectedShort; + const takenLong = display?.long.taken; + const expectedLong = display?.expectedLong; // v1.4.37 W4b — symmetric take-now / overdue pill with the generic // card. Sort schedules by `windowStart` so the most-actionable @@ -403,14 +410,14 @@ export function Glp1MedicationCard({ {/* Injection state — next + last, rendered through the SAME shared the generic card uses, so the order - (next-then-last), colour, spacing, gating, reserved min-height - and the label-left / value-right two-column layout are identical - across the two card types. The GLP-1 card overrides only the - left-column label with its appointment-phrased wording; the value - column carries the time (plus the optional rotation nudge). */} + (next-then-last), labels ("Next intake" / "Last intake"), colour, + spacing, gating, reserved min-height and the label-left / + value-right two-column layout are identical across the two card + types. v1.15.8 dropped the GLP-1-specific appointment-phrased + label override so both cards name the concept identically; the + relative-day value phrasing ("in N days" / "today") stays the + GLP-1 card's own and is preserved. */} @@ -462,6 +469,10 @@ export function Glp1MedicationCard({ streak={streak} shortDays={shortDays} longDays={longDays} + takenShort={takenShort} + expectedShort={expectedShort} + takenLong={takenLong} + expectedLong={expectedLong} /> ) : (