mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
docs(planning): record phase B4 completion in STATE.md + report
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c1ce15d1c3
commit
53561d3ad5
2 files changed
+170
-2
No files matched your search
+49
-2
@@ -362,10 +362,57 @@ sibling agent removed an unrelated unused-var).
|
||||
|
||||
### B4 — Achievements UI
|
||||
|
||||
- [ ] Surface in dashboard or dedicated page
|
||||
- [ ] Unlocked-at timestamp visible
|
||||
- [x] Surface in dashboard or dedicated page (both)
|
||||
- [x] Unlocked-at timestamp visible
|
||||
- Detailed report: `.planning/phase-B4-report.md`
|
||||
|
||||
#### B4 status block — 2026-05-09T21:22+02:00 — done
|
||||
|
||||
3 commits on `origin/main`:
|
||||
|
||||
- `34c967c` `feat(achievements): dedicated /achievements page with
|
||||
locked/unlocked list` — full page rewrite. Locked entries render
|
||||
grayed out with `Lock` icon + criterion hint
|
||||
(`{current} / {target}`) + 0-100% progress bar; unlocked entries
|
||||
keep the highlighted primary-gradient variant with the "Completed
|
||||
on …" footer. Grouped by new `category` axis (medication, vitals,
|
||||
security, engagement) — derived stably in
|
||||
`getAchievementCategory()`, render order in
|
||||
`ACHIEVEMENT_CATEGORY_ORDER`. Sidebar link was already in place
|
||||
under `navItems`.
|
||||
- `a242047` `feat(dashboard): recent achievements card with toggle in
|
||||
layout settings` — new `achievements` widget id (default order 13,
|
||||
visible by default, `tileVisible: false`), `WIDGET_LABEL_KEYS` entry,
|
||||
dashboard wiring via `showAchievementsCard = isChartVisible(...)`.
|
||||
- `81f5019` `feat(dashboard): RecentAchievementsCard component +
|
||||
tests (b4 follow-up)` — actual component + tests; split from
|
||||
`a242047` because `git commit -o` excluded the untracked component
|
||||
files (race-avoidance trick), so the wiring commit referenced an
|
||||
empty module until this follow-up landed.
|
||||
|
||||
i18n: 9 new keys under `achievements.*` (EN + DE). C4 will sweep.
|
||||
|
||||
Tests: 957 / 957 unit pass (was 890 at phase start; +15 from B4, +52
|
||||
from sibling agents). +1 e2e (`e2e/achievements.spec.ts` walks the
|
||||
desktop sidebar → `/achievements` heading visible). Pre-existing
|
||||
typecheck errors in `dashboard-layout.test.ts` (3) and
|
||||
`doctor-report-pdf-core.test.ts` (1) reproduce on a clean stash so
|
||||
they're not B4's; lint clean for B4 files (1 error in B6's untracked
|
||||
`doctor-report-dialog.tsx` — outside scope).
|
||||
|
||||
Race-condition pattern (echoing A2 / A4 / B1 / B2 / B3): commit
|
||||
`34c967c`'s `git diff --cached` listed 6 files but the commit-stat
|
||||
shows 9 — three C2 auto-deploy files (`.env.example`,
|
||||
`.github/workflows/docker-publish.yml`,
|
||||
`docs/audit/v1415-auto-deploy.md`) got swept in between staging and
|
||||
commit. Files correct on `main`; message scope narrower than diff.
|
||||
v1.4.16: adopt `superpowers:using-git-worktrees` per agent.
|
||||
|
||||
Out of scope (not done): relative time formatting for `unlockedAt`
|
||||
("3 days ago") — left as v1.4.16 polish (no `Intl.RelativeTimeFormat`
|
||||
helper exists yet, adding one would have expanded B4's blast
|
||||
radius).
|
||||
|
||||
### B5 — Onboarding tour first-run
|
||||
|
||||
- [ ] Welcome flow for new users (after passkey registration)
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
# Phase B4 — Achievements UI
|
||||
|
||||
**Status**: done
|
||||
**Completed**: 2026-05-09T21:22+02:00
|
||||
**Commits**: 3 (`34c967c`, `a242047`, `81f5019`) all on `origin/main`.
|
||||
|
||||
## Scope shipped
|
||||
|
||||
### 1. Dedicated `/achievements` page (`34c967c`)
|
||||
|
||||
Pre-v1.4.15 the page silently dropped locked achievements; only unlocked
|
||||
ones rendered. Now every badge paints — locked entries grayed out at
|
||||
opacity-70 with a `Lock` icon + `"Locked"` badge, the criterion hint
|
||||
(`{current} / {target}`), and a 0–100% progress bar. Unlocked entries
|
||||
keep the existing primary-gradient highlighted card with the
|
||||
"Completed on …" footer. Sidebar link to `/achievements` was already in
|
||||
place under `navItems` in `sidebar-nav.tsx`.
|
||||
|
||||
Achievements are grouped by a new presentation-only `category` axis —
|
||||
`medication`, `vitals`, `security`, `engagement` — derived in
|
||||
`getAchievementCategory()`. Stable category render order in
|
||||
`ACHIEVEMENT_CATEGORY_ORDER`. Inside a category, unlocked items sort
|
||||
first; among locked items the closest-to-unlock sorts first so the
|
||||
user always sees their immediate next goal at the top.
|
||||
|
||||
### 2. Dashboard recent-unlocks card (`a242047` + `81f5019`)
|
||||
|
||||
`<RecentAchievementsCard>` shows the three most-recently unlocked
|
||||
achievements (sorted by `completedAt` descending). When nothing is
|
||||
unlocked yet, the card paints a discovery CTA + link to `/achievements`
|
||||
so brand-new users learn the feature exists. Reuses the existing
|
||||
`/api/gamification/achievements` endpoint — TanStack Query dedupes when
|
||||
both this card and the page mount in the same session.
|
||||
|
||||
Layout integration: new `achievements` widget id in `DashboardWidgetId`,
|
||||
default order 13 (below the chart row, matching the brief),
|
||||
`tileVisible: false` because there is no tile surface for this widget.
|
||||
Visible by default. Toggleable from Settings → Dashboard via the
|
||||
existing `<DashboardLayoutSection>` machinery.
|
||||
|
||||
## i18n
|
||||
|
||||
9 new keys under `achievements.*` (EN + DE):
|
||||
`locked`, `criterionHint`, `progressPercent`,
|
||||
`categories.{medication,vitals,security,engagement}`,
|
||||
`dashboardCard.{title,viewAll,empty}`. C4's parity audit will sweep
|
||||
later as planned.
|
||||
|
||||
## Tests
|
||||
|
||||
- `+9 unit` — `groupByCategory` ordering, page render in EN/DE, locked
|
||||
vs unlocked card variants
|
||||
- `+6 unit` — `pickRecentUnlocks` ordering / cap / no-date fallback,
|
||||
empty-state CTA, three-most-recent render shape
|
||||
- `+1 e2e` — sidebar → click → `/achievements` heading visible
|
||||
|
||||
957 / 957 unit pass (was 890 baseline at phase start; +15 from B4, +52
|
||||
from sibling agents).
|
||||
|
||||
## Race conditions
|
||||
|
||||
The 5-parallel-agent shared-cwd staging collision struck twice during
|
||||
B4, matching the pattern STATE.md notes from A2 / A4 / B1 / B2 / B3:
|
||||
|
||||
1. Commit `34c967c`'s `git diff --cached --stat` listed 6 files but the
|
||||
final commit-stat shows 9 — `.env.example`,
|
||||
`.github/workflows/docker-publish.yml`, and
|
||||
`docs/audit/v1415-auto-deploy.md` (all C2's auto-deploy work) got
|
||||
pulled in between my `git add` and `git commit`. Files are correct
|
||||
on `main`; the message scope is narrower than the actual diff.
|
||||
2. The dashboard-card commit had to be split into two
|
||||
(`a242047` for the wiring + `81f5019` for the
|
||||
`RecentAchievementsCard` component file). Used `git commit -o
|
||||
<pathspec>` to avoid sweeping in a sibling agent's untracked files,
|
||||
but `-o` excluded my own untracked `<RecentAchievementsCard>` source
|
||||
too — the follow-up `81f5019` commit landed it. Splitting into two
|
||||
commits per `verification-before-completion` rather than amending so
|
||||
the parallel-agent push race can't lose the landing.
|
||||
|
||||
Recommendation echoes prior phases: v1.4.16 should adopt
|
||||
`superpowers:using-git-worktrees` per agent.
|
||||
|
||||
## Files changed (B4 scope, by commit)
|
||||
|
||||
`34c967c`:
|
||||
|
||||
- `src/lib/gamification/achievements.ts` — new `AchievementCategory`
|
||||
type, `getAchievementCategory()`, `ACHIEVEMENT_CATEGORY_ORDER`,
|
||||
`category` field on `AchievementProgress`
|
||||
- `src/app/achievements/page.tsx` — full rewrite: locked-vs-unlocked
|
||||
cards, category grouping, exported `groupByCategory` for unit tests
|
||||
- `src/app/achievements/__tests__/page.test.tsx` — new
|
||||
- `e2e/achievements.spec.ts` — new
|
||||
- `messages/en.json` + `messages/de.json` — 9 new keys
|
||||
|
||||
`a242047`:
|
||||
|
||||
- `src/lib/dashboard-layout.ts` — `achievements` widget id + default
|
||||
layout entry
|
||||
- `src/components/settings/dashboard-layout-section.tsx` —
|
||||
`WIDGET_LABEL_KEYS["achievements"]` mapping
|
||||
- `src/app/page.tsx` — import + `showAchievementsCard` gate + chart-row
|
||||
slot
|
||||
- `src/app/achievements/page.tsx` — prettier compaction follow-up to
|
||||
`34c967c` (pure formatting)
|
||||
|
||||
`81f5019`:
|
||||
|
||||
- `src/components/gamification/recent-achievements-card.tsx` — new
|
||||
- `src/components/gamification/__tests__/recent-achievements-card.test.tsx` —
|
||||
new
|
||||
|
||||
## Out of scope (not done)
|
||||
|
||||
- Relative time formatting (`"3 days ago"`) — the brief mentioned
|
||||
formatted-relative for `unlockedAt`, but the existing
|
||||
`formatDate(completedAt)` is well-tested across the app, no
|
||||
`Intl.RelativeTimeFormat` helper exists yet, and adding one as a
|
||||
drive-by would expand B4's blast radius. Left as v1.4.16 polish.
|
||||
- Sidebar nav link addition — was already in place in `sidebar-nav.tsx`
|
||||
under `navItems` (verified via the e2e spec which clicks it).
|
||||
Reference in new issue
Block a user