mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
docs(planning): mark Wave-B B1a (charts visual leap) complete
Apple-Health-style polish lands across all dashboard chart wrappers in v1.4.16: gradient fills, 90-day personal baseline, rich tooltips with delta-vs-baseline, 600 ms first-render animation respecting prefers-reduced-motion, sparse-data empty state. Mood chart picks up emoji glyphs at every data point; medication chart picks up the gradient + rich tooltip on top of A6's existing target/threshold reference lines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
613d6619df
commit
d87ac3a639
2 files changed
+176
-2
No files matched your search
+38
-2
@@ -79,14 +79,50 @@ Last update: 2026-05-09T23:12:52+02:00
|
||||
|
||||
## Wave B — Quality-leap features
|
||||
|
||||
### B1 — Insights/Charts Apple-Health-style visual leap
|
||||
### B1 — Insights/Charts Apple-Health-style visual leap (chart wrappers, first half)
|
||||
|
||||
- [ ] Benchmark against Apple Health, Withings Health Mate, Oura Ring
|
||||
- [ ] Insights surface: gradient fills, animation, dynamic comparisons (better/worse), interactive tooltips, vertical slide-to-compare
|
||||
- [ ] Charts: same level of polish — Recharts replacement allowed if quality bar requires
|
||||
- [x] Charts: same level of polish — Recharts replacement allowed if quality bar requires
|
||||
- [ ] Insights surface visualizes data, not just text-summarizes
|
||||
- Detailed report: `.planning/phase-B1-report.md`
|
||||
|
||||
#### B1a — Charts visual leap (chart wrappers only) — complete
|
||||
|
||||
- 2026-05-10T00:35+02:00 — B1a complete on origin/main.
|
||||
- **Primitives** (`src/components/charts/{chart-gradient,chart-tooltip,chart-empty-state}.tsx`)
|
||||
plus `src/lib/charts/reduced-motion.ts`. 11 unit tests.
|
||||
- **HealthChart** (BP/weight/pulse/body-fat/sleep/steps wrapper):
|
||||
`LineChart` → `ComposedChart`, gradient-filled `<Area>` per type,
|
||||
90-day median personal-baseline `<ReferenceLine>` ("Your normal" /
|
||||
"Dein Mittel"), `<RichChartTooltip>` with delta-vs-baseline,
|
||||
600 ms ease-out animation respecting `prefers-reduced-motion`,
|
||||
sparse-data `<ChartEmptyState>` for <3 points.
|
||||
- **MoodChart**: same polish + emoji glyphs at every score (😖 🙁
|
||||
😐 🙂 😄 mapped from 1..5) via Recharts `dot` callback; lavender
|
||||
gradient under the score line.
|
||||
- **MedicationComplianceChart**: same polish, purple gradient,
|
||||
rich tooltip with "−N pp vs. target" delta. A6's existing 80 %
|
||||
threshold + 100 % goal lines untouched.
|
||||
- +23 net tests; full suite 1298/1299 (single pre-existing B6 fail).
|
||||
- Commits on origin/main: primitives → `2611bb4` (subject belongs
|
||||
to B5b due to a parallel-staging race; my 6 files + 7 i18n keys
|
||||
correct), `74c2eb8 feat(charts): BP/weight/pulse polish (gradient,
|
||||
baseline, rich tooltip)`, `901f44e feat(charts): mood chart polish
|
||||
with emoji glyphs at data points` (also absorbed B5b's
|
||||
`provider-runner.ts`), `8008613 feat(charts): medication chart
|
||||
polish (gradient + animation + rich tooltip)` (clean).
|
||||
- Architectural deviation: brief asked for separate
|
||||
`blood-pressure-chart.tsx` / `weight-chart.tsx` / `pulse-chart.tsx`
|
||||
files; the codebase has one `HealthChart` wrapper used for all
|
||||
three families. Splitting would have been DRY-regressing — kept
|
||||
one wrapper, one commit covers all three.
|
||||
- "Optional targetWeightKg user-pref" on weight chart not added —
|
||||
that's a Settings → Account field B6 owns; chart wrapper already
|
||||
accepts a `valueBands` prop the dashboard can pass a target band
|
||||
through whenever the user-pref ships.
|
||||
- Detailed report: `.planning/phase-B1a-report.md`
|
||||
|
||||
### B2 — AI provider settings UX cleanup (Pulldown-driven)
|
||||
|
||||
- [ ] Settings → AI: provider dropdown drives form below (no top/bottom split)
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
# Phase B1a — Charts Apple-Health-style visual leap (chart wrappers)
|
||||
|
||||
Status: complete
|
||||
Date: 2026-05-10T00:35:00+02:00
|
||||
Branch: origin/main
|
||||
|
||||
## Scope
|
||||
|
||||
The brief asked for a visual quality-leap on every chart wrapper: gradient
|
||||
fills, smooth interpolation, first-render animation, Apple-Health-style
|
||||
tooltips, in-band shading, personal baseline lines, and a sparse-data
|
||||
empty state. Recharts stays as the rendering library; Dracula tokens stay
|
||||
canonical; the wrappers stay `next/dynamic`-importable via the proven
|
||||
single-component pattern from `scatter-correlation-chart.tsx`.
|
||||
|
||||
## What landed
|
||||
|
||||
Five atomic commits on origin/main (one cross-agent race noted):
|
||||
|
||||
1. **`feat(charts): reusable gradient + rich-tooltip primitives`** — landed
|
||||
inside commit `2611bb4 feat(db): User.aiProviderChain for ordered
|
||||
fallback config` due to a parallel-staging race with the B5b worker
|
||||
(B5b ran `git add -A` between my `git add` and `git commit`). My
|
||||
primitives are on origin/main verbatim; the commit subject just
|
||||
belongs to B5b's adjacent work.
|
||||
2. **`74c2eb8 feat(charts): BP/weight/pulse polish (gradient, baseline,
|
||||
rich tooltip)`** — single HealthChart wrapper covers all three vital
|
||||
families (BP, weight, pulse, body fat, sleep, steps).
|
||||
3. **`901f44e feat(charts): mood chart polish with emoji glyphs at data
|
||||
points`** — bundled with B5b's `provider-runner.ts` files (same race
|
||||
pattern; my charts files content correct).
|
||||
4. **`8008613 feat(charts): medication chart polish (gradient +
|
||||
animation + rich tooltip)`** — clean, only my 2 files.
|
||||
|
||||
Empty-state commit (brief item 7) was integrated into commits 2-4 since
|
||||
the same `<ChartEmptyState>` primitive lands per-chart with the polish.
|
||||
A separate empty-state commit would have been a no-op.
|
||||
|
||||
## Architectural deviation from the brief
|
||||
|
||||
The brief lists separate per-chart-family files (`blood-pressure-chart.tsx`,
|
||||
`weight-chart.tsx`, `pulse-chart.tsx`). The actual codebase has **one
|
||||
generic `HealthChart` wrapper** that renders any combination of measurement
|
||||
types — `app/page.tsx` and `app/insights/page.tsx` instantiate it with
|
||||
different `types={[...]}`, `colors={...}`, and `valueBands={...}` props.
|
||||
Splitting that wrapper into three near-identical files would be a
|
||||
regression in DRY-ness, so I upgraded `HealthChart` once and bundled
|
||||
BP/weight/pulse into a single commit that covers all three families.
|
||||
Mood and medication kept their existing dedicated wrappers (they have
|
||||
chart-specific affordances: emoji glyphs for mood, target-range pair
|
||||
of reference lines for medication).
|
||||
|
||||
The brief's "optional targetWeightKg user-pref" line was not added.
|
||||
That requires a Settings → Account-section field which is owned by B6;
|
||||
the chart wrapper already accepts a `valueBands` prop the dashboard
|
||||
uses for BMI-healthy shading, so the surface is wired for B6 to pass
|
||||
a target band through whenever the user-pref ships.
|
||||
|
||||
## Acceptance criteria coverage
|
||||
|
||||
- ✅ Gradient fills under area charts (BP, weight, pulse, mood, medication)
|
||||
via `<ChartLinearGradient>` + `<Area fill="url(#id)">` over Recharts'
|
||||
`ComposedChart`. Soft 0–35 % opacity vertical fade matches Apple Health.
|
||||
- ✅ Smooth interpolation — every line uses `type="monotone"` (already the
|
||||
case for HealthChart; mood and medication switched their primary line
|
||||
to monotone too).
|
||||
- ✅ Animation on first render — 600 ms ease-out via Recharts'
|
||||
`isAnimationActive` + `animationDuration` + `animationEasing`. Suppressed
|
||||
when `prefers-reduced-motion: reduce` is set at the OS level (new
|
||||
`src/lib/charts/reduced-motion.ts` helper, 4 unit tests).
|
||||
- ✅ Rich tooltip primitive `<RichChartTooltip>` — rounded card with drop
|
||||
shadow, per-row coloured dot, tabular-nums value cell, optional delta-
|
||||
vs-baseline sub-line ("+3 mmHg vs. your normal" / "−15 pp vs. target").
|
||||
Wired via Recharts `<Tooltip content={...}>` so the chart wrappers
|
||||
pre-shape rows and pass them in.
|
||||
- ✅ In-target zone shading — already present on HealthChart via the
|
||||
`valueBands` prop (used by BP for systolic 100–135 / diastolic 65–84
|
||||
bands; weight uses healthy-BMI bands). Untouched.
|
||||
- ✅ Personal baseline line — new `computePersonalBaseline()` helper
|
||||
(90-point rolling median, returns null for <5 points). Painted as a
|
||||
faint dashed `<ReferenceLine>` labelled `"Your normal"` / `"Dein
|
||||
Mittel"` (i18n keys `charts.personalBaseline`).
|
||||
- ✅ Empty / sparse-data state — new `<ChartEmptyState>` primitive
|
||||
consistently used by every wrapper when daily points < 3. Lucide
|
||||
`LineChart` glyph + i18n title+description (EN+DE).
|
||||
|
||||
## Tests added
|
||||
|
||||
- 4 gradient primitive tests (`chart-gradient.test.tsx`)
|
||||
- 4 tooltip primitive tests (`chart-tooltip.test.tsx`)
|
||||
- 3 empty-state primitive tests (`chart-empty-state.test.tsx`)
|
||||
- 4 reduced-motion helper tests (`reduced-motion.test.ts`)
|
||||
- 4 `computePersonalBaseline()` tests + 2 HealthChart SSR-markup tests
|
||||
- 1 mood-chart-polish SSR test
|
||||
- 1 medication-chart-polish SSR test
|
||||
|
||||
**Total: +23 net new tests.** Full suite stayed green throughout
|
||||
(1295 → 1298 → 1299 across the three iterations; the single failing
|
||||
`<AiSection>` test is in B6 territory, predates this phase).
|
||||
|
||||
## Cross-agent worktree race notes
|
||||
|
||||
Three of the four commits absorbed parallel-agent staged work:
|
||||
|
||||
1. Primitive files commit landed under B5b's `feat(db):
|
||||
User.aiProviderChain` subject. My 6 files (`chart-gradient.tsx`,
|
||||
`chart-tooltip.tsx`, `chart-empty-state.tsx`, 3 tests, plus 7 i18n
|
||||
keys EN+DE) are on origin/main inside `2611bb4` — content-verified.
|
||||
2. Mood polish commit (`901f44e`) absorbed B5b's `provider-runner.ts`
|
||||
+ 2 test files + `insights/generate/route.ts` modifications + AI
|
||||
provider chain insights routing. My mood-chart edits are intact.
|
||||
3. The mood-chart edits had to be reapplied twice after a watcher /
|
||||
format-on-save process (likely a parallel agent) reverted my
|
||||
in-flight `Edit` calls back to baseline mid-edit. Final form
|
||||
shipped via a single `Write` call (atomic, no half-state for the
|
||||
reverter to catch).
|
||||
|
||||
Documented in STATE.md status block and matches the recurring meta
|
||||
note from v1.4.15 marathon ("per-agent git-worktree adoption deferred
|
||||
to v1.4.16").
|
||||
|
||||
## Verification
|
||||
|
||||
- `pnpm vitest run src/components/charts` — 68 / 68 green
|
||||
- `pnpm test` — 1298 / 1299 green (1 unrelated B6 fail)
|
||||
- `pnpm lint` — 0 errors / 12 pre-existing warnings
|
||||
- `pnpm typecheck` — 4 pre-existing errors in B5b/B6 files; my files clean
|
||||
|
||||
Local tracked changes: 0 outside my scope. Untracked: 4 export route
|
||||
folders + thresholds-settings-section.tsx (B7 / B6 territory).
|
||||
|
||||
## Hand-off
|
||||
|
||||
Phase B1b (Insights surface upgrade) inherits the primitives at
|
||||
`src/components/charts/chart-{gradient,tooltip,empty-state}.tsx` and the
|
||||
`src/lib/charts/reduced-motion.ts` helper. The `<RichChartTooltip>`
|
||||
component is generic and can be reused for any Recharts-rendered surface
|
||||
in the insights page.
|
||||
Reference in new issue
Block a user