Segments Phase 4: detail page, activity efforts, athlete tab, new APIs

New API endpoints:
- GET /api/segments/{id} — single segment metadata
- GET /api/activities/{id}/segment_efforts — efforts for an activity (auth)
- GET /api/users/{handle}/segment_summary — public best time + count per segment

New components:
- SegmentDetail.svelte — map + metadata + effort table (with PR/Δ) + rescan button
- SegmentsPage.svelte — URL router: shows detail when /segments/{id}/, list otherwise

Updated:
- segments/index.astro — now uses SegmentsPage router
- nginx-activity.conf — add /segments/ try_files rule for client-side routing
- ActivityDetail.svelte — segment efforts block below laps
- AthleteView.svelte — Segments tab with best time + effort count per segment
- format.ts — add formatElapsed() for compact m:ss display
This commit is contained in:
Davide Scaini
2026-05-13 08:09:24 +02:00
parent c7f0013e57
commit f2075e29d2
8 changed files with 516 additions and 12 deletions
+10
View File
@@ -26,6 +26,16 @@ export function formatDuration(s: number | null): string {
return `${m}m ${sec.toString().padStart(2, '0')}s`;
}
/** Compact m:ss or h:mm:ss for segment effort tables. */
export function formatElapsed(s: number): string {
s = Math.floor(s);
const h = Math.floor(s / 3600);
const m = Math.floor((s % 3600) / 60);
const sec = s % 60;
if (h > 0) return `${h}:${m.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
return `${m}:${sec.toString().padStart(2, '0')}`;
}
export function formatSpeed(kmh: number | null): string {
if (kmh == null) return '—';
return `${kmh.toFixed(1)} km/h`;