Add medals, HRV status dots, smooth segment hover, side-by-side map/timeline, HR zone times
- 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>
This commit is contained in:
@@ -14,16 +14,7 @@ import {
|
||||
formatHeartRate, formatDateTime, formatCadence, sportIcon,
|
||||
} from '../utils/format'
|
||||
|
||||
// Find the cumulative distance along the track nearest a clicked lat/lng.
|
||||
function nearestDistance(points, lat, lng) {
|
||||
let best = null, bestD = Infinity
|
||||
for (const p of points) {
|
||||
if (p.latitude == null || p.longitude == null || p.distance_m == null) continue
|
||||
const d = (p.latitude - lat) ** 2 + (p.longitude - lng) ** 2
|
||||
if (d < bestD) { bestD = d; best = p.distance_m }
|
||||
}
|
||||
return best
|
||||
}
|
||||
import { projectToTrack } from '../utils/track'
|
||||
|
||||
const METRICS = [
|
||||
{ key: 'heart_rate', label: 'Heart Rate', unit: 'bpm', color: '#f43f5e' },
|
||||
@@ -83,8 +74,9 @@ export default function ActivityDetailPage() {
|
||||
|
||||
const handleMapClick = ({ lat, lng }) => {
|
||||
if (!segCreate || !dataPoints) return
|
||||
const dist = nearestDistance(dataPoints, lat, lng)
|
||||
if (dist == null) return
|
||||
const proj = projectToTrack(dataPoints, lat, lng)
|
||||
if (proj?.distance_m == null) return
|
||||
const dist = proj.distance_m
|
||||
setSegPoints(prev => (prev.length >= 2 ? [{ distance_m: dist }] : [...prev, { distance_m: dist }]))
|
||||
}
|
||||
|
||||
@@ -162,10 +154,12 @@ export default function ActivityDetailPage() {
|
||||
{activity.hr_zones && Object.values(activity.hr_zones).some(v => v > 0) && (
|
||||
<div className="bg-gray-900 rounded-xl border border-gray-800 p-4">
|
||||
<h3 className="text-sm font-medium text-gray-300 mb-3">Heart Rate Zones</h3>
|
||||
<HRZoneBar zones={activity.hr_zones} />
|
||||
<HRZoneBar zones={activity.hr_zones} totalSeconds={activity.moving_time_s ?? activity.duration_s} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Map and activity timeline side by side, each ~half width on large screens */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 items-start">
|
||||
{/* Map with controls — only when the activity has a GPS track */}
|
||||
{activity.polyline && activity.distance_m > 0 ? (
|
||||
<div className="bg-gray-900 rounded-xl overflow-hidden border border-gray-800">
|
||||
@@ -309,6 +303,7 @@ export default function ActivityDetailPage() {
|
||||
<p className="text-gray-600 text-sm text-center py-8">No timeline data available for this activity</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Laps · Routes · Segments — on one row, each shrinking to fit and
|
||||
expanding to fill the width when fewer are present. */}
|
||||
|
||||
Reference in New Issue
Block a user