Commit Graph
9 Commits
Author SHA1 Message Date
Marc-André Bombeck 3f5816112b feat(dashboard): recent workouts tile
Adds `<RecentWorkoutsTile>` as a default-visible dashboard widget
(`recentWorkouts`). The tile reads the top three canonical workouts
through `useWorkouts({ limit: 3 })`, renders each as a deep-link
row with the sport icon + the day + the duration, and falls back
to an Apple-Health-onboarding cue when the workout list is empty.

`DASHBOARD_WIDGET_IDS` + `DEFAULT_DASHBOARD_LAYOUT.widgets` carry
the new id; `widgetIdEnum` on `/api/dashboard/widgets` picks it up
from the same source-of-truth array. The Settings → Dashboard
layout editor surfaces it under the existing widget toggle list so
users can opt out without touching the layout JSON. Six locales
updated.
2026-05-16 15:47:44 +02:00
Marc-André Bombeck 8e5f71b1b9 chore(dashboard): retire the GLP-1 tile
Drop the standalone GLP-1 dashboard tile and its data-fetch route.
The /api/dashboard/glp1 endpoint has no consumer outside the retired
tile (no native client reads it), so the route, the tile component,
its test, the e2e spec, the locale keys, the query-key entry and the
drift-guard assertion all retire together.

The medication detail page and its bidirectional contracts stay
untouched. An opt-in dashboard widget that surfaces the same level
chart will land later in this cycle, anchored on the existing
dashboard-layout toggle list.
2026-05-16 01:00:09 +02:00
Marc-André Bombeck 17aed374eb chore(dashboard): apply the tap-target floor across the dashboard tile and onboarding chrome
Lift the GLP-1 tile range strip + tab pills, the getting-started
checklist dismiss buttons, and the settings reorder + source-ladder
arrow buttons so every interactive element clears the 44 px WCAG
2.5.5 floor.

- `glp1-tile.tsx`: range-strip radio buttons gain `inline-flex
  min-h-11 items-center justify-center px-3` so each preset sits at
  44 px; the tab pills (level / weight) lift the same way.
- `getting-started-checklist.tsx`: dismiss-all CTA drops `size="sm"`
  so it sits at the default 40 px primitive height; per-row dismiss
  `X` button wraps in `inline-flex h-11 w-11`.
- `dashboard-layout-section.tsx`: reorder up + down arrows `h-5 w-5`
  → `size-11`; column-header spacer widens to `w-11` to keep the
  layout grid aligned.
- `sources-section.tsx`: metric source ladder + device-type ladder
  up/down arrows stack vertically on `<sm` so each button keeps the
  `h-11 w-11` floor without crowding the row; sit side-by-side again
  from `sm:` up to preserve the desktop layout.
2026-05-15 18:45:08 +02:00
Marc-André Bombeck 9b0b1c6b84 chore(ui): repair stray motion-reduce tokens that bled into JSDoc comments and pair the mood chart spinner 2026-05-15 17:25:57 +02:00
Marc-André Bombeck 32a023dfff chore(ui): apply M7 + L1 + L3 + L4 polish across medication form, motion-reduce, details aria, therapy timeline
W10 design-review carry-over items. M7 lifts the medication-form
day-of-week buttons, the more-horizontal trigger, and the schedule
label-input gaps to the 44 px / space-y-2 standard used by every
other settings surface. L1 pairs every animate-spin/pulse/bounce
with motion-reduce:animate-none across the component tree so users
with prefers-reduced-motion get a still surface. L3 adds the
aria-controls + panel-id pairing to the inventory + coach-evidence
<details> blocks for parity with the Health Score provenance
accordion. L4 inserts a sr-only h4 per therapy-timeline entry so
screen-reader users can jump between medication blocks.
2026-05-15 17:13:42 +02:00
Marc-André Bombeck 053d678ac7 refactor(dashboard): promote GLP-1 schedule dates to a header pill row and drop the green seam
The faint dracula-green left-seam on the outer wrapper was decoration
without a semantic tie to the schedule rows beside it. The Syringe-icon
in the title row already carries the 'active therapy' signal at a
glance.

Replace the <dl> grid with a soft-green pill bar that groups Last- and
Next-Injection as one cohesive 'therapy schedule' unit. Each pill keeps
its data-slot anchor (glp1-tile-last / glp1-tile-next) so existing
tests stay in lockstep; new test asserts the data-slot='glp1-tile-schedule'
wrapper renders and the border-l-dracula-green/60 + border-l-2 classes
no longer appear on the outer wrapper.
2026-05-15 16:44:39 +02:00
Marc-André Bombeck c0ec1fecfb feat(dashboard): wire the GLP-1 drug-level pane behind a tab strip and range picker
The GLP-1 tile previously rendered a single weight chart — a downstream
proxy for the dose-response. The maintainer-flagged headline is the
estimated drug-level curve because it surfaces the sawtooth shape of
the weekly cycle directly.

