From ee178feff024f1bd1b6a415459a1bfb6642301c5 Mon Sep 17 00:00:00 2001 From: Dominik Klarkowski Date: Tue, 16 Jun 2026 11:51:10 +0200 Subject: [PATCH] init --- app/page.tsx | 4 ++-- app/running/[id]/page.tsx | 4 ++-- app/strength/[id]/page.tsx | 4 ++-- components/ai-analysis-card.tsx | 4 ++-- components/dashboard-analysis-card.tsx | 4 ++-- components/exercise-progress-chart.tsx | 15 +++++++++++++-- components/volume-chart.tsx | 13 +++++++++++++ lib/format.ts | 8 ++++---- lib/models/analysis.ts | 22 ++++++++++++++++++++++ 9 files changed, 62 insertions(+), 16 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 51881f6..0f4642e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -5,7 +5,7 @@ import { DashboardAnalysisCard } from "@/components/dashboard-analysis-card"; import { EmptyState } from "@/components/empty-state"; import { StatCard } from "@/components/stat-card"; import { formatDateShort, formatDistance, formatDuration, formatPace } from "@/lib/format"; -import { getDashboardAnalysis } from "@/lib/models/analysis"; +import { getDashboardAnalysis, serializeAnalysis } from "@/lib/models/analysis"; import { listRunningActivities } from "@/lib/models/running"; import { listStrengthWorkouts } from "@/lib/models/strength"; @@ -84,7 +84,7 @@ export default async function Home() { - + ); } diff --git a/app/running/[id]/page.tsx b/app/running/[id]/page.tsx index 43d9396..016c4f4 100644 --- a/app/running/[id]/page.tsx +++ b/app/running/[id]/page.tsx @@ -5,7 +5,7 @@ import { RouteMapSection } from "@/components/route-map-section"; import { StatCard } from "@/components/stat-card"; import { formatDate, formatDistance, formatDuration, formatPace } from "@/lib/format"; import { fetchActivityRoutePoints, getAuthorizedClient } from "@/lib/garmin/client"; -import { getLatestAnalysisForTarget } from "@/lib/models/analysis"; +import { getLatestAnalysisForTarget, serializeAnalysis } from "@/lib/models/analysis"; import { getRunningActivity, setRunningActivityRoutePoints, type RunningActivity } from "@/lib/models/running"; export const dynamic = "force-dynamic"; @@ -117,7 +117,7 @@ export default async function RunningActivityPage({ ) : null} - + ); } diff --git a/app/strength/[id]/page.tsx b/app/strength/[id]/page.tsx index 7152615..5ed5124 100644 --- a/app/strength/[id]/page.tsx +++ b/app/strength/[id]/page.tsx @@ -3,7 +3,7 @@ import { AiAnalysisCard } from "@/components/ai-analysis-card"; import { ExerciseProgressChart } from "@/components/exercise-progress-chart"; import { InfoTooltip } from "@/components/info-tooltip"; import { formatDate, formatDateShort } from "@/lib/format"; -import { getLatestAnalysisForTarget } from "@/lib/models/analysis"; +import { getLatestAnalysisForTarget, serializeAnalysis } from "@/lib/models/analysis"; import { getStrengthWorkout, listStrengthWorkouts } from "@/lib/models/strength"; import { exerciseE1rm, getExerciseHistory } from "@/lib/strength/stats"; @@ -40,7 +40,7 @@ export default async function StrengthWorkoutPage({ {workout.notes ?

{workout.notes}

: null} - +
{exercisesWithHistory.map(({ exercise }, index) => { diff --git a/components/ai-analysis-card.tsx b/components/ai-analysis-card.tsx index 6aca758..123c085 100644 --- a/components/ai-analysis-card.tsx +++ b/components/ai-analysis-card.tsx @@ -4,12 +4,12 @@ import { useActionState } from "react"; import { Sparkles } from "lucide-react"; import { generateAnalysisAction } from "@/app/ai/actions"; import { formatDate } from "@/lib/format"; -import type { AiAnalysis, AiAnalysisTargetType } from "@/lib/models/analysis"; +import type { AiAnalysisTargetType, SerializedAiAnalysis } from "@/lib/models/analysis"; type AiAnalysisCardProps = { targetType: AiAnalysisTargetType; targetId: string; - analysis: AiAnalysis | null; + analysis: SerializedAiAnalysis | null; }; export function AiAnalysisCard({ targetType, targetId, analysis }: AiAnalysisCardProps) { diff --git a/components/dashboard-analysis-card.tsx b/components/dashboard-analysis-card.tsx index 7ae9f1a..f1e1037 100644 --- a/components/dashboard-analysis-card.tsx +++ b/components/dashboard-analysis-card.tsx @@ -4,10 +4,10 @@ import { useActionState } from "react"; import { Sparkles } from "lucide-react"; import { generateDashboardAnalysisAction } from "@/app/ai/actions"; import { formatDate } from "@/lib/format"; -import type { AiAnalysis } from "@/lib/models/analysis"; +import type { SerializedAiAnalysis } from "@/lib/models/analysis"; type Props = { - analysis: AiAnalysis | null; + analysis: SerializedAiAnalysis | null; }; export function DashboardAnalysisCard({ analysis }: Props) { diff --git a/components/exercise-progress-chart.tsx b/components/exercise-progress-chart.tsx index 6becc27..890b365 100644 --- a/components/exercise-progress-chart.tsx +++ b/components/exercise-progress-chart.tsx @@ -1,5 +1,6 @@ "use client"; +import { useEffect, useState } from "react"; import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; type ExerciseProgressChartProps = { @@ -26,8 +27,18 @@ function E1rmDelta({ data }: { data: ExerciseProgressChartProps["data"] }) { } export function ExerciseProgressChart({ name, data }: ExerciseProgressChartProps) { - if (data.length < 2) { - return null; + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + + if (data.length < 2) return null; + + if (!mounted) { + return ( +
+
+
+
+ ); } return ( diff --git a/components/volume-chart.tsx b/components/volume-chart.tsx index b8631fa..95aa4b4 100644 --- a/components/volume-chart.tsx +++ b/components/volume-chart.tsx @@ -1,5 +1,6 @@ "use client"; +import { useEffect, useState } from "react"; import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; type VolumeChartProps = { @@ -7,6 +8,18 @@ type VolumeChartProps = { }; export function VolumeChart({ data }: VolumeChartProps) { + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + + if (!mounted) { + return ( +
+
+
+
+ ); + } + return (
Wolumen treningowy (ciężar × powtórzenia)
diff --git a/lib/format.ts b/lib/format.ts index 02636ba..0a86317 100644 --- a/lib/format.ts +++ b/lib/format.ts @@ -1,12 +1,12 @@ import { format } from "date-fns"; import { pl } from "date-fns/locale"; -export function formatDate(date: Date): string { - return format(date, "d MMMM yyyy, HH:mm", { locale: pl }); +export function formatDate(date: Date | string): string { + return format(new Date(date), "d MMMM yyyy, HH:mm", { locale: pl }); } -export function formatDateShort(date: Date): string { - return format(date, "d MMM yyyy", { locale: pl }); +export function formatDateShort(date: Date | string): string { + return format(new Date(date), "d MMM yyyy", { locale: pl }); } export function formatDuration(seconds: number): string { diff --git a/lib/models/analysis.ts b/lib/models/analysis.ts index d12e23f..6b1eace 100644 --- a/lib/models/analysis.ts +++ b/lib/models/analysis.ts @@ -16,6 +16,28 @@ export type AiAnalysis = AiAnalysisInput & { createdAt: Date; }; +export type SerializedAiAnalysis = { + _id: string; + targetType: AiAnalysisTargetType; + targetId: string; + summary: string; + tips: string[]; + model: string; + createdAt: string; +}; + +export function serializeAnalysis(analysis: AiAnalysis): SerializedAiAnalysis { + return { + _id: analysis._id.toString(), + targetType: analysis.targetType, + targetId: analysis.targetId.toString(), + summary: analysis.summary, + tips: analysis.tips, + model: analysis.model, + createdAt: analysis.createdAt.toISOString(), + }; +} + const COLLECTION = "ai_analyses"; async function getCollection() {