mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
fix(medications): category label uses the category-map lookup uniformly
The generic medication card mapped `medication.category` to one of the localised category labels via an inline lookup table, while the GLP-1 card hard-coded `medications.treatmentClassGlp1` into the same slot. The asymmetry made Mounjaro and Ramipril cards visually diverge on the medications list even though both should read the same row shape. Lift the lookup into `src/lib/medications/category-label.ts` and use it from both card variants. The GLP-1 nature of a medication is already implied by the rotation hint + injection-metadata rows below the header, so the category badge now carries the real classification (BLOOD_PRESSURE, HORMONE, OTHER, ...) on both cards. Tests pinned the old contract; they now assert the new shared label shape for both English and German locales.
This commit is contained in:
1 parent
d0ba800d9c
commit
b643baed82
4 files changed
+63
-22
No files matched your search
@@ -164,10 +164,13 @@ beforeEach(() => {
|
||||
|
||||
describe("<Glp1MedicationCard> — 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("<Glp1MedicationCard> — 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("<Glp1MedicationCard> — 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.
|
||||
|
||||
@@ -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 (
|
||||
<Card className={medication.active ? "" : "opacity-60"}>
|
||||
<MedicationCardHeader
|
||||
name={medication.name}
|
||||
dose={medication.dose}
|
||||
categoryLabel={t("medications.treatmentClassGlp1")}
|
||||
categoryLabel={categoryLabel}
|
||||
stateBadges={stateBadges}
|
||||
actions={headerActions}
|
||||
/>
|
||||
|
||||
@@ -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<string, string> = {
|
||||
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) ||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* v1.4.37 W4b — shared medication category-label lookup.
|
||||
*
|
||||
* Both the generic `<MedicationCard>` and the GLP-1 variant render
|
||||
* `categoryLabel` into the same `<MedicationCardHeader>` 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, string | number>) => string;
|
||||
|
||||
const MEDICATION_CATEGORY_KEYS: Record<string, string> = {
|
||||
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);
|
||||
}
|
||||
Reference in new issue
Block a user