Add segments, YTD stats, route matching fixes, body battery layout, pace fix
- Segments page: new /segments route with auto-generate (1km splits, turn detection, hill detection), manual segment creation, per-segment performance times across matched activities; fixed auth on existing segment endpoints - YTD distance: new /activities/stats/ytd endpoint; Dashboard replaces 'Total distance' with 'Running this year' + 'Cycling this year'; Activities page shows YTD stats row - Weekly chart click: clicking a Dashboard bar navigates to Activities filtered to that week; Activities reads from/to query params with dismissable chip - Route matching: add ±2.5% distance gate + 3% relative DTW threshold (was flat 80m); tighten candidate pre-filter from 80/120% to 95/105% - Body battery layout: HR chart and body battery now side-by-side at same height on large screens instead of stacked full-width - Pace display fix: MetricTimeline clamps GPS speed outliers before computing Y-axis domain; tick formatter guards against v<=0 or v>25 m/s Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -103,7 +103,7 @@ function BodyBatteryChart({ bb }) {
|
||||
}))
|
||||
|
||||
return (
|
||||
<div className="bg-gray-900 rounded-xl border border-gray-800 p-5 space-y-4">
|
||||
<div className="bg-gray-900 rounded-xl border border-gray-800 p-5 space-y-4 h-full">
|
||||
<h3 className="text-sm font-medium text-gray-300">Body Battery</h3>
|
||||
|
||||
<div className="flex items-center gap-8">
|
||||
@@ -340,22 +340,26 @@ function DailySnapshot({ day, avg30, intradayHr, bodyBattery, onOlder, onNewer,
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 24-hour heart rate chart */}
|
||||
{intradayHr?.length > 0 && (
|
||||
<div className="bg-gray-900 rounded-xl border border-gray-800 p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h3 className="text-sm font-medium text-gray-300">24-hour Heart Rate</h3>
|
||||
{day.avg_hr_day && (
|
||||
<span className="text-xs text-gray-500">avg {Math.round(day.avg_hr_day)} bpm</span>
|
||||
)}
|
||||
</div>
|
||||
<IntradayHrChart values={intradayHr} />
|
||||
{/* 24-hour heart rate chart + body battery (side by side) */}
|
||||
{(intradayHr?.length > 0 || bodyBattery) && (
|
||||
<div className={`grid gap-4 ${intradayHr?.length > 0 && bodyBattery ? 'grid-cols-1 lg:grid-cols-2' : 'grid-cols-1'}`}>
|
||||
{intradayHr?.length > 0 && (
|
||||
<div className="bg-gray-900 rounded-xl border border-gray-800 p-4 flex flex-col">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h3 className="text-sm font-medium text-gray-300">24-hour Heart Rate</h3>
|
||||
{day.avg_hr_day && (
|
||||
<span className="text-xs text-gray-500">avg {Math.round(day.avg_hr_day)} bpm</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 min-h-0">
|
||||
<IntradayHrChart values={intradayHr} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<BodyBatteryChart bb={bodyBattery} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Body battery */}
|
||||
<BodyBatteryChart bb={bodyBattery} />
|
||||
|
||||
{/* Activity strip */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||
|
||||
|
||||
Reference in New Issue
Block a user