egentrening/src/routes/summary/ExerciseStatistics.svelte
Håkon Størdal 8981dc9615 New features
2025-09-04 21:38:07 +02:00

119 lines
3.5 KiB
Svelte

<script lang="ts">
import type { WorkoutDataSummary } from '$lib/workout/types';
import { exercises } from '$lib/workout';
import {
type TimePeriod,
getAveragePerDay,
getGoalProgress,
getProgressGradient
} from './summaryUtils';
import ExerciseGraph from './ExerciseGraph.svelte';
interface Props {
selectedPeriod: TimePeriod;
workoutSummary: WorkoutDataSummary;
}
let { selectedPeriod, workoutSummary }: Props = $props();
// Track when component is mounted for initial animation
let animated = $state(false);
// Track selected exercise for graph
let selectedExercise = $state<string | null>(null);
// Set mounted flag after component loads
setTimeout(() => {
animated = true;
}, 300);
</script>
<div class="overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm">
<div class="border-b border-gray-200 bg-gray-50 px-6 py-4">
<h3 class="text-lg font-medium text-gray-900">
Detailed Statistics for {selectedPeriod.label.toLowerCase()}
</h3>
</div>
<div class="divide-y divide-gray-200">
{#each exercises as { key, config }}
{@const totalValue = workoutSummary[key]}
{@const dailyAverage = getAveragePerDay(totalValue, workoutSummary)}
{@const goalProgress = getGoalProgress(totalValue, config.dailyGoal, workoutSummary)}
<div
class="cursor-pointer px-6 py-4 transition-colors duration-200 {selectedExercise === key
? 'bg-blue-50'
: 'hover:bg-gray-50'}"
role="button"
tabindex="0"
onclick={() => (selectedExercise = selectedExercise === key ? null : key)}
onkeydown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
selectedExercise = selectedExercise === key ? null : key;
}
}}
>
<div class="mb-2 flex items-center justify-between">
<div class="flex items-center">
<span class="mr-3 text-2xl">{config.icon}</span>
<h4 class="font-medium text-gray-900">{config.name}</h4>
</div>
<div class="text-right">
<div class="text-lg font-semibold {config.textColorClass}">
{config.formatter(totalValue)}
</div>
<div class="text-xs text-gray-500">Total</div>
</div>
</div>
<div class="flex items-center justify-between text-sm text-gray-600">
<div>
<span class="font-medium">Daily Average:</span>
{config.formatter(Number(dailyAverage.toFixed(2)))}
</div>
<div class="flex items-center">
<div class="mr-2 h-4 w-48 overflow-hidden rounded-full bg-gray-200">
<div
class="h-4 rounded-full bg-gradient-to-r {getProgressGradient(
config.color
)} transition-all duration-1000 ease-out"
style="width: {animated ? goalProgress : 0}%"
></div>
</div>
<span class="text-sm font-medium">{goalProgress.toFixed(0)}%</span>
</div>
</div>
<!-- Goal Information -->
<div class="mt-2 flex items-center justify-between text-xs text-gray-500">
<div>
Goal: {config.formatter(config.dailyGoal)}/day
</div>
{#if goalProgress >= 100}
<div class="font-medium text-green-600">✅ Goal achieved!</div>
{:else if goalProgress >= 80}
<div class="font-medium text-yellow-600">🎯 Close to goal</div>
{:else if totalValue > 0}
<div class="text-gray-400">Keep going!</div>
{/if}
</div>
{#if selectedExercise === key}
<div class="mt-4 rounded-lg border bg-gray-50 p-4">
<ExerciseGraph
exerciseKey={key}
{selectedPeriod}
dailyGoal={config.dailyGoal}
color={config.color}
/>
</div>
{/if}
</div>
{/each}
</div>
</div>