119 lines
4.0 KiB
TypeScript
119 lines
4.0 KiB
TypeScript
import { startOfWeek } from "date-fns";
|
|
import { DashboardAnalysisCard } from "@/components/dashboard-analysis-card";
|
|
import { RunningStatsChart } from "@/components/running-stats-chart";
|
|
import { StatCard } from "@/components/stat-card";
|
|
import { VolumeTrendChart } from "@/components/volume-trend-chart";
|
|
import { formatDateShort } from "@/lib/format";
|
|
import { listAnalysesForCalendar, serializeAnalysis } from "@/lib/models/analysis";
|
|
import { listRunningActivities } from "@/lib/models/running";
|
|
import { listStrengthWorkouts } from "@/lib/models/strength";
|
|
import { workoutVolumeKg } from "@/lib/strength/stats";
|
|
import { getCurrentUserId } from "@/lib/session";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
const TREND_LIMIT = 20;
|
|
|
|
export default async function Home() {
|
|
const userId = await getCurrentUserId();
|
|
|
|
const [runs, strengthWorkouts, allAnalyses] = await Promise.all([
|
|
listRunningActivities(userId),
|
|
listStrengthWorkouts(userId),
|
|
listAnalysesForCalendar(userId),
|
|
]);
|
|
|
|
const weekStart = startOfWeek(new Date(), { weekStartsOn: 1 });
|
|
const weeklyKm =
|
|
runs.filter((run) => run.startTime >= weekStart).reduce((sum, run) => sum + run.distanceM, 0) /
|
|
1000;
|
|
const weeklyStrengthSessions = strengthWorkouts.filter(
|
|
(workout) => workout.date >= weekStart
|
|
).length;
|
|
|
|
const totalRuns = runs.length;
|
|
const totalDistanceKm = runs.reduce((sum, r) => sum + r.distanceM, 0) / 1000;
|
|
const totalStrengthWorkouts = strengthWorkouts.length;
|
|
|
|
const volumeTrendData = strengthWorkouts
|
|
.slice(0, TREND_LIMIT)
|
|
.map((w) => ({
|
|
label: formatDateShort(w.date),
|
|
date: w.date.toISOString().slice(0, 10),
|
|
name: w.name,
|
|
volumeKg: workoutVolumeKg(w),
|
|
}))
|
|
.reverse();
|
|
|
|
// Aggregate running distance per ISO week (Mon-based).
|
|
const weeklyDistanceM = new Map<number, number>();
|
|
for (const r of runs) {
|
|
const weekStartTs = startOfWeek(r.startTime, { weekStartsOn: 1 }).getTime();
|
|
weeklyDistanceM.set(weekStartTs, (weeklyDistanceM.get(weekStartTs) ?? 0) + r.distanceM);
|
|
}
|
|
const runTrendData = [...weeklyDistanceM.entries()]
|
|
.sort((a, b) => a[0] - b[0])
|
|
.slice(-TREND_LIMIT)
|
|
.map(([ts, meters]) => ({
|
|
label: formatDateShort(new Date(ts)),
|
|
distanceKm: Math.round((meters / 1000) * 100) / 100,
|
|
}));
|
|
|
|
const serializedAnalyses = allAnalyses.map(serializeAnalysis);
|
|
|
|
const dayRuns = runs.map((r) => ({
|
|
id: r._id.toString(),
|
|
name: r.name,
|
|
startTime: r.startTime.toISOString(),
|
|
distanceM: r.distanceM,
|
|
durationSec: r.durationSec,
|
|
avgPaceSecPerKm: r.avgPaceSecPerKm,
|
|
}));
|
|
|
|
const dayWorkouts = strengthWorkouts.map((w) => ({
|
|
id: w._id.toString(),
|
|
name: w.name,
|
|
date: w.date.toISOString(),
|
|
exerciseCount: w.exercises.length,
|
|
}));
|
|
|
|
return (
|
|
<div className="flex flex-col gap-8">
|
|
<section className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
|
<StatCard label="Km w tym tygodniu" value={`${weeklyKm.toFixed(1)} km`} hint="Bieganie" />
|
|
<StatCard
|
|
label="Siłownia w tym tygodniu"
|
|
value={weeklyStrengthSessions}
|
|
hint="Treningi"
|
|
/>
|
|
<StatCard
|
|
label="Łączny dystans"
|
|
value={`${totalDistanceKm.toFixed(0)} km`}
|
|
hint={`${totalRuns} biegów`}
|
|
/>
|
|
<StatCard
|
|
label="Treningi siłowe"
|
|
value={totalStrengthWorkouts}
|
|
hint="Łącznie"
|
|
/>
|
|
</section>
|
|
|
|
{(volumeTrendData.length > 1 || runTrendData.length > 1) && (
|
|
<section className="flex flex-col gap-4">
|
|
<h2 className="text-lg font-semibold text-fg">Globalne statystyki</h2>
|
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
|
{volumeTrendData.length > 1 && <VolumeTrendChart data={volumeTrendData} />}
|
|
{runTrendData.length > 1 && <RunningStatsChart data={runTrendData} />}
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
<DashboardAnalysisCard
|
|
analyses={serializedAnalyses}
|
|
runs={dayRuns}
|
|
workouts={dayWorkouts}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|