From 52c6c8dc9bb6c158abd9b4f87e4a8d355b643607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 20 Oct 2026 21:08:16 +0200 Subject: [PATCH] fix(demo): keep the demo dashboard to the measurement charts The shared demo account carries onboarding answers, so the setup checklist stayed on its dashboard for every visitor although none of its rows can be done there. Under DEMO_MODE the dashboard leaves it out, as the shell already does with the tour. The demo seed hides the mood and medication cards, achievements, recent workouts and check-ups, opens weight, BMI and blood pressure on 90 days, and resting heart rate on 30 with its 7-day trend and trend line. --- scripts/seed-demo.ts | 31 ++++++++++++++-- .../dashboard-demo-checklist.test.ts | 35 +++++++++++++++++++ src/app/page-client.tsx | 9 ++++- src/app/page.tsx | 11 ++++-- 4 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 src/app/__tests__/dashboard-demo-checklist.test.ts diff --git a/scripts/seed-demo.ts b/scripts/seed-demo.ts index 57abb04a7..aad6f50f2 100644 --- a/scripts/seed-demo.ts +++ b/scripts/seed-demo.ts @@ -90,6 +90,7 @@ import { import { ENVIRONMENT_LOCATION_AAD } from "../src/lib/crypto/encrypted-columns"; import { sealInsightsCachedText } from "../src/lib/insights/cached-text-cipher"; import { + DEFAULT_CHART_OVERLAY_PREFS, DEFAULT_DASHBOARD_LAYOUT, serializeDashboardLayout, } from "../src/lib/dashboard-layout"; @@ -497,16 +498,40 @@ async function seed() { // One row of six tiles (weight, the two blood-pressure values, resting // heart rate, HRV and mood) and the weight chart right below it, so the // first screen of the public demo shows the summary, the tiles and the - // start of a chart together. Every other tile stays one switch away in - // the layout settings; its data is seeded either way. Writing the layout - // verbatim keeps the demo's home page stable across default tweaks. + // start of a chart together. Below it only the measurement charts: the + // mood and medication cards, achievements, recent workouts and check-ups + // stay one switch away in the layout settings, their data seeded either + // way. Weight, BMI and blood pressure open on 90 days, resting heart rate + // on 30 with its 7-day trend and trend line, so each chart shows a shape + // rather than a flat week. Writing the layout verbatim keeps the demo's + // home page stable across default tweaks. const SHOWCASE_TILE_IDS = new Set(["weight", "bp", "pulse", "hrv", "mood"]); + const HIDDEN_CARD_IDS = new Set([ + "mood", + "medications", + "achievements", + "recentWorkouts", + "vorsorge", + ]); const demoDashboardLayout = serializeDashboardLayout({ ...DEFAULT_DASHBOARD_LAYOUT, widgets: DEFAULT_DASHBOARD_LAYOUT.widgets.map((w) => ({ ...w, + visible: w.visible && !HIDDEN_CARD_IDS.has(w.id), tileVisible: SHOWCASE_TILE_IDS.has(w.id), })), + chartOverlayPrefs: { + weight: { ...DEFAULT_CHART_OVERLAY_PREFS, rangePoints: 90 }, + bmi: { ...DEFAULT_CHART_OVERLAY_PREFS, rangePoints: 90 }, + bp: { ...DEFAULT_CHART_OVERLAY_PREFS, rangePoints: 90 }, + // The resting heart rate chart is the dashboard's `pulse` chart. + pulse: { + ...DEFAULT_CHART_OVERLAY_PREFS, + rangePoints: 30, + showTrendIndicator: true, + showTrendArrow: true, + }, + }, }); await client.query( `UPDATE users SET dashboard_widgets_json = $1 WHERE id = $2`, diff --git a/src/app/__tests__/dashboard-demo-checklist.test.ts b/src/app/__tests__/dashboard-demo-checklist.test.ts new file mode 100644 index 000000000..0e7f0b50f --- /dev/null +++ b/src/app/__tests__/dashboard-demo-checklist.test.ts @@ -0,0 +1,35 @@ +import { describe, it, expect } from "vitest"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +/** + * The setup checklist on a demo instance. + * + * The demo is one shared, read-only account carrying onboarding answers, so + * `isStillInSetup` keeps the checklist on screen for every visitor although + * none of its rows can be done there. The shell already leaves out the tour + * under `DEMO_MODE`; the dashboard leaves out the checklist the same way. + * Pinned here: every render path of the page threads the flag, and the + * client mounts the checklist only without it. + */ +const PAGE_PATH = join(process.cwd(), "src/app/page.tsx"); +const CLIENT_PATH = join(process.cwd(), "src/app/page-client.tsx"); + +describe("dashboard setup checklist on a demo instance", () => { + it("threads DEMO_MODE into every render of the dashboard client", () => { + const src = readFileSync(PAGE_PATH, "utf8"); + expect(src).toContain('process.env.DEMO_MODE === "true"'); + const renders = src.match(/]*>/g) ?? []; + expect(renders.length).toBeGreaterThan(0); + for (const render of renders) { + expect(render).toContain("demoMode={demoMode}"); + } + }); + + it("mounts the checklist only outside demo mode", () => { + const src = readFileSync(CLIENT_PATH, "utf8"); + const mounts = src.match(/]*\/>/g) ?? []; + expect(mounts).toHaveLength(1); + expect(src).toContain("{demoMode ? null : }"); + }); +}); diff --git a/src/app/page-client.tsx b/src/app/page-client.tsx index cb3aab93e..b72534282 100644 --- a/src/app/page-client.tsx +++ b/src/app/page-client.tsx @@ -193,6 +193,7 @@ import { export default function DashboardPageClient({ batchWindow: batchWindowProp, + demoMode = false, }: { /** * v1.30.9 — the batched-series ISO window computed server-side from the @@ -204,6 +205,12 @@ export default function DashboardPageClient({ * under, so the prefetched slice lands instead of triggering a refetch. */ batchWindow?: BatchWindow; + /** + * True on a `DEMO_MODE` instance. The demo is one shared, read-only + * account a visitor cannot set up, so the setup checklist has nothing to + * offer there, as the shell already leaves out the tour. + */ + demoMode?: boolean; } = {}) { const { isAuthenticated } = useAuth(); const mounted = useMounted(); @@ -2312,7 +2319,7 @@ export default function DashboardPageClient({ * content (tiles, charts) keeps a stable position, and the * near-empty dashboards of the new accounts the card targets keep * it visible without scrolling anyway. */} - + {demoMode ? null : } ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index 493c3f27e..48af2ec52 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -76,12 +76,15 @@ const DIGEST_SOFT_BUDGET_MS = 400; * others. */ export default async function DashboardPage() { + // `DEMO_MODE` is a server-only env var; resolved here and threaded in, as + // the layout does for the shell. + const demoMode = process.env.DEMO_MODE === "true"; // Operator/test escape hatch: `DASHBOARD_SSR_PREFETCH=false` renders the // pure client-fetch dashboard (pre-prefetch behaviour). The e2e server // sets it so Playwright route mocks — which only see CLIENT fetches — // keep governing what the dashboard paints. It gates EVERY prefetch below. if (process.env.DASHBOARD_SSR_PREFETCH === "false") { - return ; + return ; } let dehydratedState = null; let batchWindow: BatchWindow | undefined; @@ -171,11 +174,13 @@ export default async function DashboardPage() { } if (dehydratedState === null) { - return ; + return ( + + ); } return ( - + ); }