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

This commit is contained in:
2026-06-16 00:13:16 +01:00
parent e7123ee5db
commit d01f66223b
10 changed files with 98 additions and 45 deletions
+5 -8
View File
@@ -11,6 +11,7 @@ import { startOfWeek, format, subWeeks, eachWeekOfInterval, subDays, addDays } f
import api from '../utils/api'
import { useIsMobile } from '../hooks/useMediaQuery'
import StatCard from '../components/ui/StatCard'
import HrvBadge from '../components/ui/HrvBadge'
import ActivityMap from '../components/activity/ActivityMap'
import {
formatDuration, formatDistance, formatHeartRate, formatElevation,
@@ -23,20 +24,13 @@ const Grid = WidthProvider(GridLayout)
const MEDALS = { 1: '🥇', 2: '🥈', 3: '🥉' }
const tooltipStyle = { background: '#111827', border: '1px solid #374151', borderRadius: 8, fontSize: 12, color: '#fff' }
const HRV_PALETTE = {
balanced: 'text-green-400 bg-green-400/10 border-green-400/30',
unbalanced: 'text-orange-400 bg-orange-400/10 border-orange-400/30',
low: 'text-red-400 bg-red-400/10 border-red-400/30',
poor: 'text-red-400 bg-red-400/10 border-red-400/30',
}
// Compact single-stat widgets. val(health, ytdStats) → display string.
const STAT_DEFS = {
stat_steps: { label: 'Steps today', accent: 'green', sub: 'goal 10,000', val: h => h.steps != null ? h.steps.toLocaleString() : '--' },
stat_resting_hr: { label: 'Resting HR', accent: 'red', val: h => formatHeartRate(h.resting_hr) },
stat_sleep: { label: 'Sleep', accent: 'default', val: h => formatSleep(h.sleep_duration_s) },
stat_vo2max: { label: 'VO₂ max', accent: 'blue', val: h => h.vo2max != null ? h.vo2max.toFixed(1) : '--', sub: h => h.fitness_age != null ? `fitness age ${h.fitness_age}` : undefined },
stat_hrv: { label: 'HRV status', accent: 'purple', val: h => h.hrv_nightly_avg != null ? `${Math.round(h.hrv_nightly_avg)} ms` : '--', sub: h => h.hrv_status || undefined },
stat_hrv: { label: 'HRV status', accent: 'purple', val: h => h.hrv_nightly_avg != null ? `${Math.round(h.hrv_nightly_avg)} ms` : '--', sub: h => h.hrv_status ? <HrvBadge status={h.hrv_status} /> : undefined },
stat_running: { label: 'Running this year', accent: 'blue', val: (h, y) => y ? `${y.running_km.toFixed(0)} km` : '--' },
stat_cycling: { label: 'Cycling this year', accent: 'orange', val: (h, y) => y ? `${y.cycling_km.toFixed(0)} km` : '--' },
stat_stress: { label: 'Stress', accent: 'purple', val: h => h.avg_stress != null ? Math.round(h.avg_stress) : '--' },
@@ -410,6 +404,9 @@ function RecentActivities({ activities }) {
<span className="text-lg">{sportIcon(activity.sport_type)}</span>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-white truncate">{activity.name}</p>
{activity.named_route_name && (
<p className="text-xs text-blue-400 truncate">📍 {activity.named_route_name}</p>
)}
<p className="text-xs text-gray-500">{formatDate(activity.start_time)}</p>
</div>
<div className="text-right text-sm">