mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
Merge dashboard discoverability flips into release/v1.20.0
Default the built, data-gated sleep, steps and glucose dashboard tiles to visible (each self-hides without data) and surface the Vorsorge chart row, so shipped capabilities are discoverable on the home dashboard. Seed the demo with an explicit showcase layout.
This commit is contained in:
commit
ff06689a1b
4 files changed
+77
-18
No files matched your search
@@ -48,6 +48,10 @@ import { Buffer } from "node:buffer";
|
||||
import pg from "pg";
|
||||
import { encryptToBytes } from "../src/lib/ai/coach/bytes-codec";
|
||||
import { encrypt } from "../src/lib/crypto";
|
||||
import {
|
||||
DEFAULT_DASHBOARD_LAYOUT,
|
||||
serializeDashboardLayout,
|
||||
} from "../src/lib/dashboard-layout";
|
||||
import { VALUE_RANGES } from "../src/lib/validations/measurement";
|
||||
|
||||
const DATABASE_URL = process.env.DATABASE_URL;
|
||||
@@ -253,6 +257,32 @@ async function seed() {
|
||||
],
|
||||
);
|
||||
|
||||
// ── Dashboard showcase layout ─────────────
|
||||
// The demo seeds rich sleep / steps / glucose / VO2max / vorsorge data,
|
||||
// so persist an explicit dashboard layout that surfaces those tiles plus
|
||||
// the daily-verdict hero. The v1.20.0 defaults already turn the data
|
||||
// tiles on, but writing the layout verbatim keeps the public demo's home
|
||||
// page stable regardless of future default tweaks. Derived from
|
||||
// DEFAULT_DASHBOARD_LAYOUT with the seeded-rich strip tiles forced on.
|
||||
const SHOWCASE_TILE_IDS = new Set([
|
||||
"sleep",
|
||||
"steps",
|
||||
"glucose",
|
||||
"vo2Max",
|
||||
"recentWorkouts",
|
||||
]);
|
||||
const demoDashboardLayout = serializeDashboardLayout({
|
||||
...DEFAULT_DASHBOARD_LAYOUT,
|
||||
heroVisible: true,
|
||||
widgets: DEFAULT_DASHBOARD_LAYOUT.widgets.map((w) =>
|
||||
SHOWCASE_TILE_IDS.has(w.id) ? { ...w, tileVisible: true } : w,
|
||||
),
|
||||
});
|
||||
await client.query(
|
||||
`UPDATE users SET dashboard_widgets_json = $1 WHERE id = $2`,
|
||||
[JSON.stringify(demoDashboardLayout), userId],
|
||||
);
|
||||
|
||||
// ── Measurements (90 days) ────────────────
|
||||
// The loop runs i = 0..days (inclusive) so the final iteration lands a
|
||||
// point for TODAY (daysAgo(0)) — every headline metric has a current
|
||||
|
||||
@@ -105,10 +105,11 @@ describe("resolveDashboardFirstPaintGate", () => {
|
||||
describe("resolveConfiguredTileCount (v1.16.8)", () => {
|
||||
it("counts only widgets that paint a strip tile, with bp counting double", () => {
|
||||
// Default layout: weight, bp, pulse, bodyFat, mood, bpInTarget, vo2Max
|
||||
// are tile-visible AND tile-capable; medications + recentWorkouts are
|
||||
// tile-visible in the stored layout but paint NO strip tile, so
|
||||
// they must not inflate the silhouette. bp paints sys + dia = 2.
|
||||
expect(resolveConfiguredTileCount(DEFAULT_DASHBOARD_LAYOUT)).toBe(8);
|
||||
// plus the v1.20.0 sleep / steps / glucose flips are tile-visible AND
|
||||
// tile-capable; medications + recentWorkouts are tile-visible in the
|
||||
// stored layout but paint NO strip tile, so they must not inflate the
|
||||
// silhouette. bp paints sys + dia = 2.
|
||||
expect(resolveConfiguredTileCount(DEFAULT_DASHBOARD_LAYOUT)).toBe(11);
|
||||
});
|
||||
|
||||
it("ignores chart-only and iOS-pin-only widget ids", () => {
|
||||
@@ -139,9 +140,10 @@ describe("resolveConfiguredTileCount (v1.16.8)", () => {
|
||||
describe("resolveChartRowPlaceholderCount (v1.16.8)", () => {
|
||||
it("reserves one cell per chart-visible chart-capable widget", () => {
|
||||
// Default layout chart row: weight, bp, pulse, bodyFat, mood,
|
||||
// medications are `visible`; bpInTarget / achievements /
|
||||
// recentWorkouts carry no chart-shaped footprint.
|
||||
expect(resolveChartRowPlaceholderCount(DEFAULT_DASHBOARD_LAYOUT)).toBe(6);
|
||||
// medications are `visible`, plus the v1.20.0 vorsorge flip
|
||||
// (chart-row only); bpInTarget / achievements / recentWorkouts carry
|
||||
// no chart-shaped footprint.
|
||||
expect(resolveChartRowPlaceholderCount(DEFAULT_DASHBOARD_LAYOUT)).toBe(7);
|
||||
});
|
||||
|
||||
it("adds the BMI cell riding the weight gate when a height is known", () => {
|
||||
@@ -149,7 +151,7 @@ describe("resolveChartRowPlaceholderCount (v1.16.8)", () => {
|
||||
resolveChartRowPlaceholderCount(DEFAULT_DASHBOARD_LAYOUT, {
|
||||
hasHeightCm: true,
|
||||
}),
|
||||
).toBe(7);
|
||||
).toBe(8);
|
||||
});
|
||||
|
||||
it("respects a trimmed layout", () => {
|
||||
|
||||
@@ -469,16 +469,29 @@ describe("DASHBOARD_WIDGET_CATALOGUE_IDS — 27-id catalogue", () => {
|
||||
expect(new Set(DASHBOARD_WIDGET_CATALOGUE_IDS).size).toBe(36);
|
||||
});
|
||||
|
||||
it("registers the Vorsorge widget default-off on both surfaces", () => {
|
||||
// v1.18.2 — Vorsorge becomes a first-class dashboard widget like
|
||||
// medications: chart-row only, default-invisible so existing
|
||||
// dashboards are unchanged until the user opts in.
|
||||
it("ships the sleep / steps / glucose strip tiles default-on (v1.20.0)", () => {
|
||||
// These modules are on by default and their tiles self-gate on having
|
||||
// data in page.tsx, so accounts that sync sleep / step / glucose data
|
||||
// discover the tiles on / without hunting through Settings. The chart
|
||||
// rows stay off because those charts live on the /insights sub-pages.
|
||||
for (const id of ["sleep", "steps", "glucose"]) {
|
||||
const widget = DEFAULT_DASHBOARD_LAYOUT.widgets.find((w) => w.id === id);
|
||||
expect(widget).toBeDefined();
|
||||
expect(widget?.tileVisible).toBe(true);
|
||||
expect(widget?.visible).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("registers the Vorsorge widget chart-row default-on (v1.20.0)", () => {
|
||||
// v1.18.2 — Vorsorge is a first-class chart-row widget (no strip tile).
|
||||
// v1.20.0 flipped it default-visible so preventive-care reminders
|
||||
// surface on /; it self-gates and renders nothing without due reminders.
|
||||
expect(DASHBOARD_WIDGET_IDS).toContain("vorsorge");
|
||||
const entry = DEFAULT_DASHBOARD_LAYOUT.widgets.find(
|
||||
(w) => w.id === "vorsorge",
|
||||
);
|
||||
expect(entry).toBeDefined();
|
||||
expect(entry?.visible).toBe(false);
|
||||
expect(entry?.visible).toBe(true);
|
||||
expect(entry?.tileVisible).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -367,9 +367,21 @@ export const DEFAULT_DASHBOARD_LAYOUT: DashboardLayout = {
|
||||
{ id: "mood", visible: true, tileVisible: true, order: 4 },
|
||||
{ id: "bpInTarget", visible: true, tileVisible: true, order: 5 },
|
||||
{ id: "medications", visible: true, tileVisible: true, order: 6 },
|
||||
{ id: "sleep", visible: false, tileVisible: false, order: 7 },
|
||||
{ id: "steps", visible: false, tileVisible: false, order: 8 },
|
||||
{ id: "glucose", visible: false, tileVisible: false, order: 9 },
|
||||
// v1.20.0 — sleep / steps / glucose strip tiles default-on so accounts
|
||||
// that sync these signals discover them on / without hunting through
|
||||
// Settings → Dashboard. Each follows the VO2max precedent: the strip
|
||||
// tile is on (`tileVisible: true`) and self-gates on having data
|
||||
// (`hasSleep` / `hasSteps` / `showGlucoseCards` in page.tsx), so an
|
||||
// empty account still gets a clean dashboard. The chart row stays off
|
||||
// (`visible: false`) — those charts live on the /insights sub-pages.
|
||||
{ id: "sleep", visible: false, tileVisible: true, order: 7 },
|
||||
{ id: "steps", visible: false, tileVisible: true, order: 8 },
|
||||
{ id: "glucose", visible: false, tileVisible: true, order: 9 },
|
||||
// totalBodyWater / boneMass / oxygenSaturation carry as catalogue ids
|
||||
// for the iOS layout round-trip only — they have no web render block or
|
||||
// data-floor flag in page.tsx, so flipping their defaults would surface
|
||||
// nothing on web. Their data IS discoverable via the /insights sub-pages.
|
||||
// Kept false until a web render path lands (see .planning/v1200-backlog.md).
|
||||
{ id: "totalBodyWater", visible: false, tileVisible: false, order: 10 },
|
||||
{ id: "boneMass", visible: false, tileVisible: false, order: 11 },
|
||||
{ id: "oxygenSaturation", visible: false, tileVisible: false, order: 12 },
|
||||
@@ -411,8 +423,10 @@ export const DEFAULT_DASHBOARD_LAYOUT: DashboardLayout = {
|
||||
{ id: "falls", visible: false, tileVisible: false, order: 22 },
|
||||
{ id: "walkingSteadiness", visible: false, tileVisible: false, order: 23 },
|
||||
// v1.18.2 — Vorsorge summary card. Chart-row only (no strip tile), so
|
||||
// `tileVisible` is forced false; default-invisible until opt-in.
|
||||
{ id: "vorsorge", visible: false, tileVisible: false, order: 24 },
|
||||
// `tileVisible` is forced false. v1.20.0 — flipped `visible: true` so
|
||||
// preventive-care reminders surface on / by default; the card self-gates
|
||||
// (see page.tsx) and shows nothing for accounts without due reminders.
|
||||
{ id: "vorsorge", visible: true, tileVisible: false, order: 24 },
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user