mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
docs(planning): A2 report + STATE update for charts mobile audit
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
fa91a73aef
commit
e0c007a8af
2 files changed
+158
-99
No files matched your search
+72
-8
@@ -38,15 +38,48 @@ Last update: 2026-05-10T12:48+02:00
|
||||
- [x] Commit `a856272` pushed to origin/main first attempt.
|
||||
- Detailed report: `.planning/phase-A1-report.md`
|
||||
|
||||
### A2 — Charts mobile audit (axis-label overflow + X-axis density consistency)
|
||||
### A2 — Charts mobile audit (axis-label overflow + X-axis density consistency) ✅
|
||||
|
||||
- [ ] Playwright headless against live prod at Pixel 5 + smaller
|
||||
viewports
|
||||
- [ ] "Wochendurchschnitt" + "7T/30T/90T/Alle" tabs wrap-break:
|
||||
shorten the label or hide on mobile
|
||||
- [ ] Medication chart shows every date on X; weight/BMI charts
|
||||
sparser. Make consistent across all charts
|
||||
- [ ] Apply universal x-axis tick-density helper
|
||||
- [x] Playwright headless against live PROD at Pixel 5, iPhone 12,
|
||||
iPhone SE, Galaxy Fold compact (280 px) — probe at
|
||||
`.planning/v1419-a2-prod-probe.mjs`, screenshots + JSON in
|
||||
`/tmp/v1419-a2-prod/`. Findings doc:
|
||||
`.planning/phase-A2-mobile-findings.md`.
|
||||
- [x] Pre-fix readings: Blood Pressure / Pulse / Mood / Medications
|
||||
cards all wrapped tabs to a 2nd row on iPhone 12 (header 92 →
|
||||
188 px); fold-compact pushed Medications to 3 rows + horizontal
|
||||
overflow.
|
||||
- [x] Header layout fix — mobile-first stack (`flex-col` below sm,
|
||||
`flex-row` ≥ sm). Title + chips on row 1, range tabs + cog
|
||||
right-aligned on row 2 with `flex-nowrap` + `px-2 sm:px-3` so
|
||||
tabs always own a single row at 280 px. Bucket-aggregation chip
|
||||
and comparison captions hide on mobile (`hidden sm:inline-flex`).
|
||||
Applied to HealthChart, MoodChart, MedicationComplianceChart.
|
||||
- [x] Universal x-axis tick-density helper at
|
||||
`src/lib/charts/x-axis-density.ts` + reactive viewport hook at
|
||||
`src/hooks/use-viewport-width.ts`. Caps visible ticks at 4
|
||||
(Fold) / 6 (Pixel-5 / iPhone-12) / 8 (small tablet) / 10
|
||||
(desktop). Wired into HealthChart, MoodChart,
|
||||
MedicationComplianceChart, ComplianceLineChart. Scatter
|
||||
correlation chart deliberately left alone (numeric x-axis with
|
||||
explicit `ticks` array).
|
||||
- [x] e2e regression at `e2e/charts-mobile.spec.ts` — runs only on
|
||||
`chromium-mobile`; asserts 1 row of tabs per card + ≤ 7 visible
|
||||
ticks per axis. Helper unit tests at
|
||||
`src/lib/charts/__tests__/x-axis-density.test.ts` (13 cases).
|
||||
- [x] `pnpm test`: 1637 / 1637 green. `pnpm lint`: 0 errors / 17
|
||||
warnings (12 baseline + 5 from concurrent A3 work, none from A2).
|
||||
`pnpm typecheck`: A2 surface clean (one A3-owned error in
|
||||
`src/app/insights/page.tsx` unrelated to A2).
|
||||
- [x] Two commits on origin/main: `77a3ad3` (fix), `a739085` (test).
|
||||
Note: parallel-agent race condition during the second commit
|
||||
caused a polluted intermediate commit `0cf23f3` to slip through
|
||||
with the wrong file content but the right message; the same
|
||||
content (mine) was re-committed cleanly as `a739085` immediately
|
||||
after. `0cf23f3` was already pushed by another agent before the
|
||||
cleanup rebase could land, so it remains in history with a
|
||||
stale `phase-A5-report.md` payload — non-destructive but ugly.
|
||||
No `--no-verify` / `--no-gpg-sign` used.
|
||||
- Detailed report: `.planning/phase-A2-report.md`
|
||||
|
||||
### A3 — /insights polish + Comparison switch move
|
||||
@@ -222,6 +255,37 @@ medications.csv,mood.csv}/`) left in place — same call as v1.4.16 /
|
||||
`.planning/STATE.md` and `phase-A4-report.md` edits — same shared-
|
||||
cwd race across earlier marathons, no information lost.
|
||||
|
||||
- 2026-05-10T13:11+02:00 — Wave A / A6 complete. Settings mobile
|
||||
consistency audited at Pixel-5 (393×851) against production with
|
||||
Marc's session cookie; findings + raw geometry tables in
|
||||
`.planning/phase-A6-settings-audit.md`. Five commits landed:
|
||||
`957f8e9` equalises every form input height to 36 px (AI active-
|
||||
provider select `h-10` -> `h-9`, AI add-provider select `h-8` ->
|
||||
`h-9`, Dashboard "Compare to" trigger drops `min-h-11` so default
|
||||
`h-9` applies); `9fda634` standardises action-button placement —
|
||||
Account → Password, Account → Restart onboarding tour, and
|
||||
Dashboard → Reset to defaults all stack the action below the title
|
||||
on `<sm` (full-width) and right-align on `>=sm`, fixing the
|
||||
Pixel-5 right-edge overflow Marc reported on the tour button (~48
|
||||
px past the card border); `1075784` lifts the Sprache select out
|
||||
of the dob/language pair into its own row at the bottom of the
|
||||
Profile card, plus the duplicated native-select class string is
|
||||
pulled into a shared `NATIVE_SELECT_CLASS` constant; `737b533`
|
||||
uniformises card-internal `space-y` to `space-y-4` (Dashboard
|
||||
layout `space-y-5` -> `space-y-4`, AI provider config forms
|
||||
`space-y-3` -> `space-y-4`); `78f1f3f` adds
|
||||
`e2e/settings-mobile-consistency.spec.ts` — Pixel-5-only Playwright
|
||||
spec that locks in the five invariants (input heights, button-no-
|
||||
overflow, language-not-paired-with-dob, Compare-to-36 px, AI
|
||||
selects-36 px). All 1637 vitest tests + zero source typecheck
|
||||
errors + zero lint errors after the marathon. Cross-agent race:
|
||||
commit `737b533` bundled A5's `.planning/STATE.md` update; STATE
|
||||
content correct, no semantic harm. Detailed report:
|
||||
`.planning/phase-A6-report.md`. Files touched (only mine):
|
||||
`src/components/settings/{account,ai,dashboard-layout}-section.tsx`,
|
||||
`e2e/settings-mobile-consistency.spec.ts`,
|
||||
`.planning/phase-A6-{settings-audit,report}.md`.
|
||||
|
||||
---
|
||||
|
||||
## Previous milestone — v1.4.18 (completed 2026-05-10T11:45+02:00)
|
||||
|
||||
@@ -1,112 +1,107 @@
|
||||
# Phase A2 — `/admin/api-tokens` painted scrollbar (3rd attempt)
|
||||
# Phase A2 — Charts mobile audit (header overflow + x-axis density)
|
||||
|
||||
Marathon: v1.4.18 Wave-A
|
||||
Agent: A2 (parallel with A1, A3, B1)
|
||||
Started: 2026-05-10T10:08+02:00
|
||||
Finished: 2026-05-10T10:21+02:00
|
||||
Commit: `3e16074` on origin/main
|
||||
Marathon: v1.4.19 Wave-A
|
||||
Agent: A2 (parallel with A1, A3, A4, A5, A6, A7, A8)
|
||||
Commits: `77a3ad3` (fix), `a739085` (test) on origin/main
|
||||
|
||||
## Root cause — confirmed against PROD
|
||||
## Scope
|
||||
|
||||
The two earlier fixes (v1.4.15 column-hide, v1.4.16 mobile card-list)
|
||||
both targeted `<ApiTokenOverviewSection>`. They did remove overflow
|
||||
inside that card, and the production probe confirmed it: at Pixel-5
|
||||
(393 CSS px) the api-tokens card now reports `scrollWidth=327,
|
||||
clientWidth=327, diff=0` and the document itself reports
|
||||
`scrollWidth=innerWidth=393` — zero page-level horizontal overflow.
|
||||
Marc reported on his Pixel 5 that the chart cards on `/dashboard` and
|
||||
`/insights` looked broken in two distinct ways:
|
||||
|
||||
The actual scrollbar Marc kept reporting lives one component up, in
|
||||
`<AdminShell>`. Its mobile section strip is a horizontal flex with
|
||||
13 admin entries and an `overflow-x-auto` wrapper. At Pixel-5 the
|
||||
strip reports:
|
||||
1. The "Wochendurchschnitt" / "Weekly avg" bucket-aggregation chip
|
||||
plus the optional comparison-period chip plus the four range tabs
|
||||
(`7 Pkt / 30 Pkt / 90 Pkt / Alle`) plus the new cog dropdown all
|
||||
sat on a single `flex justify-between` row. On Pixel 5 the chips
|
||||
ate enough horizontal space that the four tabs wrapped to a 2nd
|
||||
row inside the same flex container; on Galaxy Fold compact (280
|
||||
px) the tabs split across three or four rows and the medication
|
||||
card overflowed horizontally.
|
||||
|
||||
```
|
||||
<nav aria-label="Admin sections"
|
||||
class="-mx-4 mb-4 overflow-x-auto px-4 md:hidden">
|
||||
scrollWidth: 1692
|
||||
clientWidth: 361
|
||||
diff: 1331
|
||||
```
|
||||
2. The medication compliance chart drew one tick per data point on
|
||||
the x-axis. A 30-day Pixel-5 window painted thirty overlapping
|
||||
labels into a 393 px gutter, while the weight / BMI charts looked
|
||||
sparser by accident — they auto-bucketed daily → weekly above 90
|
||||
days but at the 30-day range still drew a tick every couple of
|
||||
days. The chart row read inconsistently across metrics.
|
||||
|
||||
The strip sits right above the api-tokens card (`rectTop: 112`, three
|
||||
lines below the topbar), which is why each previous fix attempt felt
|
||||
like it should have helped — Marc was looking at the strip's
|
||||
scrollbar, but reporting the page he was on.
|
||||
## Method
|
||||
|
||||
`<SettingsShell>` is the exact same pattern with 10 entries and the
|
||||
same defect; both shells were fixed.
|
||||
Playwright headless against live PROD with Marc's session cookie at
|
||||
four mobile viewports: Pixel 5 (393 × 851), iPhone 12 (390 × 844),
|
||||
iPhone SE (375 × 667), Galaxy Fold compact (280 × 653). For each
|
||||
chart card on `/` and `/insights` the probe captured: header
|
||||
height, number of distinct y-rows the range tabs occupied,
|
||||
horizontal overflow, x-axis visible-tick count.
|
||||
|
||||
## Playwright proof
|
||||
The probe script lives at `.planning/v1419-a2-prod-probe.mjs`;
|
||||
machine-readable findings + per-viewport screenshots in
|
||||
`/tmp/v1419-a2-prod/` (kept out of git because they're 1-2 MB of
|
||||
PNGs); narrative summary at `.planning/phase-A2-mobile-findings.md`.
|
||||
|
||||
Probe script: `/tmp/v1418-api-tokens-prod-probe.mjs` (uses Marc's
|
||||
session cookie `cmox4d6fj000101p8w9ykhcnm`, viewport Pixel-5).
|
||||
## Fix shipped — `77a3ad3`
|
||||
|
||||
Pre-fix readings against `https://healthlog.bombeck.io/admin/api-tokens`:
|
||||
The header is now stacked into two rows below the sm breakpoint:
|
||||
title + chips on row 1; range tabs + cog right-aligned on row 2 with
|
||||
`flex-nowrap` so tabs always fit a single row at 280 px. Tab padding
|
||||
shrinks to `px-2` on mobile, `px-3` ≥ sm. The decorative
|
||||
bucket-aggregation chip + comparison-caption / unavailable chips all
|
||||
become `hidden sm:inline-flex` — the range tabs already communicate
|
||||
the visible window and the cog dropdown surfaces the overlay state
|
||||
explicitly.
|
||||
|
||||
```
|
||||
[393x851] dims= { docScrollWidth: 393, innerWidth: 393, ... }
|
||||
[393x851] overflow culprits (top of list by diff):
|
||||
<nav.overflow-x-auto> sw=1692 cw=361 diff=1331
|
||||
<span.truncate> sw=331 cw=269 diff=62 (text-overflow:ellipsis,
|
||||
no painted scrollbar)
|
||||
[1920x1080] dims delta = 0
|
||||
```
|
||||
Applied to `HealthChart` (BP / weight / pulse / BMI / sleep / steps),
|
||||
`MoodChart` and `MedicationComplianceChart` consistently. Above sm
|
||||
the original side-by-side layout is preserved with all chips visible.
|
||||
|
||||
Post-fix the strip still reports `scrollWidth > clientWidth` (so swipe
|
||||
and keyboard-arrow scrolling stay), but with `scrollbar-width: none`
|
||||
A new helper at `src/lib/charts/x-axis-density.ts` maps viewport
|
||||
width to a maximum tick count and computes the corresponding Recharts
|
||||
`interval` skip count: ≤ 360 px → 4 ticks, ≤ 480 px → 6 ticks, ≤ 768
|
||||
px → 8 ticks, > 768 px → 10 ticks. A reactive `useViewportWidth`
|
||||
hook in `src/hooks/` reads the layout viewport and updates on resize
|
||||
/ orientation-change with a desktop default during SSR.
|
||||
|
||||
- `::-webkit-scrollbar { display: none }` the painted bar no longer
|
||||
draws. CSS-isolation probe (`/tmp/v1418-css-probe.html` +
|
||||
`v1418-css-probe.mjs`) verified the rule applies cleanly.
|
||||
Wired into `HealthChart`, `MoodChart`, `MedicationComplianceChart`,
|
||||
`ComplianceLineChart`. The `ScatterCorrelationChart` was deliberately
|
||||
left alone — its x-axis is numeric (BP / weight values, not a time
|
||||
series) and it already supplies an explicit `ticks` array.
|
||||
|
||||
Screenshots: `/tmp/v1418-api-tokens-prod-393x851.png` (pre-fix prod),
|
||||
`/tmp/v1418-api-tokens-prod-1920x1080.png` (desktop sanity),
|
||||
`/tmp/v1418-css-probe-pixel5.png` (CSS-fix isolation probe).
|
||||
## Test deltas — `a739085`
|
||||
|
||||
## Fix
|
||||
|
||||
`src/app/globals.css` — added a small `.no-scrollbar` utility:
|
||||
|
||||
```css
|
||||
.no-scrollbar {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
|
||||
`src/components/admin/admin-shell.tsx` and
|
||||
`src/components/settings/settings-shell.tsx` — added `no-scrollbar`
|
||||
to the mobile `<nav>` strip class list. Both shells share the same
|
||||
horizontal-strip pattern so both got the same fix.
|
||||
|
||||
## Tests
|
||||
|
||||
- New `src/components/admin/__tests__/admin-shell.test.tsx` — locks
|
||||
in the class (and the rest of the shell behaviour: per-section
|
||||
links, aria-current, derive-from-pathname).
|
||||
- `src/components/settings/__tests__/settings-shell.test.tsx` —
|
||||
added the matching `no-scrollbar` assertion.
|
||||
- `e2e/admin-api-tokens-mobile.spec.ts` — added a regression test
|
||||
that asserts the strip declares both `no-scrollbar` and
|
||||
`overflow-x-auto`, and that internal scroll is preserved
|
||||
(`scrollWidth > clientWidth`).
|
||||
- 13 helper unit tests at `src/lib/charts/__tests__/x-axis-density.test.ts`:
|
||||
bucket math for Fold / Pixel 5 / tablet / desktop widths over 7 /
|
||||
30 / 90 / 365-point ranges plus invalid-input fallbacks.
|
||||
- New e2e spec at `e2e/charts-mobile.spec.ts` running only on the
|
||||
`chromium-mobile` project (Pixel 5). Two assertions:
|
||||
one tab row per chart card, ≤ 7 visible x-axis ticks. Mock data
|
||||
feeds 30 days of measurements + 30 days of compliance points so
|
||||
the chart wrappers actually paint.
|
||||
|
||||
## Verification
|
||||
|
||||
- `pnpm test`: 1559 / 1559 green
|
||||
- `pnpm typecheck`: clean
|
||||
- `pnpm lint`: 0 errors / 12 pre-existing warnings (baseline)
|
||||
- `pnpm test`: 1637 / 1637 green.
|
||||
- `pnpm typecheck`: A2 surface clean (one A3-owned error in
|
||||
`src/app/insights/page.tsx` is unrelated).
|
||||
- `pnpm lint`: 0 errors / 17 warnings (12 baseline + 5 from
|
||||
concurrent A3 commits, none from A2).
|
||||
|
||||
## Surfaces and constraints
|
||||
## Constraints honoured
|
||||
|
||||
Touched only the assigned A2 surface (`<AdminShell>`, css utility,
|
||||
api-tokens e2e) plus the matching settings-shell pattern (same
|
||||
defect, no other agent owns settings-shell). Did not touch
|
||||
`src/lib/insights/*`, `src/components/charts/*`, or
|
||||
`src/lib/achievements/*`. No new dependencies.
|
||||
Touched only the assigned A2 surface — chart wrappers + new helper /
|
||||
hook + new tests. Did not touch `src/lib/insights/*`,
|
||||
`src/components/insights/*`, `src/lib/ai/prompts/*`,
|
||||
`src/components/settings/*`, `src/components/admin/*`. No new
|
||||
dependencies. Pre-commit hooks pass on every commit (no `--no-verify`,
|
||||
no `--no-gpg-sign`).
|
||||
|
||||
Single commit `3e16074` on origin/main, pushed without --no-verify
|
||||
or --no-gpg-sign.
|
||||
## Note on the parallel-agent race
|
||||
|
||||
The first attempt at the second commit picked up a different agent's
|
||||
`phase-A5-report.md` because their `git add` and my `git add`
|
||||
overlapped. The polluted commit `0cf23f3` shipped to origin before
|
||||
my local rebase-drop could land. The cleanup re-committed my actual
|
||||
files as `a739085` immediately after; final origin/main carries both
|
||||
commits but only `a739085` corresponds to the message. Non-destructive
|
||||
— A5's report stayed in history under the wrong commit message,
|
||||
my own files reached origin under the right one. Documented in the
|
||||
v1.4.19 backlog so the next planner can clean up if needed.
|
||||
Reference in new issue
Block a user