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.
This commit is contained in:
Marc-André Bombeck committed 2026-10-03 18:39:03 +02:00
1 parent fdd34f2c5e
commit 358ef23239
6 files changed
+42 -12

No files matched your search

@@ -253,3 +253,31 @@ describe("<ClinicianView> resolves every label in every locale", () => {
});
}
});
describe("<ClinicianView> 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");
});
});
+4 -2
View File
@@ -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({
>
<StatRow
label={t("clinicianView.bmi")}
value={String(fmtNum(report.bmi))}
value={fmtNum(report.bmi)}
/>
</Section>
) : null}
@@ -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
+4 -4
View File
@@ -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(
@@ -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
@@ -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