Commit Graph
135 Commits
Author SHA1 Message Date
owain 84eb1c46bd fix: map settings no longer revert/lose in-flight edits when profile refetches (dirty-flag guards hydrate clobber)
Build and push images / validate (push) Successful in 4s
Build and push images / build-backend (push) Successful in 54s
Build and push images / build-worker (push) Successful in 44s
Build and push images / build-frontend (push) Successful in 10s
2026-06-25 11:04:53 +01:00
owain 3c97595093 feat: persist map tile settings (provider/style/API keys) server-side on user record; unbake Thunderforest default key into backend config (THUNDERFOREST_DEFAULT_KEY) served via profile
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 7s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 9s
2026-06-23 12:37:27 +01:00
owain ffe285b7ba feat: global map provider/tiles setting (Thunderforest, MapTiler, CARTO, OSM, Esri) selectable in Profile with style + API key + live preview; all maps use it via useMapSettings/resolveTile
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-23 12:29:50 +01:00
owain deecf668d2 feat: route tiles use Thunderforest Outdoors map style (dense labels, paths, contours)
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-23 12:17:13 +01:00
owain 0901ed0ef3 feat: route tiles use OpenStreetMap standard raster for denser place/street labels
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-23 12:11:59 +01:00
owain 62cea59ef8 feat: taller route tiles (h-46), labels overlay above route line for more place names, renamed auto routes become custom (rename clears auto_detected + startup backfill)
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-23 12:08:33 +01:00
owain 16dff88564 fix: route tiles fill with fractional zoom (zoomSnap 0) so the route isn't tiny with wide margins; taller tiles (h-36)
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-23 12:02:49 +01:00
owain 73bc986219 feat: routes page — deep-link to specific route, sport-colour tiles over real map, sort filter (distance/completions/last-completed), group custom vs auto-detected, taller map tiles
Build and push images / validate (push) Successful in 4s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 8s
2026-06-23 11:57:39 +01:00
owain 4272c5dc4c feat: rename segments + create routes from an activity (detail page & dashboard recent activities)
Build and push images / validate (push) Successful in 9s
Build and push images / build-backend (push) Successful in 1m15s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 10s
2026-06-23 11:45:19 +01:00
owain 156ce60aa0 fix: personal records only count watch-recorded FIT activities; exclude phone/Strava GPX/TCX imports (bad GPS teleporting in old activities)
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 5s
2026-06-21 19:51:32 +01:00
owain e2870e86a8 feat: Activities filters (type/year/date-range/distance) + Summary page with all-time & per-year/per-sport totals and distance-per-year chart
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 7s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-21 16:29:33 +01:00
owain 69ecdaa4b2 feat: Strava export dry-run preview (new vs duplicate counts + confirm import) and prefer existing Garmin data over Strava on dedup
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-21 10:41:54 +01:00
owain 07139120df feat: Strava support — bulk-export .gz/.tcx import fix + .tcx parser; full Strava API OAuth live sync (activities via streams) with Profile connect UI; colour gym/no-GPS sports red
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 8s
2026-06-21 10:18:09 +01:00
owain e8615dd12d frontend: week-filter totals (distance/time/count) on Activities; colour HIIT & gym/no-GPS workouts red
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-21 09:40:50 +01:00
owain 7d52b28f9d frontend: share one BodyBatteryChart component across Health + Dashboard (Dashboard now shows mid-activity pause markers too)
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-20 20:16:06 +01:00
owain e33946e270 feat: avg pace over moving time (fixes elapsed-based pace on paused activities); show mid-activity pauses on Body Battery band via active_spans
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 7s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 10s
2026-06-20 20:04:53 +01:00
owainandClaude Opus 4.8 0e1d35364f frontend: standardise metric colours + crisp activity icons platform-wide
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 8s
Colours (Health, Dashboard, Activities, ActivityDetail, Profile):
- Heart rate (resting/avg/24h/intraday + activity HR metric) → red
- Sleep stat text → light-sleep violet; unify dashboard sleep-stage palette
  to the Health-page hypnogram colours
- VO2 max graph + stat coloured by its gauge rating category (shared utils/vo2.js)
- Stress → orange; Steps stay yellow; Weight → blue
- Running → green, Cycling → orange (sportColor)

