feat(insights): drive overview section + tile render from saved layout

Read the resolved insights layout on /insights and render the customizable
region from it. Sections render in saved order, hidden ones drop out, and
the Vitals grid honours the per-tile order + visibility. HeroStrip stays
anchored above the customizable region; every existing feature/data gate
(briefing flag, cycle-enabled, per-tile data) still applies on top, so the
default layout reproduces the prior page.

- useInsightsLayout() reads GET /api/insights/layout through the query-key
  factory, defaulting to DEFAULT_INSIGHTS_LAYOUT while in flight.
- orderedVisibleSectionIds + resolveTileLayout: pure, unit-tested helpers.
- Widen DEFAULT_VISIBLE_TILE_IDS with the Vitals/Mobility grid tiles so a
  fresh account keeps the tiles it shows today.
This commit is contained in:
Marc-André Bombeck committed 2026-06-07 16:45:26 +02:00
1 parent f6221623a4
commit e3539d7c94
5 files changed
+472 -98

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { queryKeys } from "@/lib/query-keys";
import {
DEFAULT_INSIGHTS_LAYOUT,
resolveInsightsLayout,
type InsightsLayout,
} from "@/lib/insights-layout";
/**
* v1.15.11 W2 — the resolved insights overview layout (sections + tiles).
*
* Reads `GET /api/insights/layout` through the centralised query-key factory
* (`queryKeys.insightsLayout()` → `["user","insightsLayout"]`), the same key
* the W3 PUT mutation invalidates on save so the overview repaints in
* lockstep with the Settings / inline-edit surfaces. The response is already
* the server-resolved v2 layout; we pass it back through
* `resolveInsightsLayout` defensively so a partial / legacy blob from a stale
* cache still normalises to a valid `InsightsLayout`.
*
* Returns `DEFAULT_INSIGHTS_LAYOUT` while the query is in-flight so the page
* renders in the default section + tile order on first paint — no layout
* flicker, no empty state, no "everything hidden" flash before the saved
* layout lands.
*/
export function useInsightsLayout(enabled: boolean): InsightsLayout {
const { data } = useQuery({
queryKey: queryKeys.insightsLayout(),
queryFn: async () => {
const res = await fetch("/api/insights/layout");
if (!res.ok) throw new Error("Failed to load insights layout");
return resolveInsightsLayout((await res.json()).data);
},
enabled,
});
return data ?? DEFAULT_INSIGHTS_LAYOUT;
}