import { useState, Fragment } from 'react' import { useQuery } from '@tanstack/react-query' import { Link, useNavigate } from 'react-router-dom' import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts' import { format } from 'date-fns' import api from '../utils/api' import { formatDuration, formatDate, formatPace, formatDistance } from '../utils/format' import { useUnit } from '../hooks/useUnits' import RouteMiniMap from '../components/ui/RouteMiniMap' const SPORTS = ['running', 'cycling'] const DISTANCE_ORDER = [ '400m', '800m', '1k', '1 mile', '3k', '5k', '10k', 'Half marathon', 'Marathon', '50k', '100k', ] const TABS = ['Distance PRs', 'Route Records', 'Segments'] const MEDALS = { 1: '🥇', 2: '🥈', 3: '🥉' } function DistancePRs() { const [sport, setSport] = useState('running') const [selectedDistance, setSelectedDistance] = useState(null) const { data: records } = useQuery({ queryKey: ['records', sport], queryFn: () => api.get('/records/', { params: { sport_type: sport } }).then(r => r.data), }) const { data: history } = useQuery({ queryKey: ['record-history', selectedDistance, sport], queryFn: () => api.get(`/records/history/${encodeURIComponent(selectedDistance)}`, { params: { sport_type: sport }, }).then(r => r.data), enabled: !!selectedDistance, }) const sortedRecords = records?.slice().sort((a, b) => { const ai = DISTANCE_ORDER.indexOf(a.distance_label) const bi = DISTANCE_ORDER.indexOf(b.distance_label) return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi) }) return (
{SPORTS.map(s => ( ))}
{sortedRecords?.length === 0 && (

🏆

No records yet — import activities to track your best times

)}
{sortedRecords?.map(rec => ( setSelectedDistance(rec.distance_label)} className={`border-b border-gray-800/50 cursor-pointer transition-colors ${ selectedDistance === rec.distance_label ? 'bg-blue-900/20' : 'hover:bg-gray-800/40' }`} > ))}
Distance Best time Date
{rec.distance_label} {formatDuration(rec.duration_s)} {formatDate(rec.achieved_at)} e.stopPropagation()} className="text-xs text-blue-400 hover:underline" > View →
{selectedDistance && history ? ( <>

{selectedDistance} progression

Lower is faster

{history.length > 1 ? ( ({ date: h.achieved_at, time: h.duration_s }))} margin={{ top: 4, right: 4, bottom: 4, left: 8 }} > format(new Date(d), 'MMM yy')} /> format(new Date(d), 'MMM d, yyyy')} formatter={v => [formatDuration(v), 'Time']} /> ) : (
Only one record — complete more activities to see progression
)} ) : (
Select a distance to see your progression
)}
) } function RouteRecords() { const navigate = useNavigate() const unit = useUnit() const { data: records, isLoading } = useQuery({ queryKey: ['route-records'], queryFn: () => api.get('/records/routes').then(r => r.data), }) if (isLoading) return

Loading…

if (!records?.length) return (

🗺️

No route records yet — create named routes and complete activities on them

) return (
{records.map(rec => ( navigate(`/activities/${rec.activity_id}`)} className="border-b border-gray-800/50 hover:bg-gray-800/40 transition-colors cursor-pointer" > ))}
Route Distance Best time Pace Date
{rec.sport_type} {rec.route_name} {formatDistance(rec.distance_m, unit)} {formatDuration(rec.duration_s)} {formatPace(rec.avg_speed_ms, rec.sport_type, unit)} {formatDate(rec.start_time)}
) } function SegmentLeaderboard({ segmentId }) { const { data } = useQuery({ queryKey: ['segment', segmentId], queryFn: () => api.get(`/segments/${segmentId}`).then(r => r.data), }) if (!data) return

Loading…

if (!data.leaderboard?.length) return

No efforts yet — still matching.

return (
{data.leaderboard.map((e, i) => (
{MEDALS[e.rank] || i + 1} {formatDuration(e.duration_s)} {e.activity_name} {e.date && {formatDate(e.date)}}
))}
) } function SegmentRecords() { const [open, setOpen] = useState(null) const unit = useUnit() const { data: segments, isLoading } = useQuery({ queryKey: ['segments'], queryFn: () => api.get('/segments/').then(r => r.data), }) if (isLoading) return

Loading…

if (!segments?.length) return (

🏅

No segments yet — create one from an activity's detail page

) return (
{segments.map(seg => ( setOpen(open === seg.id ? null : seg.id)} className={`border-b border-gray-800/50 cursor-pointer transition-colors ${ open === seg.id ? 'bg-blue-900/20' : 'hover:bg-gray-800/40' }`} > {open === seg.id && ( )} ))}
Segment Distance Best time Efforts
{seg.sport_type && {seg.sport_type}} {seg.name} {formatDistance(seg.distance_m, unit)} {seg.best_s != null ? formatDuration(seg.best_s) : '--'} {seg.effort_count}
) } export default function RecordsPage() { const [tab, setTab] = useState('Distance PRs') return (

Records

{TABS.map(t => ( ))}
{tab === 'Distance PRs' && } {tab === 'Route Records' && } {tab === 'Segments' && }
) }