Icons: new stroke-based SVG SportIcon component replaces emoji everywhere
(lists, filters, activity detail, body-battery overlays, YTD stats) — crisp
at any resolution.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 22:31:23 +01:00
owainandClaude Opus 4.8 a0912fd09a frontend: dashboard recent-activities widget no longer scrolls
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 10s
Rows now flex to fill the card height so the list always fits exactly,
removing the vertical scrollbar that appeared once rows carried a route
line, and the spurious horizontal scrollbar caused by overflow-auto +
the -mx-2 hover bleed. Empty state preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 22:00:42 +01:00
owainandClaude Opus 4.8 c14f3d60fa frontend: health trend graphs — taller widgets, top-right keys, tighter x-axis spacing, HRV line-only (no fill) with baseline-based Y range, weight/VO2 Y axes scaled to displayed data
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 10s
- All trend chart widgets taller (140->170) with bottom margin trimmed to 0 to cut empty space below the x-axis
- Sleep trend legend moved to top-right beside the title (matches HRV)
- HRV (overnight avg) now line+dots only (no purple gradient fill) so the grey baseline band reads clearly; Y axis spans baseline-low -15ms to baseline-high +15ms
- Weight Y axis now +/-5kg around displayed range (still keeps goal line in view)
- VO2 Max Y axis now displayed min/max +/-5 points instead of fixed 30-70

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 21:47:24 +01:00
owainandClaude Opus 4.8 227dadaca0 feat: rename activities; keep original Garmin title as a tag
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
Add Activity.original_name (migrated via init_db ALTER). The rename
endpoint stores the import title the first time a user renames and
clears it if renamed back. Activity detail page gets inline rename
(pencil) UI and shows the original title beneath the new name; the
activities list shows it as a small tag too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 21:35:09 +01:00
owain cc43d0f726 frontend: fix health body-battery activity bands not rendering (inline ReferenceArea so recharts recognizes them)
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 10s
2026-06-18 14:08:44 +01:00
owain 471e43466c frontend: body battery activity overlays on dashboard; redesign highlight as coloured band below the data
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-18 13:56:16 +01:00
owain a50d13179c frontend: global km/mi distance-unit toggle across dashboard, activities, routes, records
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-18 13:01:25 +01:00
owain e61c77842f api: send Cache-Control no-store on /api responses so browsers can't cache stale auth state (e.g. garmin connected)
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 5s
2026-06-18 12:07:13 +01:00
owain 385443522b frontend nginx: no-cache on index.html so deploys aren't masked by stale SPA shell (Edge heuristic caching)
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 10s
2026-06-18 12:00:58 +01:00
owain 66076f2585 garmin sync: fix same-day second activity skipped by date-only dedup
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 5s
2026-06-18 11:38:10 +01:00
owainandClaude Opus 4.8 3a07655e2e dashboard sleep widget: fill empty space with hypnogram graph from Health tab
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 9s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 18:54:49 +01:00
owain c172bbe393 lap awards: 🥇 PR rows highlighted, 🏆 route lap-best, no awards on partial laps
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
2026-06-16 12:38:29 +01:00
owain a168f594a7 fix lap-bests distance matching + show PRs on activity page
Build and push images / validate (push) Successful in 5s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 9s
2026-06-16 11:38:12 +01:00
owain 16144d60b4 HRV: plot Garmin overnight avg (weeklyAvg) not last-night; dedupe chart tooltips
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 8s
2026-06-16 01:01:33 +01:00
owain d01f66223b HRV baseline band from Garmin + dashboard HRV colours + route name on recent activities + sync-now race fix
Build and push images / validate (push) Successful in 7s
Build and push images / build-backend (push) Successful in 1m9s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 10s
2026-06-16 00:13:16 +01:00
owainandClaude Fable 5 e7123ee5db Mobile-responsive UI: bottom tab nav, stacked dashboard, page fixes for phones
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 10s
- Layout: sidebar hidden below md; mobile top header + bottom tab bar
  (Dashboard/Activities/Health/Routes) with a More slide-up sheet holding
  Records/Import/Profile/Users, Garmin sync progress and sign-out
- Dashboard: single-column widget stack on phones ordered from the saved
  desktop layout (stat cards pair into 2-col grid); drag-edit stays
  desktop-only; grid conditionally mounted so WidthProvider never
  measures a hidden container
- New useMediaQuery/useIsMobile hook (matchMedia, md breakpoint)
- ActivityDetail: 2-col stats on phones, wrapping map toolbar, Height
  control hidden on small screens
- Activities: compact distance/time/pace line on mobile rows
- Records/Users: horizontally scrollable tables; route records hide
  Pace/Date columns below sm
- Profile forms single-column on phones; Routes expanded card stacks;
  Health flex-wrap fixes; p-4 md:p-6 page padding; h-dvh for mobile
  browser chrome
