From 49c9b425bb951178b21299fed8fa309dc8b15a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 24 May 2026 00:21:41 +0200 Subject: [PATCH] fix(dashboard): paired-metric tiles drop to text-2xl so 131/85 stays visible (v1.4.49.4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Marc reported the BD (Sys) tile rendering `13… mmHg` — the systolic / diastolic pair `131/85` outgrew the `text-3xl` footprint on the narrowest grid columns and the value-row's `truncate` ellipsis chopped the most important part of the tile (the number itself). The other tiles in the strip (weight, pulse, glucose, …) carry a single value that never threatens the column-width budget on its own, so they keep `text-3xl`. Only paired-metric tiles step down to `text-2xl`; `131/85 mmHg` at `text-2xl tabular-nums tracking-tight` is ~84 px wide vs ~140 px for the narrowest 2-column grid cell — fits with the unit and trend arrow at every viewport. `truncate` stays as a defence-in-depth safety net for outlier values. `cn()` arg ordering tightened so `tailwind-merge` keeps the explicit `leading-none`. Tailwind v4's `text-3xl` / `text-2xl` carry their own default `line-height`, and the last-wins dedup rule otherwise dropped `leading-none` and broke the W20a across-tile baseline alignment that `trend-card-baseline-alignment.test.tsx` pins. Also fixes a pre-existing date-boundary test flake: `src/app/api/medications/intake/__tests__/route.test.ts`'s `reads the rollup tier when coverage is present` computed `todayKey` from UTC components, but the route's `readMedicationCompliance` uses the user's timezone (Europe/Berlin in the test fixture). At every 22:00 — 24:00 UTC window — once the Berlin clock crossed midnight — the two computations diverged by one calendar day, the mock returned data for the wrong day, and `today.scheduled` came back zero instead of three. The test now derives `todayKey` from the same Berlin zone the route uses. Tests: 5270 unit + integration pass, lint clean, typecheck clean. --- CHANGELOG.md | 10 ++++++++ package.json | 2 +- .../intake/__tests__/route.test.ts | 22 +++++++++++++---- src/components/charts/trend-card.tsx | 24 ++++++++++++++++++- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aa34ca7f..994869fde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [1.4.49.4] — 2026-05-24 — BD-tile number truncation + medication-compliance test tz drift + +Marc reported the dashboard BD (Sys) tile rendering `"13… mmHg"` — the systolic / diastolic pair (`131/85`) was wider than the narrowest grid column and the value-row's `truncate` ellipsis chopped the most important part of the tile (the number itself). + +### Fixed + +- `src/components/charts/trend-card.tsx` — paired-metric tiles (BP systolic/diastolic) now render the value at `text-2xl` (one Tailwind step down from `text-3xl`). Single-value tiles (weight, pulse, glucose, …) keep `text-3xl` because their value never threatens the column-width budget on its own. The `truncate` class stays as a defence-in-depth safety net so an outlier value (e.g. `200/120 mmHg` plus a future trend caption) still clips gracefully rather than overflowing the card boundary. +- `cn()` argument ordering tightened so `tailwind-merge` keeps the explicit `leading-none` — Tailwind v4's `text-3xl` / `text-2xl` carry their own default `line-height`, and the last-wins dedup rule would otherwise drop the explicit `leading-none` and break the W20a across-tile baseline alignment. The `` baseline-alignment tests pin the contract. +- `src/app/api/medications/intake/__tests__/route.test.ts` (one pre-existing test) computed its `todayKey` from UTC date components but the route's `readMedicationCompliance` uses the user's timezone (Europe/Berlin in the test fixture). At every nightly 22:00 — 24:00 UTC window — once the Berlin clock crossed midnight — the two computations diverged by one calendar day and the rollup-tier read returned zero for "today". The test now derives `todayKey` from the same `Europe/Berlin` zone the route uses; the assertion stays the same. + ## [1.4.49.3] — 2026-05-23 — Full i18n call-site audit + 28 stale missing keys filled Marc reported additional raw key strings (`notifications.eventMoodReminder`, `notifications.eventMoodReminderDesc`) reaching the UI after v1.4.49.2 had landed the relative-time fix. The narrow `{count}`-call audit that drove v1.4.49.2 had missed every dynamic key construction; the full exhaustive sweep below picked up 28 keys called from real code that had never existed in any locale bundle. diff --git a/package.json b/package.json index 60ea87301..1078d78c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "healthlog", - "version": "1.4.49.3", + "version": "1.4.49.4", "description": "Self-hosted personal-health-tracking PWA with Withings integration, AI insights, and doctor-report PDF export.", "license": "AGPL-3.0-only", "homepage": "https://healthlog.dev", diff --git a/src/app/api/medications/intake/__tests__/route.test.ts b/src/app/api/medications/intake/__tests__/route.test.ts index a60c60529..cf959e94b 100644 --- a/src/app/api/medications/intake/__tests__/route.test.ts +++ b/src/app/api/medications/intake/__tests__/route.test.ts @@ -363,12 +363,26 @@ describe("v1.4.39 W-MED — compliance rollup read swap", () => { // runs. The pre-fix shape hard-coded `2026-05-18`, which fell out // of the trailing-7-day window on every subsequent wall-clock // day and silently shifted the body.data tail off the seed. + // + // v1.4.49.4 — compute todayKey in the SAME zone the route uses + // (`readMedicationCompliance` → `userDayKey(now, safeTz)`), not in + // UTC. SESSION_OK carries no `timezone`, so the route falls + // through to `DEFAULT_TIMEZONE = "Europe/Berlin"`. The pre-fix + // UTC computation diverged from the route's Berlin computation by + // one calendar day during the 22:00-24:00 UTC window every night, + // causing the test to fail deterministically once the clock + // crossed midnight Berlin while still on the previous UTC day. const todayKey = (() => { const now = new Date(); - const y = now.getUTCFullYear(); - const m = String(now.getUTCMonth() + 1).padStart(2, "0"); - const d = String(now.getUTCDate()).padStart(2, "0"); - return `${y}-${m}-${d}`; + const fmt = new Intl.DateTimeFormat("en-CA", { + timeZone: "Europe/Berlin", + year: "numeric", + month: "2-digit", + day: "2-digit", + }); + // `en-CA` short-date format is `YYYY-MM-DD`, matching the + // route's `userDayKey` output (see `src/lib/tz/resolver.ts`). + return fmt.format(now); })(); vi.mocked(prisma.medicationComplianceRollup.findMany).mockResolvedValue([ { day: todayKey, scheduled: 3, taken: 2, skipped: 1 }, diff --git a/src/components/charts/trend-card.tsx b/src/components/charts/trend-card.tsx index 1679c828b..84186ff47 100644 --- a/src/components/charts/trend-card.tsx +++ b/src/components/charts/trend-card.tsx @@ -268,7 +268,29 @@ export function TrendCard({ className="mt-2 flex min-w-0 items-baseline gap-x-1.5" data-slot="trend-card-value-row" > - + {latest !== null ? renderPair(latest, secondary?.latest) : "—"}