diff --git a/.planning/phase-W4-v1425-insights-sub-pages-report.md b/.planning/phase-W4-v1425-insights-sub-pages-report.md new file mode 100644 index 000000000..6ad2a1d7b --- /dev/null +++ b/.planning/phase-W4-v1425-insights-sub-pages-report.md @@ -0,0 +1,152 @@ +# Phase W4 — Insights sub-pages restructure + Sleep + batch-delete (v1.4.25) + +Scope: W4a (routed sub-pages) + W4b (DELETE endpoint) + W4c (Sleep +sub-page) + W4d (mother-page restructure) + W4e (i18n strings). +Branch: `develop`. No push, no tag, atomic commits. + +## Commits + +1. `38bbba1` — `feat(insights): scaffold 6 metric sub-pages with shared layout shell` +2. `959adec` — `feat(api): DELETE /api/measurements/by-external-ids for iOS deletion-sync` +3. `cb825ec` — `feat(insights): new /insights/schlaf sub-page with sleep-stage chart` +4. `7644bd0` — `refactor(insights): mother page hosts overview only; metric depth moves to sub-pages` +5. `d63fa27` — `i18n(insights): EN + DE strings for the 7 insights sub-pages` +6. `85e93dc` — `chore(openapi): regenerate docs/api/openapi.yaml` (reconciles a W7 agent's drift) + +## Files added + +- `src/app/insights/layout.tsx` — shared layout (server component shim). +- `src/app/insights/blutdruck/page.tsx` +- `src/app/insights/gewicht/page.tsx` +- `src/app/insights/puls/page.tsx` +- `src/app/insights/stimmung/page.tsx` +- `src/app/insights/medikamente/page.tsx` +- `src/app/insights/bmi/page.tsx` +- `src/app/insights/schlaf/page.tsx` +- `src/app/api/measurements/by-external-ids/route.ts` — DELETE handler. +- `src/components/insights/insights-layout-shell.tsx` — owns the + advisor query at the layout level so the regenerate button works + on every sub-page. +- `src/components/insights/sub-page-shell.tsx` — title + badge + + focusable h1 (a11y focus restoration on tab navigation). +- `src/components/insights/sleep-duration-chart.tsx` — wrapper around + ``. +- `src/components/insights/sleep-stage-stacked-bar.tsx` — Recharts + horizontal stacked bar of stage composition. +- `src/components/insights/sleep-overview.tsx` — sleep page composition. +- `src/hooks/use-insights-layout-prefs.ts` — `comparisonBaseline` + reader (single source for every Insights surface). +- `src/lib/insights/sub-page-metric.ts` — slug → MeasurementType[] map. +- `tests/integration/measurements-batch-delete.test.ts` — 4 cases. +- `src/components/insights/__tests__/sleep-stage-stacked-bar.test.tsx` — 4 cases. +- `src/components/insights/__tests__/sub-page-shell.test.tsx` — 3 cases. + +## Files modified + +- `src/app/insights/page.tsx` — slim mother page (1498 lines removed). +- `src/components/insights/insights-tab-strip.tsx` — route-aware + (`` + `usePathname()` replacing scroll-into-view). +- `src/lib/openapi/routes.ts` — DELETE endpoint registered. +- `docs/api/openapi.yaml` — regenerated. +- `messages/en.json`, `messages/de.json` — sub-page descriptions, + empty-state CTAs, sleep stage labels. +- `src/app/__tests__/insights-polish.test.ts` — two guard tests + re-pointed at the new homes of their assertions (layout shell + + shared layout-prefs hook). +- `src/components/insights/__tests__/trends-row.test.tsx` — every + render now wraps in a TanStack-Query provider because the W7 + timezone agent added a `useAuth()` call inside ``. + +## Architectural notes + +- **CoachDrawer placement** stays on the mother page body only. + Navigating to a sub-page unmounts the drawer (Marc's directive). The + layout shell deliberately does NOT mount the drawer. +- **Per-card chart cog**: every sub-page passes a `chartKey` from + `CHART_OVERLAY_KEYS` so the existing dashboard chart-cog surface + applies on Insights without a single backend change. +- **`comparisonBaseline` plumbing**: `useInsightsLayoutPrefs()` is now + the single shared reader; the mother page no longer touches it + because its charts moved out. +- **OpenAPI**: the new `MeasurementsDeleteByExternalIdsRequest` / + `MeasurementsDeleteByExternalIdsResult` schemas live alongside the + ingest pair; the path entry sits under the Measurements tag. + +## Test delta + +- Unit + integration tests outside `tests/integration/` go from 2365 + → 2375 (+10), all passing. +- New integration suite `measurements-batch-delete.test.ts` adds 4 + cases (requires Postgres; not run locally per project convention). + +## Verification + +``` +$ pnpm typecheck # exit 0 +$ pnpm lint # clean (no warnings on my files) +$ pnpm openapi:check # spec in sync +$ pnpm vitest run --exclude='tests/integration/**' + Test Files 277 passed (277) + Tests 2375 passed | 1 skipped (2376) +``` + +`pnpm format:check` still surfaces 21 pre-existing warnings in +parallel-agent files (planning markdown, mood-related modules, +admin login-overview tests). My files all pass; the unrelated drift +is out of W4 scope. + +## Manual verification (not run locally) + +Manual smoke notes to verify after deploy: +- `/insights` mother page renders (Hero + Briefing + Trends + Advisor + + Coach drawer). +- Each of `/insights/{blutdruck,gewicht,puls,stimmung,medikamente,bmi,schlaf}` + renders without crashes and the tab strip's active pill matches. +- Chart-cog on a sub-page chart persists overlay prefs across + refreshes (`PUT /api/dashboard/chart-overlay-prefs` round-trip). +- AI-disabled user (demo) doesn't see broken Coach buttons — the + layout shell forwards `regenerate` only when authenticated, and the + per-section status cards already gate on `hasProvider`. +- Sleep sub-page empty-state (no SLEEP_DURATION rows) renders the + "Apple Health sync in v1.5" CTA. + +## Deferred / not in scope + +- Per-night stacked column chart (true time-series view of stages). + The W4c chart shows the 30-day composition aggregate the existing + `/api/analytics` endpoint already exposes; a per-night view would + need a new server endpoint with per-day×stage data. Park for v1.4.26 + in `.planning/v1425-backlog.md` if Marc wants it. +- `/api/insights/general-status` endpoint is now orphaned (no + consumer survives the mother-page slim-down). Server route + + associated translations weren't removed in this phase to keep the + surface area tight; W4 ships orchestration only. +- The `insights.navGeneral` translation key is still referenced from + the legacy `SECTION_LABEL_KEYS` map but the new route-aware strip + doesn't read it. Safe to delete in a follow-up cleanup. +- Greying out the comparison overlay picker when prior-period data is + unavailable (research §4 polish; not required to ship). + +## Constraints honoured + +- No version bump or CHANGELOG entry (W11 owns release notes). +- No edits to `src/lib/ai/coach/snapshot.ts` (W7b in flight). +- No edits to `src/components/insights/coach-panel/coach-drawer.tsx` + internals. +- No edits to chart-tokens / recommendation-card / insight-advisor + internals (W5b in flight). +- No edits to admin login overview (W8b in flight). + +## Risk notes for reviewer + +- The layout shell mounts `useInsightsAdvisorQuery(isAuthenticated)` + on every Insights surface. The query's `staleTime: 60 * 60 * 1000` + (60 min) prevents network thrash; re-mounting on tab navigation is + a cache read. +- The mother page's `` wiring is GONE — the + hero band's regenerate button no longer paints. The strip-mounted + regenerate icon is the sole page-level affordance. +- `DELETE /api/measurements/by-external-ids` bypasses the + `Idempotency-Key` cache. A retried DELETE returns the same + `deletedCount: 0` because the rows are already gone — that's the + correct contract; the client should treat both calls as success.