mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 22:26:13 +00:00
feat(insights): give every category page a metric explainer + normalise keys to english
Two parts to the explainer surface, landed together because they share the same i18n block and page props. Coverage: every routed insights category page now passes an `explainerMetric` prop, so the heading-side `?` glyph is present on all 37 pages instead of 13. The 24 newly covered metrics — body composition (body water, bone/fat/muscle/lean mass, fat-free mass, visceral fat), mobility (flights, walking distance/speed/steadiness, asymmetry, double-support, step length, walking heart rate), cardiovascular (pulse-wave velocity, vascular age), hearing (environmental + headphone levels, loud-sound events), respiratory rate, daylight, blood glucose, and skin temperature — each gain a one to two sentence layperson definition grounded in standard clinical references, shipped in all six locales. Keys: the explainer key segments were a German/English mix (`blutdruck`, `puls`, `gewicht`, `schlaf`, `stimmung`, `medikamente` alongside English ones). Rename them to English to match the naming-convention ADR — `bloodPressure`, `pulse`, `weight`, `sleep`, `mood`, `medications` — across every locale, and update the `explainerMetric` prop values that reference them. These are i18n key segments, not route slugs, so the slug contract is untouched.
This commit is contained in:
1 parent
8f120cfdef
commit
05097df1ee
37 files changed
+440
-101
No files matched your search
@@ -19,6 +19,7 @@ export default function InsightsLaermereignissePage() {
|
||||
insightMetric="AUDIO_EXPOSURE_EVENT"
|
||||
chartKey="audioExposureEvent"
|
||||
i18nPrefix="insights.audioExposureEvent"
|
||||
explainerMetric="audioEvents"
|
||||
color="#ff79c6"
|
||||
unit=""
|
||||
yAxisUnit=""
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsBlutzuckerPage() {
|
||||
insightMetric="BLOOD_GLUCOSE"
|
||||
chartKey="bloodGlucose"
|
||||
i18nPrefix="insights.bloodGlucose"
|
||||
explainerMetric="bloodGlucose"
|
||||
color="#ff79c6"
|
||||
unit="mg/dL"
|
||||
yAxisUnit="mg/dL"
|
||||
|
||||
@@ -52,7 +52,7 @@ export default function InsightsBlutdruckPage() {
|
||||
<SubPageShell
|
||||
title={t("insights.bloodPressureSectionTitle")}
|
||||
description={t("insights.subPage.blutdruckDescription")}
|
||||
explainerMetric="blutdruck"
|
||||
explainerMetric="bloodPressure"
|
||||
>
|
||||
<MetricEmptyState
|
||||
icon={<HeartPulse className="size-6" />}
|
||||
@@ -100,7 +100,7 @@ export default function InsightsBlutdruckPage() {
|
||||
<SubPageShell
|
||||
title={t("insights.bloodPressureSectionTitle")}
|
||||
description={t("insights.subPage.blutdruckDescription")}
|
||||
explainerMetric="blutdruck"
|
||||
explainerMetric="bloodPressure"
|
||||
>
|
||||
<HealthChartDynamic
|
||||
chartKey="bp"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsKoerperwasserPage() {
|
||||
insightMetric="TOTAL_BODY_WATER"
|
||||
chartKey="totalBodyWater"
|
||||
i18nPrefix="insights.totalBodyWater"
|
||||
explainerMetric="bodyWater"
|
||||
color="#8be9fd"
|
||||
unit="kg"
|
||||
yAxisUnit="kg"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsKnochenmassePage() {
|
||||
insightMetric="BONE_MASS"
|
||||
chartKey="boneMass"
|
||||
i18nPrefix="insights.boneMass"
|
||||
explainerMetric="boneMass"
|
||||
color="#ffb86c"
|
||||
unit="kg"
|
||||
yAxisUnit="kg"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsTageslichtPage() {
|
||||
insightMetric="TIME_IN_DAYLIGHT"
|
||||
chartKey="timeInDaylight"
|
||||
i18nPrefix="insights.timeInDaylight"
|
||||
explainerMetric="daylight"
|
||||
color="#50fa7b"
|
||||
unit="min"
|
||||
yAxisUnit="min"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsDoppelstandphasePage() {
|
||||
insightMetric="WALKING_DOUBLE_SUPPORT"
|
||||
chartKey="walkingDoubleSupport"
|
||||
i18nPrefix="insights.walkingDoubleSupport"
|
||||
explainerMetric="doubleSupportTime"
|
||||
color="#50fa7b"
|
||||
unit="%"
|
||||
yAxisUnit="%"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsLaermbelastungPage() {
|
||||
insightMetric="AUDIO_EXPOSURE_ENV"
|
||||
chartKey="audioExposureEnv"
|
||||
i18nPrefix="insights.audioExposureEnv"
|
||||
explainerMetric="environmentalAudio"
|
||||
color="#ff79c6"
|
||||
unit="dBA"
|
||||
yAxisUnit="dBA"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsFettfreieMassePage() {
|
||||
insightMetric="FAT_FREE_MASS"
|
||||
chartKey="fatFreeMass"
|
||||
i18nPrefix="insights.fatFreeMass"
|
||||
explainerMetric="fatFreeMass"
|
||||
color="#bd93f9"
|
||||
unit="kg"
|
||||
yAxisUnit="kg"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsFettmassePage() {
|
||||
insightMetric="FAT_MASS"
|
||||
chartKey="fatMass"
|
||||
i18nPrefix="insights.fatMass"
|
||||
explainerMetric="fatMass"
|
||||
color="#ffb86c"
|
||||
unit="kg"
|
||||
yAxisUnit="kg"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsStockwerkePage() {
|
||||
insightMetric="FLIGHTS_CLIMBED"
|
||||
chartKey="flightsClimbed"
|
||||
i18nPrefix="insights.flightsClimbed"
|
||||
explainerMetric="flightsClimbed"
|
||||
color="#50fa7b"
|
||||
unit="flights"
|
||||
yAxisUnit="flights"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsKopfhoererpegelPage() {
|
||||
insightMetric="AUDIO_EXPOSURE_HEADPHONE"
|
||||
chartKey="audioExposureHeadphone"
|
||||
i18nPrefix="insights.audioExposureHeadphone"
|
||||
explainerMetric="headphoneAudio"
|
||||
color="#ff79c6"
|
||||
unit="dBA"
|
||||
yAxisUnit="dBA"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsMagermassePage() {
|
||||
insightMetric="LEAN_BODY_MASS"
|
||||
chartKey="leanBodyMass"
|
||||
i18nPrefix="insights.leanBodyMass"
|
||||
explainerMetric="leanBodyMass"
|
||||
color="#bd93f9"
|
||||
unit="kg"
|
||||
yAxisUnit="kg"
|
||||
|
||||
@@ -114,7 +114,7 @@ export default function InsightsMedikamentePage() {
|
||||
return (
|
||||
<SubPageShell
|
||||
title={t("insights.medicationCompliance")}
|
||||
explainerMetric="medikamente"
|
||||
explainerMetric="medications"
|
||||
>
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<Loader2 className="text-primary h-6 w-6 animate-spin motion-reduce:animate-none" />
|
||||
@@ -130,7 +130,7 @@ export default function InsightsMedikamentePage() {
|
||||
return (
|
||||
<SubPageShell
|
||||
title={t("insights.medicationCompliance")}
|
||||
explainerMetric="medikamente"
|
||||
explainerMetric="medications"
|
||||
>
|
||||
<EmptyState
|
||||
icon={<Pill className="size-6" />}
|
||||
@@ -156,7 +156,7 @@ export default function InsightsMedikamentePage() {
|
||||
<SubPageShell
|
||||
title={t("insights.medicationCompliance")}
|
||||
description={t("insights.subPage.medikamenteDescription")}
|
||||
explainerMetric="medikamente"
|
||||
explainerMetric="medications"
|
||||
>
|
||||
<div
|
||||
className={
|
||||
|
||||
@@ -76,7 +76,7 @@ export default function InsightsStimmungPage() {
|
||||
<SubPageShell
|
||||
title={t("insights.moodSectionTitle")}
|
||||
description={t("insights.subPage.stimmungDescription")}
|
||||
explainerMetric="stimmung"
|
||||
explainerMetric="mood"
|
||||
>
|
||||
<MetricEmptyState
|
||||
icon={<Smile className="size-6" />}
|
||||
@@ -97,7 +97,7 @@ export default function InsightsStimmungPage() {
|
||||
<SubPageShell
|
||||
title={t("insights.moodSectionTitle")}
|
||||
description={t("insights.subPage.stimmungDescription")}
|
||||
explainerMetric="stimmung"
|
||||
explainerMetric="mood"
|
||||
>
|
||||
<MoodChart
|
||||
chartKey="mood"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsMuskelmassePage() {
|
||||
insightMetric="MUSCLE_MASS"
|
||||
chartKey="muscleMass"
|
||||
i18nPrefix="insights.muscleMass"
|
||||
explainerMetric="muscleMass"
|
||||
color="#bd93f9"
|
||||
unit="kg"
|
||||
yAxisUnit="kg"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsPulswellengeschwindigkeitPage() {
|
||||
insightMetric="PULSE_WAVE_VELOCITY"
|
||||
chartKey="pulseWaveVelocity"
|
||||
i18nPrefix="insights.pulseWaveVelocity"
|
||||
explainerMetric="pulseWaveVelocity"
|
||||
color="#ff5555"
|
||||
unit="m/s"
|
||||
yAxisUnit="m/s"
|
||||
|
||||
@@ -62,7 +62,7 @@ export default function InsightsPulsPage() {
|
||||
<SubPageShell
|
||||
title={t("insights.pulseSectionTitle")}
|
||||
description={t("insights.subPage.pulsDescription")}
|
||||
explainerMetric="puls"
|
||||
explainerMetric="pulse"
|
||||
>
|
||||
<MetricEmptyState
|
||||
icon={<Heart className="size-6" />}
|
||||
@@ -113,7 +113,7 @@ export default function InsightsPulsPage() {
|
||||
<SubPageShell
|
||||
title={t("insights.pulseSectionTitle")}
|
||||
description={t("insights.subPage.pulsDescription")}
|
||||
explainerMetric="puls"
|
||||
explainerMetric="pulse"
|
||||
>
|
||||
<HealthChartDynamic
|
||||
chartKey="pulse"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsAtemfrequenzPage() {
|
||||
insightMetric="RESPIRATORY_RATE"
|
||||
chartKey="respiratoryRate"
|
||||
i18nPrefix="insights.respiratoryRate"
|
||||
explainerMetric="respiratoryRate"
|
||||
color="#8be9fd"
|
||||
unit="breaths/min"
|
||||
yAxisUnit="breaths/min"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsHauttemperaturPage() {
|
||||
insightMetric="SKIN_TEMPERATURE"
|
||||
chartKey="skinTemperature"
|
||||
i18nPrefix="insights.skinTemperature"
|
||||
explainerMetric="skinTemperature"
|
||||
color="#ffb86c"
|
||||
unit="°C"
|
||||
yAxisUnit="°C"
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function InsightsSchlafPage() {
|
||||
<SubPageShell
|
||||
title={t("insights.sleep.title")}
|
||||
description={t("insights.sleep.description")}
|
||||
explainerMetric="schlaf"
|
||||
explainerMetric="sleep"
|
||||
>
|
||||
<MetricEmptyState
|
||||
icon={<Moon className="size-6" />}
|
||||
@@ -61,7 +61,7 @@ export default function InsightsSchlafPage() {
|
||||
<SubPageShell
|
||||
title={t("insights.sleep.title")}
|
||||
description={t("insights.sleep.description")}
|
||||
explainerMetric="schlaf"
|
||||
explainerMetric="sleep"
|
||||
>
|
||||
<SleepOverview />
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsSchrittlaengePage() {
|
||||
insightMetric="WALKING_STEP_LENGTH"
|
||||
chartKey="walkingStepLength"
|
||||
i18nPrefix="insights.walkingStepLength"
|
||||
explainerMetric="stepLength"
|
||||
color="#50fa7b"
|
||||
unit="m"
|
||||
yAxisUnit="m"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsGefaessalterPage() {
|
||||
insightMetric="VASCULAR_AGE"
|
||||
chartKey="vascularAge"
|
||||
i18nPrefix="insights.vascularAge"
|
||||
explainerMetric="vascularAge"
|
||||
color="#ff79c6"
|
||||
unit="years"
|
||||
yAxisUnit="years"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsViszeralfettPage() {
|
||||
insightMetric="VISCERAL_FAT"
|
||||
chartKey="visceralFat"
|
||||
i18nPrefix="insights.visceralFat"
|
||||
explainerMetric="visceralFat"
|
||||
color="#ffb86c"
|
||||
unit=""
|
||||
yAxisUnit=""
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsGangasymmetriePage() {
|
||||
insightMetric="WALKING_ASYMMETRY"
|
||||
chartKey="walkingAsymmetry"
|
||||
i18nPrefix="insights.walkingAsymmetry"
|
||||
explainerMetric="walkingAsymmetry"
|
||||
color="#50fa7b"
|
||||
unit="%"
|
||||
yAxisUnit="%"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsGehstreckePage() {
|
||||
insightMetric="WALKING_RUNNING_DISTANCE"
|
||||
chartKey="walkingRunningDistance"
|
||||
i18nPrefix="insights.walkingRunningDistance"
|
||||
explainerMetric="walkingDistance"
|
||||
color="#50fa7b"
|
||||
unit="km"
|
||||
yAxisUnit="km"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsGehpulsPage() {
|
||||
insightMetric="WALKING_HEART_RATE_AVERAGE"
|
||||
chartKey="walkingHeartRateAverage"
|
||||
i18nPrefix="insights.walkingHeartRateAverage"
|
||||
explainerMetric="walkingHeartRate"
|
||||
color="#ff5555"
|
||||
unit="bpm"
|
||||
yAxisUnit="bpm"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsGehgeschwindigkeitPage() {
|
||||
insightMetric="WALKING_SPEED"
|
||||
chartKey="walkingSpeed"
|
||||
i18nPrefix="insights.walkingSpeed"
|
||||
explainerMetric="walkingSpeed"
|
||||
color="#50fa7b"
|
||||
unit="km/h"
|
||||
yAxisUnit="km/h"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function InsightsGangstabilitaetPage() {
|
||||
insightMetric="WALKING_STEADINESS"
|
||||
chartKey="walkingSteadiness"
|
||||
i18nPrefix="insights.walkingSteadiness"
|
||||
explainerMetric="walkingSteadiness"
|
||||
color="#50fa7b"
|
||||
unit="%"
|
||||
yAxisUnit="%"
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function InsightsGewichtPage() {
|
||||
<SubPageShell
|
||||
title={t("insights.weightSectionTitle")}
|
||||
description={t("insights.subPage.gewichtDescription")}
|
||||
explainerMetric="gewicht"
|
||||
explainerMetric="weight"
|
||||
>
|
||||
<MetricEmptyState
|
||||
icon={<Scale className="size-6" />}
|
||||
@@ -74,7 +74,7 @@ export default function InsightsGewichtPage() {
|
||||
<SubPageShell
|
||||
title={t("insights.weightSectionTitle")}
|
||||
description={t("insights.subPage.gewichtDescription")}
|
||||
explainerMetric="gewicht"
|
||||
explainerMetric="weight"
|
||||
>
|
||||
<HealthChartDynamic
|
||||
chartKey="weight"
|
||||
|
||||
@@ -41,25 +41,52 @@ function KeyProbe({ metric }: { metric: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
// v1.8.0 — English key segments (ADR-0001). Every routed insights
|
||||
// category page now passes one of these; the explainer copy ships in
|
||||
// all six locales.
|
||||
const METRICS = [
|
||||
"blutdruck",
|
||||
"puls",
|
||||
"gewicht",
|
||||
"bloodPressure",
|
||||
"pulse",
|
||||
"weight",
|
||||
"bmi",
|
||||
"schlaf",
|
||||
"stimmung",
|
||||
"medikamente",
|
||||
"sleep",
|
||||
"mood",
|
||||
"medications",
|
||||
"restingHr",
|
||||
"hrv",
|
||||
"oxygenSaturation",
|
||||
"bodyTemperature",
|
||||
"activeEnergy",
|
||||
"workouts",
|
||||
"respiratoryRate",
|
||||
"bodyWater",
|
||||
"boneMass",
|
||||
"fatFreeMass",
|
||||
"fatMass",
|
||||
"muscleMass",
|
||||
"visceralFat",
|
||||
"leanBodyMass",
|
||||
"flightsClimbed",
|
||||
"walkingDistance",
|
||||
"walkingSteadiness",
|
||||
"walkingHeartRate",
|
||||
"walkingAsymmetry",
|
||||
"doubleSupportTime",
|
||||
"stepLength",
|
||||
"walkingSpeed",
|
||||
"pulseWaveVelocity",
|
||||
"vascularAge",
|
||||
"environmentalAudio",
|
||||
"headphoneAudio",
|
||||
"audioEvents",
|
||||
"daylight",
|
||||
"bloodGlucose",
|
||||
"skinTemperature",
|
||||
] as const;
|
||||
|
||||
describe("<MetricExplainer>", () => {
|
||||
it("renders an icon-only trigger button with a metric-specific accessible label (EN)", () => {
|
||||
const html = ssr(<MetricExplainer metric="blutdruck" />);
|
||||
const html = ssr(<MetricExplainer metric="bloodPressure" />);
|
||||
const trigger = html.match(
|
||||
/<button[^>]*data-slot="metric-explainer-trigger"[^>]*>/,
|
||||
);
|
||||
@@ -72,7 +99,7 @@ describe("<MetricExplainer>", () => {
|
||||
// `min-h-11 min-w-11`; the surrounding heading row keeps its stride
|
||||
// because `-my-3 -mx-2` swallows the extra reach. Pinned so a future
|
||||
// refactor can't collapse the hit surface back to the visual chip.
|
||||
const html = ssr(<MetricExplainer metric="puls" />);
|
||||
const html = ssr(<MetricExplainer metric="pulse" />);
|
||||
const trigger = html.match(
|
||||
/<button[^>]*data-slot="metric-explainer-trigger"[^>]*>/,
|
||||
);
|
||||
@@ -94,13 +121,13 @@ describe("<MetricExplainer>", () => {
|
||||
// the user activates the trigger. Pinning the negative keeps a
|
||||
// future "open on render" refactor from leaking copy into the
|
||||
// static markup.
|
||||
const html = ssr(<MetricExplainer metric="gewicht" />);
|
||||
const html = ssr(<MetricExplainer metric="weight" />);
|
||||
expect(html).not.toContain('data-slot="metric-explainer-body"');
|
||||
expect(html).not.toContain('data-slot="metric-explainer-title"');
|
||||
});
|
||||
|
||||
it("localises the trigger label in German", () => {
|
||||
const html = ssr(<MetricExplainer metric="schlaf" />, "de");
|
||||
const html = ssr(<MetricExplainer metric="sleep" />, "de");
|
||||
const trigger = html.match(
|
||||
/<button[^>]*data-slot="metric-explainer-trigger"[^>]*>/,
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user