mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
fix(insights): ask the empty-state Coach question in the reader's language
A metric page without data offers to ask the Coach about the metric. 44 of the 49 pages passed that question as an English literal, so a German, French or Korean reader got an English first message; only 5 read it from the catalog. 31 of the 44 were one sentence with the metric's name swapped in. `HealthKitMetricPage` now asks that sentence once, translated, around the page's own title, and the 31 pages drop the prop. The 13 pages with a question of their own (steps, HRV, sleep, mood, workouts, blood pressure and others) read it from the catalog like the 5 did, in all ten languages.
This commit is contained in:
1 parent
b970ea7468
commit
eec52534d5
57 files changed
+395
-189
No files matched your search
@@ -0,0 +1,74 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
import { I18nProvider } from "@/lib/i18n/context";
|
||||
import { primeMessages } from "@/lib/i18n/load-locale";
|
||||
import deMessages from "../../../../messages/de.json";
|
||||
|
||||
primeMessages("de", deMessages);
|
||||
|
||||
/**
|
||||
* A metric page with no data offers the Coach a question in the reader's
|
||||
* language. The pages used to pass their own English sentence, and most of
|
||||
* them were one sentence with the metric's name swapped in; the page now asks
|
||||
* that one, translated, with its own title, and a metric with a question of
|
||||
* its own reads it from the catalog too.
|
||||
*/
|
||||
|
||||
const prefills: Array<string | null | undefined> = [];
|
||||
vi.mock("@/components/insights/metric-empty-state", () => ({
|
||||
MetricEmptyState: ({ coachPrefill }: { coachPrefill?: string | null }) => {
|
||||
prefills.push(coachPrefill);
|
||||
return null;
|
||||
},
|
||||
}));
|
||||
vi.mock("@/hooks/use-ai-capability", () => ({
|
||||
useAiCapability: () => ({ available: true, reason: null }),
|
||||
}));
|
||||
vi.mock("next/dynamic", () => ({ default: () => () => null }));
|
||||
vi.mock("next/navigation", () => ({
|
||||
usePathname: () => "/insights/visceral-fat",
|
||||
}));
|
||||
vi.mock("@/hooks/use-auth", () => ({
|
||||
useAuth: () => ({
|
||||
user: { timezone: "UTC", dateOfBirth: null, gender: null },
|
||||
isAuthenticated: true,
|
||||
}),
|
||||
}));
|
||||
vi.mock("@/hooks/use-insights-layout-prefs", () => ({
|
||||
useInsightsLayoutPrefs: () => ({ layout: null, compareBaseline: false }),
|
||||
}));
|
||||
vi.mock("@/hooks/use-insights-analytics", () => ({
|
||||
useInsightsAnalytics: () => ({ data: { summaries: {} }, isEmpty: true }),
|
||||
}));
|
||||
|
||||
import InsightsViszeralfettPage from "@/app/insights/visceral-fat/page";
|
||||
import InsightsStepsPage from "@/app/insights/steps/page";
|
||||
|
||||
function emptyStatePrefill(node: React.ReactNode, locale: "en" | "de") {
|
||||
prefills.length = 0;
|
||||
renderToStaticMarkup(
|
||||
<QueryClientProvider client={new QueryClient()}>
|
||||
<I18nProvider initialLocale={locale}>{node}</I18nProvider>
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
return prefills.at(-1);
|
||||
}
|
||||
|
||||
describe("the empty metric page's Coach question", () => {
|
||||
it("is the shared sentence with the page's title, in the reader's language", () => {
|
||||
expect(emptyStatePrefill(<InsightsViszeralfettPage />, "de")).toBe(
|
||||
"Viszeralfett: Ich habe hier noch nichts erfasst. Was sagt dieser Wert über meine Gesundheit aus, und wie verbessere ich ihn?",
|
||||
);
|
||||
expect(emptyStatePrefill(<InsightsViszeralfettPage />, "en")).toBe(
|
||||
"Visceral Fat: I haven't logged anything here yet. What does this metric tell me about my health, and how do I improve it?",
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps a metric's own question, translated", () => {
|
||||
expect(emptyStatePrefill(<InsightsStepsPage />, "de")).toBe(
|
||||
"Ich habe noch keine Schritte erfasst. Was ist ein sinnvolles tägliches Schrittziel, und wie hilft mehr Gehen meiner Gesundheit?",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -148,9 +148,8 @@ export interface HealthKitMetricPageProps {
|
||||
*/
|
||||
statIcon?: ComponentType<{ className?: string }>;
|
||||
/**
|
||||
* Optional Coach prefill for the empty-state launch. Falls back to a
|
||||
* generic onboarding prompt threaded through `<CoachLaunchButton>`'s
|
||||
* default.
|
||||
* The empty state's Coach question, for a metric that asks its own. Without
|
||||
* it the page asks the shared, translated one with its own title.
|
||||
*/
|
||||
coachPrefill?: string;
|
||||
/**
|
||||
@@ -223,6 +222,17 @@ export interface HealthKitMetricPageProps {
|
||||
afterAssessment?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The empty state's Coach question for a metric that asks none of its own:
|
||||
* one translated sentence around the page's title.
|
||||
*/
|
||||
function sharedCoachPrefill(
|
||||
t: ReturnType<typeof useTranslations>["t"],
|
||||
metric: string,
|
||||
): string {
|
||||
return t("insights.subPage.emptyCoachPrefill", { metric });
|
||||
}
|
||||
|
||||
export function HealthKitMetricPage({
|
||||
measurementType,
|
||||
fallbackMeasurementType,
|
||||
@@ -424,7 +434,7 @@ export function HealthKitMetricPage({
|
||||
title={t(`${i18nPrefix}.emptyState.title`)}
|
||||
description={t(`${i18nPrefix}.emptyState.description`)}
|
||||
cta={ctaNode}
|
||||
coachPrefill={coachPrefill ?? null}
|
||||
coachPrefill={coachPrefill ?? sharedCoachPrefill(t, title)}
|
||||
/>
|
||||
</SubPageShell>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user