Add a two-tab segmented control (Drug-Level default / Weight) inside the
chart pane and a 7d / 30d / 90d / All range strip above. The Drug-Level
tab mounts <DrugLevelChart compact …>, which now drops its outer card
wrapper and renders at 160 px instead of 240 px when 'compact' is set,
so the dashboard tile owns the card surface. Range selections thread
through a new windowHoursBefore prop on DrugLevelChart; the sample step
scales 6/12/24/48 h with window length so 'All' mode does not paint
thousands of points. The standalone surface at /medications/[id]/history
keeps its full 21-day default and full-card chrome — both consumers
share one implementation.

New translation keys referenced (B6 sweep picks up):
- dashboard.glp1.tabLevel
- dashboard.glp1.tabWeight
- dashboard.glp1.tabsAria
- dashboard.glp1.rangeStripLabel
- dashboard.glp1.levelUnavailable
- dashboard.glp1.weightUnavailable

Test coverage extended: glp1-tile test asserts tab strip + range strip
DOM, drug-level default selection, level-unavailable hint when the
medication has no id; the existing DrugLevelChart test suite continues
to pin the standalone-surface contract.
2026-05-15 16:43:24 +02:00
Marc-André Bombeck 98f7d11861 chore: resolve parallel-agent merge conflicts + sweep up loose edits
7 conflict files resolved by taking the upstream/HEAD side (which carries
the W4d/W5/W6c/W3f feature additions). Stashed-side changes that were
genuine new features re-land in subsequent commits if any were lost.

This sweeps up the loose state the W4e+W5e+W5d combined agent left
mid-stash. Resolves develop's working tree for clean push.
2026-05-14 14:19:08 +02:00
Marc-André Bombeck 88efd42755 feat(dashboard): GLP-1 status tile with next-injection + weight-delta caption
Renders a dedicated tile for users on a GLP-1 receptor agonist
(Mounjaro, Ozempic, Wegovy, Zepbound, …) — the new W4d schema enum
`Medication.treatmentClass = GLP1` is the discriminator. Tile self-
hides when no active GLP-1 med exists so accounts without one see no
visual change.

The tile surfaces:
  - Drug name + current dose ("Mounjaro 7.5mg") — sourced from the
    `MedicationDoseChange` titration history (latest row wins), so a
    Mounjaro user titrating up 2.5 → 5 → 7.5 → 10 mg sees their
    current step, not the starting dose.
  - Last injection date + weekday ("Sun, 05/10")
  - Next injection date + weekday + countdown
    ("Sun, 05/17 (in 3 days)") — predicted from the existing schedule
    cadence
  - Weight delta since starting the therapy ("−4.2 kg since start"
    / "−4,2 kg seit Beginn") — green chip on loss, orange on gain,
    muted minus on flat (within ±0.1 kg)
  - A compact weight chart with vertical injection-day markers via
    the new `<HealthChart verticalMarkers={…} />` prop

New server endpoint `GET /api/dashboard/glp1` aggregates the data:
the heavy lifting (current dose / last + next injection / titration
history / inventory math) is delegated to the existing
`buildGlp1SnapshotBlock` so the Coach's GLP-1-aware reply path and
the dashboard tile read from the same shape. The route adds the UI-
facing extras (weight series since first dose-change, current vs
start, injection date keys for the markers) on top.

Mount: dashboard mother page (`src/app/page.tsx`) mounts the tile
below the AI insights preview and above the chart row — high enough
that a Mounjaro user sees their dose-response without scrolling past
BP / pulse, but below the AI preview which stays the scroll-stop
hero anchor.

Constraint respected (Marc directive 2026-05-14): the chart lives on
the Dashboard tile and the Insights /medikamente sub-page only; the
Medications page itself stays chart-free.

i18n keys: dashboard.glp1.{title, lastInjection, nextInjection,
weightDelta, inDays, empty.title, empty.description} in EN + DE.

Tests pin 7 scenarios: hidden when no active GLP-1, hidden when meds
array is empty, skeleton during fetch, full render for a Mounjaro 7.5
mg weekly profile, latest titration step wins (10 mg, not 2.5 mg),
fallback to name-only when no dose history exists, German copy under
the de locale.
2026-05-14 13:37:29 +02:00