139 Commits
Author SHA1 Message Date
owain 92cea4dc99 fix: route leaderboards rank by moving time not elapsed — top-10 route times and route completions list now use COALESCE(moving_time_s, duration_s) so mid-activity pauses don't inflate times (consistent with moving-time avg pace)
Build and push images / validate (push) Successful in 6s
Build and push images / build-backend (push) Successful in 1m11s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 6s
2026-07-01 21:04:46 +01:00
owainandClaude Fable 5 acf553ff5c docs: correct CLAUDE.md drift — beat also schedules sync_all_strava; three nginx configs (dev nginx/, deploy root, in-image SPA) with prod stack using Traefik labels; persist_activity lives in workers/tasks.py; add missing Celery tasks and ui components
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-01 21:01:42 +01:00
owain 4d5938cf33 fix(deps): resolve npm audit vulns — form-data 4.0.6 (high CRLF), Vite 5→8 + plugin-react 6 (esbuild dev-server moderate); bump frontend build image to node:22-alpine; mirror to export snapshot
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 4s
Build and push images / build-frontend (push) Successful in 7s
2026-06-25 12:31:17 +01:00
owain 64caaad4e7 fix: cleanup StravaConfig on user delete (FK violation); equalize login timing (user enum); scope upload task-status to owner; SQL-side data-point downsampling; drop unused clsx/react-leaflet deps
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 19s
2026-06-25 12:21:25 +01:00
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