Pre-existing format drift accumulated across v1.4.x; running
prettier brings the whole tree back to baseline so future diffs
read clean. No behaviour change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Seven mechanical cleanups, no behaviour changes:
- Drop the duplicate ChartOverlayPrefs type + DEFAULT in
chart-overlay-controls; re-export the canonical pair from
dashboard-layout so a future fourth toggle can't drift between
the UI and persistence layers.
- useChartOverlayPrefs accepts null/undefined chartKey and short-
circuits the dashboard-layout fetch when none is supplied. Drops
the chartKey ?? "bp" fallback and the three discard-the-result
ternaries in health-chart; mini-mode renders no longer subscribe
to the BP chart's persisted prefs.
- /api/analytics no longer pair-and-counts BP twice — the windowed
helper already produces last30Days, so the headline reads it back
instead of running computeBpInTargetPct on the same input.
- Drop the redundant moodEntries.map shape in the achievements
route; the prisma select already returns the matching shape.
- Merge the five hidden-metric cases into the no-precondition block
of isEarnable; the defensive comment admitted they couldn't fire.
- Remove the deprecated getAchievementCategory wrapper (zero callers).
- Strip six "v1.4.18 gradient removed" what-comments scattered
across the chart wrappers; the polish tests pin the absence.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hidden Easter-eggs leaked their trigger semantics through the JSON
response: a curious user with DevTools open could read metric names
(nightOwlCount, leapDayCount, etc.), i18n keys (hiddenNightOwl,
hiddenLeapDay, etc.), icon names, and target values without unlocking
anything. The opaque-card UI was already correct, but the wire shape
spoiled the surprise.
Project hidden+locked entries to a sentinel shape before serialising
(achievements.hiddenCard.title key, HelpCircle icon, target 0,
metric totalTakenIntakes), and drop hidden-only metric counters from
the metrics block unless the matching achievement is unlocked. The
iOS branch reuses the same redacted view so native clients can't
short-circuit the surprise via the resolved-strings shape.
Once an Easter-egg unlocks the real fields ship — the unlock IS the
reveal moment.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two trigger fixes so the new achievements actually fire end-to-end.
The doctor-PDF hidden Easter-egg used to watch a non-existent
`doctor-report.export` audit action. The real route emits two:
`doctor-report.generate` (the JSON helper) and
`doctor-report.pdf.generate` (the PDF route). Either now counts.
The locale-flip hidden Easter-egg used to watch
`settings.locale.update`, which no endpoint emits — the locale
field shares the catch-all `profile.update` audit row with every
other profile field. Profile-update now emits a separate
`settings.locale.update` row when (and only when) the locale value
actually changes (compares the new value to the prior persisted
locale, suppresses the row when they match), with the from/to
values in the details payload for traceability.
Already-existing TanStack Query invalidation map covers the new
metrics: `measurementDependentKeys`, `moodDependentKeys`, and
`medicationDependentKeys` already include
`queryKeys.gamificationAchievements()`, so any measurement / mood /
intake mutation triggers a refetch and the route persists the new
unlock to `UserAchievement` (existing v1.4.15 mechanism).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the v1.4.18 expansion metrics into the gamification route.
- Fetches `MoodEntry` rows alongside the existing measurement /
intake / passkey / audit fetches so the mood badges have data.
- Extends the audit-log query with `doctor-report.export` and
`settings.locale.update` (the new hidden Easter-egg triggers).
- Pushes the raw rows through `buildExpansionMetricValues` —
mood streak / improvement, weight/BP/pulse counts, consistent-month,
entry day-streak, weekend warrior, and the hidden trigger counts.
- Computes `EarnabilityFlags` from the same numbers and applies
`applyDiscoveryFilter` to the evaluated achievement list. Locked
public badges that the user has *no path* to earn yet are filtered
out; hidden Easter-eggs and already-unlocked badges always pass.
- Recomputes the headline summary (`unlockedCount`, `totalCount`,
`earnedPoints`, `totalPoints`, `nextAchievement`) from the visible
set so the dashboard counters match the rendered list. The
`nextAchievement` selector skips hidden cards so we never tease
the user with a "next goal: ?" suggestion.
The iOS format mock had to grow a `moodEntry.findMany` mock; that's
a one-line pure-test change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Long-overdue prettier sweep; prettier had drifted vs. checked-in code
across ~130 files (mostly long-argument line-break wrapping in TS and
table-padding in markdown). Pure formatting — no semantic changes —
and required to clear the v1.4.6 release gate's `pnpm format:check`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an opt-in `?format=ios` query parameter to
`GET /api/gamification/achievements`. When set, the response collapses
the existing `{ achievements, summary }` envelope into a flat
`Achievement[]` with translated `title`/`description` (server-side `t()`
honouring `Accept-Language` + `user.locale`) and `progress` normalised
to 0..1.
Default response (no query param) is unchanged. Existing web client
keeps receiving the wrapped `{ achievements, summary }` plus i18n keys
exactly as before.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HealthLog — personal health-tracking PWA with weight, blood pressure,
pulse, mood tracking, medication compliance, Withings integration,
doctor report PDF export, and Dracula-themed UI.