- vite.config: VITE_PROXY_TARGET override for host-side dev

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:52:10 +01:00
owainandClaude Fable 5 7673452bdb Refresh CLAUDE.md: lockfile status, new Celery tasks, sleep GMT convention, newer hooks/utils
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:52:10 +01:00
owainandClaude Opus 4.8 f69d34508d Sleep hypnogram: hover tooltip with exact times + darker REM colour
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
Add per-segment mouseover tooltip (stage, start–end clock, duration) to the
sleep timeline, matching the other trend charts. Darken REM from #8b5cf6 to
#7c3aed across the hypnogram, fallback bar, legends and SleepChart so it reads
distinctly from light sleep (#a78bfa).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 12:33:28 +01:00
owainandClaude Opus 4.8 e3964bbcdc Populate sleep_stages hypnogram timeline from Garmin Connect sync
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 7s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 6s
Parse Garmin's sleepLevels array in _parse_day into the existing
[[ts_ms, level], ...] format (matching the FIT parser's encoding) so the
SleepHypnogram lights up for Garmin-Connect-synced days, not just _SLEEP.fit
uploads.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 12:21:21 +01:00
owain 319b04e413 fix(sleep): store sleep timestamps from GMT not Local (displayed +1h in BST)
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 5s
2026-06-12 12:03:16 +01:00
owainandClaude Opus 4.8 b5b838bddc Fix treadmill/indoor distance over-measure and clean up polluted PRs
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 5s
- Garmin Connect sync now applies Garmins corrected summary distance/moving time,
  overriding the raw wrist-estimated FIT distance for treadmill/indoor runs
- Exclude indoor (no-GPS) runs from distance personal records (bogus fast splits)
- backfill_indoor_distances task: re-fetch corrected distance for historical indoor runs
- recompute_personal_records_all task: wipe and rebuild PRs from valid activities

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 23:55:21 +01:00
owainandClaude Opus 4.8 e9cb1ea4e4 Dashboard Running PRs: show fixed 1k, 1 mile, 5k, 10k distances
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 9s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 23:42:12 +01:00
owainandClaude Opus 4.8 c05d27c115 Dashboard grid: 1-col min width for stats, vertical compaction for make-room dragging
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 5s
Build and push images / validate (push) Successful in 2s
Build and push images / build-frontend (push) Successful in 9s
- Stat widgets can now be resized down to a single column (minW 1)
- Switch to vertical compaction so dragging a widget pushes others down to reveal the drop spot, then settles (fixes both the no-move and fly-off behaviours)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 23:36:29 +01:00
owainandClaude Opus 4.8 bb09c37b3d Dashboard polish: bounded drag push, equal-height stats, dark featured map, sport-coloured weekly bars
Build and push images / validate (push) Successful in 3s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 9s
- Prevent widgets flying down the page when dragging (preventCollision)
- Stat widgets fill their cell height so cards with/without sub-text align
- Featured-activity map defaults to dark tiles
- Weekly distance bars stacked and coloured by activity type, with a legend

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 23:31:04 +01:00
owainandClaude Opus 4.8 491660fc6b Dashboard: per-widget add/delete, free placement, VO2/HRV stats, body battery graph, more widgets
Build and push images / validate (push) Successful in 3s
Build and push images / build-frontend (push) Successful in 9s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
- VO2 max and HRV status now available as top-bar stat widgets
- Edit mode can add (palette) and remove (x) individual widgets
- Body Battery widget shows todays intraday graph (fixed collapsing height)
- compactType disabled so widgets stay put and align along top edges (no jumping up)
- New optional widgets: cycling, stress, active calories, floors, VO2 trend, sleep stages, weight trend

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 23:20:25 +01:00
owainandClaude Opus 4.8 8ed47d6042 HRV balanced dots, dashed gap lines, dashboard widgets + drag-to-edit layout
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 21s
- Green dots for balanced HRV (joining orange unbalanced / red low) on the trend
- Trend charts bridge data gaps with a dashed line instead of a blank break
- Dashboard: drop Health today; add VO2 max, small sleep, and HRV status widgets
- Dashboard: editable widget grid (react-grid-layout) with drag/resize, saved per-user

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 23:04:43 +01:00
owainandClaude Opus 4.8 af32a0bb7f Add medals, HRV status dots, smooth segment hover, side-by-side map/timeline, HR zone times
Build and push images / validate (push) Successful in 3s
Build and push images / build-frontend (push) Successful in 10s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
- Silver/bronze medals (not just gold) on route & segment leaderboards
- Colour HRV nightly-avg trend dots: orange unbalanced, red low
- Project segment-hover dot smoothly along the track line (interpolated)
- Show map and activity timeline side by side, half width each
- Show time spent in each HR zone next to the percentage

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 22:44:20 +01:00
owainandClaude Opus 4.8 ec87f68729 Add trend-range gating, vehicle filter, sync cancel, moving time, and UI fixes
Build and push images / validate (push) Successful in 9s
Build and push images / build-backend (push) Successful in 1m57s
Build and push images / build-worker (push) Successful in 50s
Build and push images / build-frontend (push) Successful in 24s
- Grey out trend ranges beyond available health history
- Reject implausibly fast (vehicle) activities on upload with feedback
- Add cancel button + cooperative cancellation for Garmin sync
- Show daily steps prominently on the dashboard
- Clear errors for malformed/empty upload ZIPs
- Snap-target dot when drawing a segment on the map
- Time-axis fallback for stationary/HIIT HR timelines; hide map when no GPS
- Parse and display moving time (timer) vs elapsed; backfill task
- Restyle SegmentsPanel like RouteLeaderboard; Laps/Routes/Segments on one row

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 19:41:56 +01:00
owainandClaude Opus 4.8 057eb9391a Fix passkey-disabled message obscured by null hash check
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 7s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 5s
Check pocketid_sub before hashed_password so users with a linked
passkey (and hence a null hash) get the helpful message rather than
"Invalid credentials".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 21:38:12 +01:00
owainandClaude Opus 4.8 01a8fe135c Disable password login once a passkey is linked
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 6s
- /token: reject password auth with a clear message if pocketid_sub is
  set on the account — passkey-linked users must sign in via PocketID
- Link callback + auto-link-by-email: null out hashed_password when the
  passkey is attached so the old hash can't be used even if the check
  above were bypassed

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 21:34:23 +01:00
owainandClaude Opus 4.8 d350e9caea Add per-route top-10 leaderboard to activity detail
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 5s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 9s
New /activities/{id}/route-leaderboard endpoint ranks the user's timed
efforts on the same route; frontend RouteLeaderboard card sits beside
Laps, showing this activity's time/rank/gap and the top 10 (current
effort highlighted green, also surfaced if outside the top 10).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 20:37:37 +01:00
owainandClaude Opus 4.8 bdd5f80c7e Harden auth/upload, fix PR-delete cascade and sync backfill
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 8s
- OIDC: require signed short-lived state on login callback; reject
  missing userinfo sub (account-takeover guard); validate token
  exchange + userinfo responses
- Upload: safe zip extraction (path-traversal + zip-bomb cap),
  streamed size-capped writes, sanitised filenames
- Garmin: increasing lookback resets last_sync_at for one-time backfill
- Activities: delete/reprocess remove PersonalRecord rows (no FK cascade)
- Profile: validate /weight limit; sync lookback UI copy
- Dashboard: sleep shading uses same day as charted body battery

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 20:24:24 +01:00
owainandClaude Opus 4.8 04689a29bd Cut Garmin sync API volume; dashboard/health/records/UI improvements
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 9s
Garmin Connect sync:
- Incremental syncs now re-fetch only a 1-day buffer (yesterday + today)
  instead of the full lookback window every run. Full lookback applies on
  the first sync only. Cuts steady-state API calls ~10x.
- Beat interval is now configurable via GARMIN_SYNC_INTERVAL_MINUTES and
  surfaced to the UI; the sync toggle is relabelled to the real cadence.

Frontend:
- Collapsible sidebar; clearer logged-in user + role display.
- Unified Body Battery colouring between dashboard and health (shared util).
- Sleep score trend chart on health page.
- Segments + medals on the dashboard's most-recent activity.
- Segments tab on the Records page.

Repo hygiene: add .gitignore, untrack committed __pycache__/*.pyc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 11:52:52 +01:00
owainandClaude Opus 4.8 6a1726e0c3 Fix sleep score parsing, dashboard body battery, segment direction
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 9s
- Garmin sync: read sleepScores from dailySleepDTO (Garmin nests it there),
  so sleep score is actually stored instead of always null
- Dashboard: pass YYYY-MM-DD to the intraday endpoint (was a full ISO
  timestamp), so the body-battery tile populates
- Segment matching: follow the segment in its created direction with a
  path-length sanity check, so out-and-back routes no longer match an early
  start pass to a late finish (the >1h bogus segment times)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 20:58:53 +01:00