mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 05:42:19 +00:00
The body fat tile was the one strip tile that led nowhere. Its href map said there was no dedicated sub-page, which was true: body fat percentage was tiled and charted on the dashboard but never got the Insights page every body-composition sibling has. Add /insights/body-fat on the generic metric-page scaffold (body group, tab-strip pill, seven locales, manual-entry empty state) and link the tile to it. Move the tile href map out of the page into tile-destinations.ts, keyed by the tile-capable widget id and typed against the sub-page slug union, so a missing or non-existent destination fails to compile. The link passes the destination's insights-page surface gate, so it never points at a page a switched-off module hides. A guard derives the tile set from TILE_CAPABLE_WIDGET_IDS and from the ids the dashboard actually pushes into its strip, and fails when a tile's metric has a sub-page the tile does not open, when a destination has no route, or when a tile links to a page owned by another module. Tiles that deliberately lead nowhere need a written reason; there are none today. The reverse-lookup test pinned BODY_FAT as having no page; it now resolves to body-fat. Refs #1084
82 lines
2.9 KiB
TypeScript
82 lines
2.9 KiB
TypeScript
/**
|
|
* Where each dashboard strip tile leads.
|
|
*
|
|
* A tile shows one metric, and every metric with an Insights sub-page should
|
|
* open it on click. The map is keyed by the tile-capable widget id and typed
|
|
* against the sub-page slug union, so a destination that does not exist is a
|
|
* compile error and a tile added to `TILE_CAPABLE_WIDGET_IDS` without a
|
|
* decision here is one too. `null` means the tile deliberately leads nowhere;
|
|
* the guard in `__tests__/tile-destinations.test.ts` demands a written reason
|
|
* for every such entry and fails when a tile's metric has a sub-page the tile
|
|
* does not open. It exists because the body fat tile was the one tile that
|
|
* led nowhere, documented as having "no dedicated sub-page" while every
|
|
* body-composition sibling had one.
|
|
*
|
|
* A link never points at a page the account cannot see: the destination
|
|
* passes the same `insights-page:<slug>` surface gate the tab strip uses.
|
|
*/
|
|
import {
|
|
INSIGHTS_OVERVIEW_PATH,
|
|
type SubPageSlug,
|
|
} from "@/lib/insights/sub-page-metric";
|
|
import { isSurfaceVisible, type SurfaceModuleMap } from "@/lib/modules/surface";
|
|
|
|
import {
|
|
TILE_CAPABLE_WIDGET_IDS,
|
|
type TileCapableWidgetId,
|
|
} from "./dashboard-gates";
|
|
|
|
export const TILE_DESTINATION: Readonly<
|
|
Record<TileCapableWidgetId, SubPageSlug | null>
|
|
> = {
|
|
weight: "weight",
|
|
bp: "blood-pressure",
|
|
pulse: "pulse",
|
|
bodyFat: "body-fat",
|
|
mood: "mood",
|
|
sleep: "sleep",
|
|
steps: "steps",
|
|
glucose: "blood-glucose",
|
|
bpInTarget: "blood-pressure",
|
|
vo2Max: "cardio-fitness",
|
|
hrv: "hrv",
|
|
oxygenSaturation: "oxygen",
|
|
respiratoryRate: "respiratory-rate",
|
|
wristTemperature: "wrist-temperature",
|
|
muscleMass: "muscle-mass",
|
|
totalBodyWater: "body-water",
|
|
boneMass: "bone-mass",
|
|
// Fluid intake lives on the nutrients page (hydration hero + quick-add),
|
|
// not on a Measurement-backed detail page.
|
|
waterIntake: "nutrients",
|
|
};
|
|
|
|
const TILE_WIDGET_IDS: ReadonlySet<string> = new Set(TILE_CAPABLE_WIDGET_IDS);
|
|
|
|
/**
|
|
* The widget a rendered tile belongs to. Blood pressure paints two tiles
|
|
* (`bp-sys`, `bp-dia`) and glucose one per logged context (`glucose-<ctx>`);
|
|
* every other tile carries its widget id verbatim.
|
|
*/
|
|
export function tileWidgetId(tileId: string): TileCapableWidgetId | null {
|
|
if (tileId === "bp-sys" || tileId === "bp-dia") return "bp";
|
|
if (tileId.startsWith("glucose-")) return "glucose";
|
|
return TILE_WIDGET_IDS.has(tileId) ? (tileId as TileCapableWidgetId) : null;
|
|
}
|
|
|
|
/**
|
|
* The href a rendered tile links to, or null when it has no destination or
|
|
* the destination page is hidden by a switched-off module.
|
|
*/
|
|
export function dashboardTileHref(
|
|
tileId: string,
|
|
modules: SurfaceModuleMap | null | undefined,
|
|
): string | null {
|
|
const widget = tileWidgetId(tileId);
|
|
if (widget === null) return null;
|
|
const slug = TILE_DESTINATION[widget];
|
|
if (slug === null) return null;
|
|
if (!isSurfaceVisible(`insights-page:${slug}`, modules)) return null;
|
|
return `${INSIGHTS_OVERVIEW_PATH}/${slug}`;
|
|
}
|