From 358ef232393c9ffdd5c9ee7e3e4302d339bde00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 3 Oct 2026 18:39:03 +0200 Subject: [PATCH] fix(clinician): write the shared view's numbers in the owner's locale The clinician view rounded numbers and printed them bare, so a German record showed 24.5 where every other surface shows 24,5. Its number formatter now goes through the locale formatter it already builds, at most two decimals. --- .../__tests__/clinician-view-locales.test.tsx | 28 +++++++++++++++++++ src/components/clinician/clinician-view.tsx | 6 ++-- src/components/clinician/history-sections.tsx | 4 +-- src/components/clinician/report-sections.tsx | 8 +++--- .../clinician/sensitive-sections.tsx | 4 +-- src/components/clinician/therapy-sections.tsx | 4 +-- 6 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/components/clinician/__tests__/clinician-view-locales.test.tsx b/src/components/clinician/__tests__/clinician-view-locales.test.tsx index 4ba27dab5..75ce2d798 100644 --- a/src/components/clinician/__tests__/clinician-view-locales.test.tsx +++ b/src/components/clinician/__tests__/clinician-view-locales.test.tsx @@ -253,3 +253,31 @@ describe(" resolves every label in every locale", () => { }); } }); + +describe(" numbers follow the owner's locale", () => { + function text(locale: "de" | "en") { + const { t } = getServerTranslator(locale); + return renderToStaticMarkup( + ClinicianView({ + t: (key, vars) => t(key, vars), + label: "Clinic", + expiresAt: "2026-03-01T00:00:00.000Z", + report: FULL_RECORD, + selection: selectionFromLeaves(ALL_LEAF_IDS), + locale, + timeFormat: "AUTO", + dateFormat: "AUTO", + }), + ).replace(/<[^>]*>/g, " "); + } + + it("writes a German decimal with a comma", () => { + // BMI 24.5 in the fixture. + expect(text("de")).toContain("24,5"); + expect(text("de")).not.toContain("24.5"); + }); + + it("keeps the point for an English reader", () => { + expect(text("en")).toContain("24.5"); + }); +}); diff --git a/src/components/clinician/clinician-view.tsx b/src/components/clinician/clinician-view.tsx index fb49c7b2e..2a5105fb1 100644 --- a/src/components/clinician/clinician-view.tsx +++ b/src/components/clinician/clinician-view.tsx @@ -149,7 +149,9 @@ export function ClinicianView({ const fmt = makeFormatters(locale, timezone, timeFormat, dateFormat); const fmtDate = (iso: string) => fmt.date(new Date(iso)); const fmtDateTime = (iso: string) => fmt.dateTime(new Date(iso)); - const fmtNum = (n: number) => Math.round(n * 100) / 100; + // Two decimals at most, in the owner's locale: a German reader gets + // "80,5", not the "80.5" a bare number prints. + const fmtNum = (n: number) => fmt.number(Math.round(n * 100) / 100); const scope = makeLeafScope(selection, unavailableLeaves); return ( @@ -207,7 +209,7 @@ export function ClinicianView({ > ) : null} diff --git a/src/components/clinician/history-sections.tsx b/src/components/clinician/history-sections.tsx index 048dcf088..06feac289 100644 --- a/src/components/clinician/history-sections.tsx +++ b/src/components/clinician/history-sections.tsx @@ -53,7 +53,7 @@ export function LabResultsSection({ report: DoctorReportData; scope: LeafScope; fmtDate: (iso: string) => string; - fmtNum: (n: number) => number; + fmtNum: (n: number) => string; }) { const results = report.labResults ?? []; @@ -73,7 +73,7 @@ export function LabResultsSection({ ? lab.valueText : `${fmtNum(lab.value as number)} ${lab.unit}`.trim(); const range = (low: number | null, high: number | null) => - formatReferenceRange(low, high, (value) => String(fmtNum(value)), { + formatReferenceRange(low, high, fmtNum, { emptyText: "", }); // The window the reading was judged against, printed as the source diff --git a/src/components/clinician/report-sections.tsx b/src/components/clinician/report-sections.tsx index db44a8de2..d994310b2 100644 --- a/src/components/clinician/report-sections.tsx +++ b/src/components/clinician/report-sections.tsx @@ -204,7 +204,7 @@ export function MeasurementGroups({ t: Translate; report: DoctorReportData; scope: LeafScope; - fmtNum: (n: number) => number; + fmtNum: (n: number) => string; }) { const groups = REPORT_GROUPS.map((group) => ({ labelKey: group.labelKey, @@ -257,7 +257,7 @@ export function MeasurementGroups({ function statSummary( t: Translate, unit: string, - values: { latest: number; avg: number; min: number; max: number }, + values: { latest: string; avg: string; min: string; max: string }, ): string { return unit ? t("clinicianView.statSummaryWithUnit", { ...values, unit }) @@ -279,7 +279,7 @@ export function GlucoseSection({ t: Translate; report: DoctorReportData; scope: LeafScope; - fmtNum: (n: number) => number; + fmtNum: (n: number) => string; }) { const entries = Object.entries(report.glucoseStats).filter( ([, s]) => s.count > 0, @@ -506,7 +506,7 @@ export function WellnessSection({ t: Translate; report: DoctorReportData; scope: LeafScope; - fmtNum: (n: number) => number; + fmtNum: (n: number) => string; }) { const wellness = report.wellnessScores?.filter( diff --git a/src/components/clinician/sensitive-sections.tsx b/src/components/clinician/sensitive-sections.tsx index ae0ca8dc1..ebc620a62 100644 --- a/src/components/clinician/sensitive-sections.tsx +++ b/src/components/clinician/sensitive-sections.tsx @@ -89,7 +89,7 @@ export function MoodSection({ t: Translate; report: DoctorReportData; scope: LeafScope; - fmtNum: (n: number) => number; + fmtNum: (n: number) => string; }) { const mood = report.mood ?? null; const buckets = mood ? Object.entries(mood.distribution) : []; @@ -145,7 +145,7 @@ export function CycleSection({ report: DoctorReportData; scope: LeafScope; fmtDate: (iso: string) => string; - fmtNum: (n: number) => number; + fmtNum: (n: number) => string; }) { const cycle = report.cycle ?? null; // The summary carries plain dates (YYYY-MM-DD); midday keeps the rendered diff --git a/src/components/clinician/therapy-sections.tsx b/src/components/clinician/therapy-sections.tsx index 097eb6497..9d7f0a788 100644 --- a/src/components/clinician/therapy-sections.tsx +++ b/src/components/clinician/therapy-sections.tsx @@ -47,7 +47,7 @@ export function Glp1Section({ report: DoctorReportData; scope: LeafScope; fmtDate: (iso: string) => string; - fmtNum: (n: number) => number; + fmtNum: (n: number) => string; }) { const glp1 = report.glp1 ?? null; const medications = glp1?.medications ?? []; @@ -173,7 +173,7 @@ export function DoseLogSection({ report: DoctorReportData; scope: LeafScope; fmtDateTime: (iso: string) => string; - fmtNum: (n: number) => number; + fmtNum: (n: number) => string; }) { const administrations = report.medicationAdministrations ?? []; // Newest first: a clinician reads "when did they last take it", not "when