diff --git a/src/components/medications/__tests__/medication-card-glp1.test.tsx b/src/components/medications/__tests__/medication-card-glp1.test.tsx index 15fb9d0a9..b3980c448 100644 --- a/src/components/medications/__tests__/medication-card-glp1.test.tsx +++ b/src/components/medications/__tests__/medication-card-glp1.test.tsx @@ -164,10 +164,13 @@ beforeEach(() => { describe(" — GLP-1 variant rendering", () => { it("renders the GLP-1 variant when treatmentClass === 'GLP1' is active", () => { - // Headline differentiator: the GLP-1 card stamps the - // `medications.treatmentClassGlp1` badge ("GLP-1 injection") - // which the generic card never renders. Presence of the badge is - // the contract pin for "GLP-1 variant is showing". + // v1.4.37 W4b — the category-label slot now mirrors the generic + // card (real `medication.category` lookup) so Ramipril and + // Mounjaro share the same row shape. The variant differentiator + // moved into the GLP-1-specific rows below the header (last/next + // injection, rotation hint). We verify the GLP-1 card no longer + // hard-codes the treatment-class label into the category slot, + // and that the actual category lookup wins. const client = makeClient(); seedCompliance(client, med7p5.id); seedGlp1Details(client, med7p5.id, {}); @@ -177,7 +180,9 @@ describe(" — GLP-1 variant rendering", () => { client, ); - expect(html).toContain("GLP-1 injection"); + // Category is OTHER → "Other" badge wins. + expect(html).toContain("Other"); + expect(html).not.toContain("GLP-1 injection"); // v1.4.28 FB-G1 — the Syringe glyph + middle-dot separator on the // list row are gone. The list row reads as the canonical two-line // shape: `{name} {dose}` on line 1, class label on line 2. @@ -468,7 +473,12 @@ describe(" — GLP-1 variant rendering", () => { "de", ); - expect(html).toContain("GLP-1-Injektion"); + // v1.4.37 W4b — the German category badge now comes from the + // shared category-label lookup (Sonstiges for OTHER) instead of + // the hard-coded "GLP-1-Injektion" label, so the GLP-1 card stays + // 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:"); expect(html).toContain("Bauch, unten links"); // v1.4.28 retired the "Dosis-Historie" disclosure on the GLP-1 card. diff --git a/src/components/medications/glp1-medication-card.tsx b/src/components/medications/glp1-medication-card.tsx index 6be98d7ab..9725874c8 100644 --- a/src/components/medications/glp1-medication-card.tsx +++ b/src/components/medications/glp1-medication-card.tsx @@ -21,6 +21,7 @@ import { Progress } from "@/components/ui/progress"; import { useTranslations, useFormatters } from "@/lib/i18n/context"; import { invalidateKeys, medicationDependentKeys } from "@/lib/query-keys"; import { formatDateTime, formatTime } from "@/lib/format"; +import { getMedicationCategoryLabel } from "@/lib/medications/category-label"; import { describeInjectionSite, nextInjectionSite, @@ -283,12 +284,14 @@ export function Glp1MedicationCard({ ); + const categoryLabel = getMedicationCategoryLabel(medication.category, t); + return ( diff --git a/src/components/medications/medication-card.tsx b/src/components/medications/medication-card.tsx index 3cf941fc4..13be5be16 100644 --- a/src/components/medications/medication-card.tsx +++ b/src/components/medications/medication-card.tsx @@ -10,6 +10,7 @@ import { MedicationCardHeader } from "@/components/medications/MedicationCardHea import { parseScheduleRecurrence } from "@/lib/medication-schedule"; import { formatTimeWindowRange } from "@/lib/time-window-format"; import { formatDateTime, formatTime } from "@/lib/format"; +import { getMedicationCategoryLabel } from "@/lib/medications/category-label"; import { parseTimeToMinutes, reduceCurrentWindowStatus, @@ -172,21 +173,7 @@ export function MedicationCard({ medication, onEdit }: MedicationCardProps) { const rate7 = compliance?.compliance7?.rate ?? 0; const rate30 = compliance?.compliance30?.rate ?? 0; const streak = compliance?.compliance7?.streak ?? 0; - const categoryLabels: Record = { - BLOOD_PRESSURE: t("medications.categoryBloodPressure"), - VITAMIN: t("medications.categoryVitamin"), - SUPPLEMENT: t("medications.categorySupplement"), - PAIN_RELIEF: t("medications.categoryPainRelief"), - ALLERGY: t("medications.categoryAllergy"), - DIGESTIVE: t("medications.categoryDigestive"), - THYROID: t("medications.categoryThyroid"), - HORMONE: t("medications.categoryHormone"), - SKIN: t("medications.categorySkin"), - SLEEP_AID: t("medications.categorySleepAid"), - OTHER: t("medications.categoryOther"), - }; - const categoryLabel = - categoryLabels[medication.category] ?? t("medications.categoryOther"); + const categoryLabel = getMedicationCategoryLabel(medication.category, t); const sortedSchedules = [...medication.schedules].sort( (a, b) => a.windowStart.localeCompare(b.windowStart) || diff --git a/src/lib/medications/category-label.ts b/src/lib/medications/category-label.ts new file mode 100644 index 000000000..0e2e9989d --- /dev/null +++ b/src/lib/medications/category-label.ts @@ -0,0 +1,41 @@ +/** + * v1.4.37 W4b — shared medication category-label lookup. + * + * Both the generic `` and the GLP-1 variant render + * `categoryLabel` into the same `` outline badge. + * Historically the generic card kept its own inline lookup table while + * the GLP-1 card hard-coded `medications.treatmentClassGlp1` into the + * slot — Marc reported the asymmetry during the v1.4.37 UX audit + * (audit item 11). The GLP-1 nature is already implied by the + * rotation hint + injection metadata rows, so both surfaces should + * paint the actual medication category instead. + * + * The lookup is a small string map; we keep it as a function that takes + * the bound `t()` translator so the keys stay one source of truth and + * we don't need to drill a separate `categoryLabels` object through + * the card props. + */ + +type Translator = (key: string, params?: Record) => string; + +const MEDICATION_CATEGORY_KEYS: Record = { + BLOOD_PRESSURE: "medications.categoryBloodPressure", + VITAMIN: "medications.categoryVitamin", + SUPPLEMENT: "medications.categorySupplement", + PAIN_RELIEF: "medications.categoryPainRelief", + ALLERGY: "medications.categoryAllergy", + DIGESTIVE: "medications.categoryDigestive", + THYROID: "medications.categoryThyroid", + HORMONE: "medications.categoryHormone", + SKIN: "medications.categorySkin", + SLEEP_AID: "medications.categorySleepAid", + OTHER: "medications.categoryOther", +}; + +export function getMedicationCategoryLabel( + category: string, + t: Translator, +): string { + const key = MEDICATION_CATEGORY_KEYS[category] ?? "medications.categoryOther"; + return t(key); +}