fix(insights): say a steady usual value as one figure, not a range of equal ends

This commit is contained in:
Marc-André Bombeck committed 2026-10-06 19:41:39 +02:00
1 parent 0ba698c54f
commit 8f58cad7f3
9 files changed
+103 -2

No files matched your search

+3
View File
@@ -3030,6 +3030,9 @@
"baselineWithin": "Dein üblicher Bereich liegt bei {low}–{high} {unit}; heute liegt {value} darin.",
"baselineAbove": "Dein üblicher Bereich liegt bei {low}–{high} {unit}; heute liegt {value} darüber.",
"baselineBelow": "Dein üblicher Bereich liegt bei {low}–{high} {unit}; heute liegt {value} darunter.",
"steadyWithin": "Deine Werte liegen meist bei {low} {unit}; heute passt {value} dazu.",
"steadyAbove": "Deine Werte liegen meist bei {low} {unit}; heute liegt {value} darüber.",
"steadyBelow": "Deine Werte liegen meist bei {low} {unit}; heute liegt {value} darunter.",
"insufficient": "Lerne noch deinen üblichen Bereich — protokolliere noch ein paar Tage weiter.",
"driver": "{note}"
},
+3
View File
@@ -3030,6 +3030,9 @@
"baselineWithin": "Your usual range is {low}–{high} {unit}; today's {value} sits within it.",
"baselineAbove": "Your usual range is {low}–{high} {unit}; today's {value} sits above it.",
"baselineBelow": "Your usual range is {low}–{high} {unit}; today's {value} sits below it.",
"steadyWithin": "Your readings usually sit at {low} {unit}; today's {value} matches.",
"steadyAbove": "Your readings usually sit at {low} {unit}; today's {value} is above that.",
"steadyBelow": "Your readings usually sit at {low} {unit}; today's {value} is below that.",
"insufficient": "Still learning your usual range — keep logging for a few more days.",
"driver": "{note}"
},
+3
View File
@@ -3030,6 +3030,9 @@
"baselineWithin": "Tu rango habitual es {low}–{high} {unit}; hoy {value} se sitúa dentro de él.",
"baselineAbove": "Tu rango habitual es {low}–{high} {unit}; hoy {value} se sitúa por encima.",
"baselineBelow": "Tu rango habitual es {low}–{high} {unit}; hoy {value} se sitúa por debajo.",
"steadyWithin": "Tus valores suelen estar en {low} {unit}; hoy {value} coincide.",
"steadyAbove": "Tus valores suelen estar en {low} {unit}; hoy {value} está por encima.",
"steadyBelow": "Tus valores suelen estar en {low} {unit}; hoy {value} está por debajo.",
"insufficient": "Todavía estoy aprendiendo tu rango habitual — sigue registrando unos días más.",
"driver": "{note}"
},
+3
View File
@@ -3030,6 +3030,9 @@
"baselineWithin": "Ta plage habituelle est de {low}–{high} {unit} ; aujourd'hui, {value} s'y situe.",
"baselineAbove": "Ta plage habituelle est de {low}–{high} {unit} ; aujourd'hui, {value} se situe au-dessus.",
"baselineBelow": "Ta plage habituelle est de {low}–{high} {unit} ; aujourd'hui, {value} se situe en dessous.",
"steadyWithin": "Tes valeurs sont habituellement à {low} {unit} ; aujourd'hui, {value} correspond.",
"steadyAbove": "Tes valeurs sont habituellement à {low} {unit} ; aujourd'hui, {value} se situe au-dessus.",
"steadyBelow": "Tes valeurs sont habituellement à {low} {unit} ; aujourd'hui, {value} se situe en dessous.",
"insufficient": "J'apprends encore ta plage habituelle — continue à enregistrer quelques jours de plus.",
"driver": "{note}"
},
+3
View File
@@ -3030,6 +3030,9 @@
"baselineWithin": "Il tuo intervallo abituale è {low}–{high} {unit}; oggi {value} rientra al suo interno.",
"baselineAbove": "Il tuo intervallo abituale è {low}–{high} {unit}; oggi {value} si colloca al di sopra.",
"baselineBelow": "Il tuo intervallo abituale è {low}–{high} {unit}; oggi {value} si colloca al di sotto.",
"steadyWithin": "I tuoi valori sono di solito {low} {unit}; oggi {value} corrisponde.",
"steadyAbove": "I tuoi valori sono di solito {low} {unit}; oggi {value} è al di sopra.",
"steadyBelow": "I tuoi valori sono di solito {low} {unit}; oggi {value} è al di sotto.",
"insufficient": "Sto ancora imparando il tuo intervallo abituale — continua a registrare per qualche giorno in più.",
"driver": "{note}"
},
+3
View File
@@ -3030,6 +3030,9 @@
"baselineWithin": "평소 범위: {low}–{high} {unit}. 오늘 값 {value} — 범위 안이에요.",
"baselineAbove": "평소 범위: {low}–{high} {unit}. 오늘 값 {value} — 범위 위예요.",
"baselineBelow": "평소 범위: {low}–{high} {unit}. 오늘 값 {value} — 범위 아래예요.",
"steadyWithin": "평소 값: {low} {unit}. 오늘 값 {value} — 평소와 같아요.",
"steadyAbove": "평소 값: {low} {unit}. 오늘 값 {value} — 평소보다 높아요.",
"steadyBelow": "평소 값: {low} {unit}. 오늘 값 {value} — 평소보다 낮아요.",
"insufficient": "아직 평소 범위를 배우는 중이에요 — 며칠 더 기록해 주세요.",
"driver": "{note}"
},
+3
View File
@@ -3030,6 +3030,9 @@
"baselineWithin": "Twój zwykły zakres to {low}–{high} {unit}; dzisiejsza wartość {value} mieści się w nim.",
"baselineAbove": "Twój zwykły zakres to {low}–{high} {unit}; dzisiejsza wartość {value} jest powyżej niego.",
"baselineBelow": "Twój zwykły zakres to {low}–{high} {unit}; dzisiejsza wartość {value} jest poniżej niego.",
"steadyWithin": "Twoje pomiary zwykle wynoszą {low} {unit}; dzisiejsza wartość {value} jest taka sama.",
"steadyAbove": "Twoje pomiary zwykle wynoszą {low} {unit}; dzisiejsza wartość {value} jest wyższa.",
"steadyBelow": "Twoje pomiary zwykle wynoszą {low} {unit}; dzisiejsza wartość {value} jest niższa.",
"insufficient": "Wciąż poznaję twój zwykły zakres — rejestruj jeszcze przez kilka dni.",
"driver": "{note}"
},
@@ -0,0 +1,72 @@
import { describe, it, expect, vi } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { I18nProvider } from "@/lib/i18n/context";
import { queryKeys } from "@/lib/query-keys";
import type { CoachReadStripData } from "@/lib/insights/derived/coach-read-shape";
/**
* A band whose two ends format to the same figure is a steady value.
*
* With many identical daily readings the robust band collapses, and the strip
* read "Your usual range is 61–61 bpm". It now names the one value instead,
* and keeps the placement of today's reading.
*/
vi.mock("@/hooks/use-auth", () => ({
useAuth: () => ({
user: { timezone: "UTC", dateOfBirth: null, gender: null },
isAuthenticated: true,
}),
}));
vi.mock("@/hooks/use-mounted", () => ({ useMounted: () => true }));
import { CoachReadStrip } from "../coach-read-strip";
const METRIC = "RESTING_HEART_RATE";
function render(data: CoachReadStripData): string {
const client = new QueryClient({
defaultOptions: { queries: { retry: false, staleTime: Infinity } },
});
client.setQueryData(queryKeys.insightsCoachRead(METRIC, "en"), data);
return renderToStaticMarkup(
<QueryClientProvider client={client}>
<I18nProvider initialLocale="en">
<CoachReadStrip metricType={METRIC} unit="bpm" />
</I18nProvider>
</QueryClientProvider>,
);
}
function band(
low: number,
high: number,
latest: number,
placement: "within" | "above" | "below",
): CoachReadStripData {
return {
baseline: { low, high, latest, placement, sampleDays: 30 },
learning: false,
driver: null,
};
}
describe("CoachReadStrip — steady band", () => {
it("names one value when both ends format alike", () => {
const markup = render(band(61, 61.04, 61, "within"));
expect(markup).not.toContain("61–61");
expect(markup).toContain("usually sit at 61");
});
it("keeps the placement for a steady value", () => {
expect(render(band(61, 61, 66, "above"))).toContain("is above that");
expect(render(band(61, 61, 57, "below"))).toContain("is below that");
});
it("still shows a real range as a range", () => {
expect(render(band(58, 64, 61, "within"))).toContain("58–64");
});
});
@@ -106,8 +106,16 @@ export function CoachReadStrip({
return t("insights.coach.readStrip.insufficient");
}
const { low, high, latest, placement } = data.baseline;
const key =
placement === "above"
// A band whose ends format to the same figure ("61–61 bpm") is a steady
// value, not a range; say it as one.
const steady = fmt(low) === fmt(high);
const key = steady
? placement === "above"
? "insights.coach.readStrip.steadyAbove"
: placement === "below"
? "insights.coach.readStrip.steadyBelow"
: "insights.coach.readStrip.steadyWithin"
: placement === "above"
? "insights.coach.readStrip.baselineAbove"
: placement === "below"
? "insights.coach.readStrip.baselineBelow"