From 63cb8b4933438457cf538a735a5fd0891e196b73 Mon Sep 17 00:00:00 2001 From: Dominik Klarkowski Date: Thu, 18 Jun 2026 11:24:56 +0200 Subject: [PATCH] init --- app/running/[id]/page.tsx | 115 ++++----------------------------- app/running/actions.ts | 36 +++++++++++ components/elevation-chart.tsx | 2 +- 3 files changed, 48 insertions(+), 105 deletions(-) diff --git a/app/running/[id]/page.tsx b/app/running/[id]/page.tsx index 7aa7582..5cab6d8 100644 --- a/app/running/[id]/page.tsx +++ b/app/running/[id]/page.tsx @@ -7,16 +7,9 @@ import { GcbChart } from "@/components/gcb-chart"; import { RunMetricChart } from "@/components/run-metric-chart"; import { StatCard } from "@/components/stat-card"; import { formatDate, formatDistance, formatDuration, formatPace } from "@/lib/format"; -import { - fetchActivityRoutePoints, - fetchActivityRunMetrics, - getAuthorizedClient, -} from "@/lib/garmin/client"; import { getLatestAnalysisForTarget, serializeAnalysis } from "@/lib/models/analysis"; import { getRunningActivity, - setRunningActivityMetrics, - setRunningActivityRoutePoints, type RunMetrics, type RunningActivity, } from "@/lib/models/running"; @@ -24,83 +17,34 @@ import { getCurrentUserId } from "@/lib/session"; export const dynamic = "force-dynamic"; -function mayHaveRoute(activity: RunningActivity): boolean { - return Boolean(activity.hasRoute) || Boolean(activity.routePoints?.length); -} - -async function RouteMapFetcher({ - activity, - userId, -}: { - activity: RunningActivity; - userId: string; -}) { - let routePoints = activity.routePoints; - - if ((!routePoints || !activity.elevationProfile) && mayHaveRoute(activity)) { - try { - const client = await getAuthorizedClient(userId); - const result = await fetchActivityRoutePoints(client, activity.garminActivityId); - if (result) { - await setRunningActivityRoutePoints(userId, activity.garminActivityId, result.points, result.elevationProfile); - routePoints = result.points; - } - } catch { - // GPS fetch failed silently - } - } - +function RouteMap({ activity }: { activity: RunningActivity }) { + const routePoints = activity.routePoints; return (
{routePoints && routePoints.length > 0 ? ( ) : (
- Brak danych GPS + Brak danych GPS — zsynchronizuj ponownie
)}
); } -function hasValidElevation(profile: number[] | undefined): boolean { - return Array.isArray(profile) && profile.some((v) => v > 0); -} - -async function ElevationFetcher({ - activity, - userId, -}: { - activity: RunningActivity; - userId: string; -}) { - let elevationProfile = activity.elevationProfile; - - if (!hasValidElevation(elevationProfile) && mayHaveRoute(activity)) { - try { - const client = await getAuthorizedClient(userId); - const result = await fetchActivityRoutePoints(client, activity.garminActivityId); - if (result) { - await setRunningActivityRoutePoints(userId, activity.garminActivityId, result.points, result.elevationProfile); - elevationProfile = result.elevationProfile; - } - } catch { - // silent - } - } - +function ElevationSection({ activity }: { activity: RunningActivity }) { + const elevationProfile = activity.elevationProfile; if (!elevationProfile || elevationProfile.length < 2) return null; const elevData = elevationProfile .map((altM, i) => ({ - distanceKm: Math.round((i / elevationProfile!.length) * activity.distanceM / 10) / 100, + distanceKm: Math.round((i / elevationProfile.length) * activity.distanceM / 10) / 100, altM, })) .filter((p) => p.altM > 0); if (elevData.length < 2) return null; - // Merge pace by fractional position (both arrays span the same run, different sample counts) const paceSrc = activity.runMetrics?.paceSec; const data = elevData.map((ep, i) => { if (!paceSrc || paceSrc.length === 0) return ep; @@ -122,33 +66,8 @@ function toChartData( .filter((p) => p.value > 0); } -async function RunMetricsFetcher({ - activity, - userId, -}: { - activity: RunningActivity; - userId: string; -}) { - let metrics: RunMetrics | undefined = activity.runMetrics; - - const missingCadence = activity.avgCadence && !metrics?.cadenceSpm; - const missingGcb = activity.avgGroundContactBalanceLeftPct && !metrics?.gcbLeftPct; - // Re-fetch if paceSec missing or was computed from integer-rounded speeds (< 15 unique values = rounding artifact) - const validPace = metrics?.paceSec?.filter((v) => v > 0) ?? []; - const missingPace = validPace.length === 0 || new Set(validPace).size < 15; - - if ((!metrics || missingCadence || missingGcb || missingPace) && mayHaveRoute(activity)) { - try { - const client = await getAuthorizedClient(userId); - const fetched = await fetchActivityRunMetrics(client, activity.garminActivityId); - if (fetched) { - await setRunningActivityMetrics(userId, activity.garminActivityId, fetched); - metrics = fetched; - } - } catch { - // silent - } - } +function RunMetricsSection({ activity }: { activity: RunningActivity }) { + const metrics: RunMetrics | undefined = activity.runMetrics; if (!metrics || metrics.distanceKm.length === 0) return null; @@ -186,11 +105,6 @@ async function RunMetricsFetcher({ ); } -function MapSkeleton() { - return ( -
- ); -} export default async function RunningActivityPage({ params, @@ -215,9 +129,7 @@ export default async function RunningActivityPage({
- }> - - + @@ -261,13 +173,8 @@ export default async function RunningActivityPage({ ) : null}
- - - - - - - + + { const since = await getLastSyncAt(userId); const activities = await fetchRunningActivities(client); @@ -39,6 +45,36 @@ async function syncWithClient(userId: string, client: GarminConnect): Promise a.hasRoute && (!a.routePoints?.length || !a.runMetrics?.paceSec)) + .slice(0, ENRICH_PER_SYNC); + + for (const activity of needsEnrich) { + try { + if (!activity.routePoints?.length) { + const route = await fetchActivityRoutePoints(client, activity.garminActivityId); + if (route) { + await setRunningActivityRoutePoints( + userId, + activity.garminActivityId, + route.points, + route.elevationProfile + ); + } + } + if (!activity.runMetrics?.paceSec) { + const metrics = await fetchActivityRunMetrics(client, activity.garminActivityId); + if (metrics) { + await setRunningActivityMetrics(userId, activity.garminActivityId, metrics); + } + } + } catch { + // Rate limited or activity has no GPS — skip silently + } + } + revalidatePath("/running"); revalidatePath("/settings"); revalidatePath("/"); diff --git a/components/elevation-chart.tsx b/components/elevation-chart.tsx index 42e24d3..1a1684a 100644 --- a/components/elevation-chart.tsx +++ b/components/elevation-chart.tsx @@ -53,7 +53,7 @@ export function ElevationChart({ data }: Props) { }; return ( -
+
Profil wysokości {hasPace && (