mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 22:32:04 +00:00
feat(dashboard): promote a Today hero above the tile strip
Surface the day's read at the top of the dashboard: a Today hero that renders the daily digest — the health score with an honest provisional/final and sleep-pending face, the briefing lead with a read-the-full-briefing affordance, and the worth-a-look rail of priority cards. The dense tile grid and chart rows stay exactly where they are, one scroll below, so muscle memory survives and no new nav destination is added. The hero reads one cached digest cell (GET /api/daily/digest) through the centralised query-key factory and unwraps the envelope; it recomputes nothing and never warms a provider on mount. It is gated on the insights module and self-gates its empty state: a genuinely empty account renders nothing rather than an alarming card, while an account with a score but nothing notable gets a calm all-clear line in the rail's place. A shimmer skeleton holds the footprint while the digest loads. Priority-card actions wire to their existing destinations by construction (log dose, reconnect a sync, view check-ups). New copy lands in all six locales.
This commit is contained in:
1 parent
ba6c0990bf
commit
168c1c85d7
14 files changed
+581
No files matched your search
@@ -98,6 +98,45 @@ test.describe("authenticated dashboard render", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
// S2 — the Today hero reads the unified daily digest from
|
||||
// `/api/daily/digest`. Mock it with a deterministic digest (a score +
|
||||
// one worth-a-look item) so the hero paints without reaching the real
|
||||
// route, and so a slow/absent digest can never hang the render or
|
||||
// surface a console error under the significant-error gate below.
|
||||
await page.route(/\/api\/daily\/digest(\?|$)/, (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
data: {
|
||||
generatedAt: new Date().toISOString(),
|
||||
phase: "final",
|
||||
sleepPending: false,
|
||||
score: { value: 82, band: "green", delta: 2 },
|
||||
topSignal: null,
|
||||
briefingLead: "Your week is trending steady.",
|
||||
line: "Your week is trending steady.",
|
||||
worthALook: [
|
||||
{
|
||||
kind: "sync_issue",
|
||||
title: "Sync needs attention",
|
||||
body: "Withings isn't syncing.",
|
||||
status: "warning",
|
||||
actions: [
|
||||
{
|
||||
labelKey: "daily.action.reconnect",
|
||||
intent: "sync.reconnect",
|
||||
href: "/settings/integrations",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
error: null,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
// v1.18.6 — the dashboard now batches every visible chart's daily
|
||||
// series into ONE `/api/measurements/series-batch` round-trip and
|
||||
// hands each chart its slice via `preloadedSeries`. The route is a
|
||||
@@ -209,6 +248,17 @@ test.describe("authenticated dashboard render", () => {
|
||||
const tileCount = Number(await strip.getAttribute("data-tile-count"));
|
||||
expect(tileCount).toBeGreaterThan(0);
|
||||
|
||||
// S2 — the Today hero paints above the tile strip from the mocked
|
||||
// digest: its score ring and its worth-a-look rail render.
|
||||
const todayHero = page.locator('[data-slot="today-hero"]');
|
||||
await expect(todayHero).toBeVisible({ timeout: 10_000 });
|
||||
await expect(
|
||||
todayHero.locator('[data-slot="today-hero-score"]'),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
todayHero.locator('[data-slot="today-hero-rail"]'),
|
||||
).toBeVisible();
|
||||
|
||||
// At least one chart must render. Recharts mounts a `<svg>` with
|
||||
// `class="recharts-surface"` (or a `<div class="recharts-wrapper">`
|
||||
// around it). Use the recharts class so we don't accidentally match
|
||||
|
||||
Reference in new issue
Block a user