Round 2: body battery redesign, profile cleanup, segment integration, route/segment records
Build and push images / validate (push) Successful in 18s
Build and push images / build-backend (push) Successful in 31s
Build and push images / build-worker (push) Successful in 32s
Build and push images / build-frontend (push) Successful in 34s

- Body battery: replace circular ring with compact full-height colored bar chart,
  level as line overlay, legend shows only types present in data
- Dashboard: add mini body battery summary card above health today panel
- Profile: remove editable resting HR and manual weight log; show 7-day avg
  resting HR and latest Garmin weight as read-only
- Backend: add GET /routes/{id}/segment-bests bulk endpoint (fetches all matched
  activity data points in one query, computes best segment time per segment)
- Backend: add GET /records/routes for fastest activity per named route
- Routes page: add Segments panel to route detail (grouped as 1km splits vs
  hills/turns, best times, delete, theoretical best)
- Activity detail page: show segment times computed client-side from data points,
  🏆 badge if new best
- Records page: add Route Records and Segment Records tabs alongside Distance PRs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 13:14:00 +01:00
parent 02eccad578
commit 568dc31e97
8 changed files with 602 additions and 199 deletions
+29
View File
@@ -44,6 +44,35 @@ async def list_records(
return result.scalars().all()
@router.get("/routes")
async def route_records(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Fastest activity per named route (course records)."""
from sqlalchemy import text
rows = await db.execute(
text("""
SELECT DISTINCT ON (nr.id)
nr.id AS route_id,
nr.name AS route_name,
nr.sport_type,
nr.distance_m,
a.id AS activity_id,
a.name AS activity_name,
a.duration_s,
a.start_time,
a.avg_speed_ms
FROM named_routes nr
JOIN activities a ON a.named_route_id = nr.id AND a.user_id = nr.user_id
WHERE nr.user_id = :uid AND a.duration_s IS NOT NULL
ORDER BY nr.id, a.duration_s ASC
"""),
{"uid": current_user.id},
)
return [dict(r._mapping) for r in rows]
@router.get("/history/{distance_label}")
async def record_history(
distance_label: str,