From 8981dc9615d36370540baddbf3a395dfdb3ac0a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20St=C3=B8rdal?= <30749741+hakon55@users.noreply.github.com> Date: Thu, 4 Sep 2025 21:38:07 +0200 Subject: [PATCH] New features --- package-lock.json | 12 +- package.json | 3 +- src/lib/db.ts | 83 ++++++++ src/lib/workout/exercises.ts | 36 +++- src/lib/workout/types.ts | 23 +- src/routes/+layout.svelte | 7 +- src/routes/ExerciseField.svelte | 36 +--- src/routes/WorkoutDisplay.svelte | 78 +++---- src/routes/WorkoutLogger.svelte | 8 +- src/routes/summary/+page.svelte | 94 +++++++++ src/routes/summary/ExerciseGraph.svelte | 209 +++++++++++++++++++ src/routes/summary/ExerciseStatistics.svelte | 119 +++++++++++ src/routes/summary/OverviewCards.svelte | 57 +++++ src/routes/summary/TimePeriodSelector.svelte | 36 ++++ src/routes/summary/summary.remote.ts | 40 ++++ src/routes/summary/summaryUtils.ts | 125 +++++++++++ src/routes/workout.remote.ts | 13 +- 17 files changed, 880 insertions(+), 99 deletions(-) create mode 100644 src/routes/summary/+page.svelte create mode 100644 src/routes/summary/ExerciseGraph.svelte create mode 100644 src/routes/summary/ExerciseStatistics.svelte create mode 100644 src/routes/summary/OverviewCards.svelte create mode 100644 src/routes/summary/TimePeriodSelector.svelte create mode 100644 src/routes/summary/summary.remote.ts create mode 100644 src/routes/summary/summaryUtils.ts diff --git a/package-lock.json b/package-lock.json index 859fa1e..3af5358 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.1", "dependencies": { "@types/pg": "^8.15.5", - "pg": "^8.16.3" + "pg": "^8.16.3", + "zod": "^4.1.5" }, "devDependencies": { "@sveltejs/adapter-node": "^5.3.1", @@ -2732,6 +2733,15 @@ "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", "dev": true, "license": "MIT" + }, + "node_modules/zod": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.5.tgz", + "integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 7e3f16b..27f3c55 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "@types/pg": "^8.15.5", - "pg": "^8.16.3" + "pg": "^8.16.3", + "zod": "^4.1.5" } } diff --git a/src/lib/db.ts b/src/lib/db.ts index fc538d7..cb17a93 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -95,6 +95,89 @@ export const db = { FOR EACH ROW EXECUTE FUNCTION update_updated_at_column() `); + + // Generate test data for August 2024 + await this.generateAugustTestData(); + }, + + // Generate comprehensive test data for August 2024 + async generateAugustTestData() { + const year = 2025; + const month = 8; // August + const daysInAugust = 31; + + console.log('Generating test data for August 2024...'); + + for (let day = 1; day <= daysInAugust; day++) { + const date = new Date(year, month - 1, day); // month is 0-indexed + const dateStr = date.toISOString().split('T')[0]; + + // Generate realistic but varied exercise data + // Some days will be rest days (random 20% chance) + const isRestDay = Math.random() < 0.2; + + let pushups = 0; + let situps = 0; + let plankTimeSeconds = 0; + let runDistanceKm = 0; + let hangups = 0; + + if (!isRestDay) { + // Pushups: 10-50 range with some progression over the month + const progressionBonus = Math.floor(day / 3); // Slight improvement over time + pushups = Math.floor(Math.random() * 40) + 10 + progressionBonus; + + // Situps: 15-60 range + situps = Math.floor(Math.random() * 45) + 15 + progressionBonus; + + // Plank: 30-180 seconds + plankTimeSeconds = Math.floor(Math.random() * 150) + 30; + + // Running: 0-8km, not every day (60% chance) + if (Math.random() < 0.6) { + runDistanceKm = Math.round((Math.random() * 7 + 1) * 100) / 100; // 1-8km with 2 decimal places + } + + // Hangups: 3-20 range + hangups = Math.floor(Math.random() * 18) + 3; + + // Weekend bonus (slightly higher values on weekends) + const dayOfWeek = date.getDay(); + if (dayOfWeek === 0 || dayOfWeek === 6) { + // Sunday or Saturday + pushups = Math.floor(pushups * 1.2); + situps = Math.floor(situps * 1.2); + plankTimeSeconds = Math.floor(plankTimeSeconds * 1.1); + hangups = Math.floor(hangups * 1.1); + } + } + + // Insert the data, using ON CONFLICT to avoid duplicates + try { + await this.query( + ` + INSERT INTO daily_exercises (date, pushups, situps, plank_time_seconds, run_distance_km, hangups) + VALUES ($1, $2, $3, $4, $5, $6) + ON CONFLICT (date) DO UPDATE SET + pushups = EXCLUDED.pushups, + situps = EXCLUDED.situps, + plank_time_seconds = EXCLUDED.plank_time_seconds, + run_distance_km = EXCLUDED.run_distance_km, + hangups = EXCLUDED.hangups, + updated_at = CURRENT_TIMESTAMP + `, + [dateStr, pushups, situps, plankTimeSeconds, runDistanceKm, hangups] + ); + + console.log( + `Added data for ${dateStr}: pushups=${pushups}, situps=${situps}, plank=${plankTimeSeconds}s, run=${runDistanceKm}km, hangups=${hangups}` + ); + } catch (error) { + console.error(`Failed to insert data for ${dateStr}:`, error); + } + } + + console.log('August 2024 test data generation completed!'); }, // Close all connections diff --git a/src/lib/workout/exercises.ts b/src/lib/workout/exercises.ts index 0b52199..f332fe0 100644 --- a/src/lib/workout/exercises.ts +++ b/src/lib/workout/exercises.ts @@ -9,8 +9,13 @@ export const exerciseConfigs: Record = { { label: '+10', value: 10 }, { label: '+25', value: 25 } ], + quickAddColorClass: 'bg-blue-100 text-blue-700 hover:bg-blue-200 focus:ring-blue-500', + colorClass: 'bg-blue-50 text-blue-700', + textColorClass: 'text-blue-600', max: 9999, - step: 1 + step: 1, + dailyGoal: 30, + formatter: (number) => number.toString() }, situps: { name: 'Sit-ups', @@ -20,8 +25,13 @@ export const exerciseConfigs: Record = { { label: '+10', value: 10 }, { label: '+20', value: 20 } ], + quickAddColorClass: 'bg-green-100 text-green-700 hover:bg-green-200 focus:ring-green-500', + colorClass: 'bg-green-50 text-green-700', + textColorClass: 'text-green-600', max: 9999, - step: 1 + step: 1, + dailyGoal: 35, + formatter: (number) => number.toString() }, plankSeconds: { name: 'Planke', @@ -31,9 +41,13 @@ export const exerciseConfigs: Record = { { label: '+30s', value: 30 }, { label: '+60s', value: 60 } ], + quickAddColorClass: 'bg-orange-100 text-orange-700 hover:bg-orange-200 focus:ring-orange-500', + colorClass: 'bg-orange-50 text-orange-700', + textColorClass: 'text-orange-600', max: 9999, step: 1, unit: 'sekunder', + dailyGoal: 45, formatter: (number) => formatTime(number) }, hangups: { @@ -44,8 +58,13 @@ export const exerciseConfigs: Record = { { label: '+1', value: 1 }, { label: '+5', value: 5 } ], + quickAddColorClass: 'bg-purple-100 text-purple-700 hover:bg-purple-200 focus:ring-purple-500', + colorClass: 'bg-purple-50 text-purple-700', + textColorClass: 'text-purple-600', max: 9999, - step: 1 + dailyGoal: 5, + step: 1, + formatter: (number) => number.toString() }, runKm: { name: 'Running', @@ -55,9 +74,13 @@ export const exerciseConfigs: Record = { { label: '+1km', value: 1 }, { label: '+2.5km', value: 2.5 } ], + quickAddColorClass: 'bg-red-100 text-red-700 hover:bg-red-200 focus:ring-red-500', + colorClass: 'bg-red-50 text-red-700', + textColorClass: 'text-red-600', max: 999.99, - step: 0.01, + step: 0.1, unit: 'km', + dailyGoal: 1, formatter: (value) => `${value.toFixed(2)} km` } }; @@ -74,7 +97,10 @@ function formatTime(seconds: number): string { } // Get strongly typed entries directly from exerciseConfigs -export const exercises = Object.entries(exerciseConfigs).map(([key, config]) => ({ +export const exercises: Array<{ + key: keyof WorkoutData; + config: ExerciseConfig; +}> = Object.entries(exerciseConfigs).map(([key, config]) => ({ key: key as keyof WorkoutData, config })); diff --git a/src/lib/workout/types.ts b/src/lib/workout/types.ts index fe6693b..3ce9b37 100644 --- a/src/lib/workout/types.ts +++ b/src/lib/workout/types.ts @@ -1,23 +1,36 @@ export type ColorExercise = 'blue' | 'green' | 'orange' | 'red' | 'purple'; -export type WorkoutData = { +export interface WorkoutData { pushups: number; situps: number; plankSeconds: number; hangups: number; runKm: number; -}; +} + +export interface WorkoutDataWithDate extends WorkoutData { + date: Date; +} + +export interface WorkoutDataSummary extends WorkoutData { + startDate: Date; + endDate: Date; +} export interface ExerciseConfig { name: string; icon: string; color: ColorExercise; quickAddOptions: Array<{ label: string; value: number }>; + quickAddColorClass: string; + colorClass: string; + textColorClass: string; min?: number; - max?: number; - step?: number; + max: number; + step: number; unit?: string; - formatter?: (value: number) => string; + formatter: (value: number) => string; + dailyGoal: number; } export interface QuickAddOption { diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 8c56a3c..5c6763c 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -9,4 +9,9 @@ -{@render children?.()} + + {@render children?.()} + {#snippet pending()} + Loading.. + {/snippet} + diff --git a/src/routes/ExerciseField.svelte b/src/routes/ExerciseField.svelte index 18fc601..9b2dfc8 100644 --- a/src/routes/ExerciseField.svelte +++ b/src/routes/ExerciseField.svelte @@ -1,36 +1,20 @@
@@ -44,26 +28,24 @@ type="number" bind:value defaultValue={value} - {min} + min="0" {max} {step} required={true} - class="flex-1 rounded-md border border-gray-300 px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none" + class="w-32 rounded-md border border-gray-300 px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none" /> {#each quickAddOptions as option} - {@render quickAddButton(option.label, () => quickAdd(option.value), color)} + {@render quickAddButton(option.label, () => quickAdd(option.value), quickAddColorClass)} {/each}
-{#snippet quickAddButton(label: string, onclick: () => void, color: ColorExercise)} +{#snippet quickAddButton(label: string, onclick: () => void, color: string)} diff --git a/src/routes/WorkoutDisplay.svelte b/src/routes/WorkoutDisplay.svelte index 547f1e0..96c6d3d 100644 --- a/src/routes/WorkoutDisplay.svelte +++ b/src/routes/WorkoutDisplay.svelte @@ -1,35 +1,18 @@ -
-

Today's Workout

+
+

Today's Workout

📅 {todayDate} @@ -51,25 +34,24 @@ config.icon, workoutData[key], config.name, - config.color, + config.colorClass, + config.textColorClass, config.formatter )} {/each}
- - {@const allWorkouData = (await getWorkoutHistory()).data} - - -
-

📊 Summary for last 7 days

-
- {#each exercises as { key, config }} - {@render summaryStatRow(config.name, allWorkouData[key], config.formatter)} - {/each} -
-
{/if} + +
+ +
+ {#snippet pending()}
⏳ Loading workout data...
@@ -89,25 +71,21 @@
{/snippet} + +
{#snippet workoutStatCard( icon: string, value: number, label: string, - color: ColorExercise = 'blue', - formatter?: (value: number) => string + color: string, + textColorClass: string, + formatter: (value: number) => string )} -
+
{icon}
-
{formatter ? formatter(value) : value}
-
{label}
-
-{/snippet} - -{#snippet summaryStatRow(label: string, value: number, formatter?: (value: number) => string)} -
- {label} - {formatter ? formatter(value) : value} +
{formatter(value)}
+
{label}
{/snippet} diff --git a/src/routes/WorkoutLogger.svelte b/src/routes/WorkoutLogger.svelte index 40b119c..055fb1b 100644 --- a/src/routes/WorkoutLogger.svelte +++ b/src/routes/WorkoutLogger.svelte @@ -5,7 +5,7 @@ import { getTodaysWorkout, saveWorkout } from './workout.remote'; import { fade } from 'svelte/transition'; - let todayDate = getTodayDateString(); + const todayDate = getTodayDateString(); // Form state let form: WorkoutData = $state({ @@ -26,10 +26,10 @@ } -
+
-

Log Today's Workout

+

Log Today's Workout

📅 {todayDate}
@@ -67,7 +67,7 @@ {/if} -
+

Quick Example:

💪 Push-ups: {exampleWorkout.pushups}
diff --git a/src/routes/summary/+page.svelte b/src/routes/summary/+page.svelte new file mode 100644 index 0000000..13f9c3b --- /dev/null +++ b/src/routes/summary/+page.svelte @@ -0,0 +1,94 @@ + + +
+ {@render header()} + + + + + + {#key selectedPeriod} + + {/key} + {@render footer(workoutSummary)} + + {#snippet pending()} + {@render loadingState()} + {/snippet} + + {#snippet failed(error, retry)} + {@render errorState(error, retry)} + {/snippet} + +
+ +{#snippet header()} +
+

📊 Workout Summary

+ +
+{/snippet} + +{#snippet footer(workoutSummary: WorkoutDataSummary)} + {@const timeDiff = workoutSummary.endDate.getTime() - workoutSummary.startDate.getTime()} + {@const days = Math.ceil(timeDiff / (1000 * 60 * 60 * 24)) + 1} +
+

+ Showing data for {days} day{days !== 1 ? 's' : ''} +

+
+{/snippet} + +{#snippet noDataState()} +
+
📊
+

No Data Available

+

No workout data found for the selected period.

+
+{/snippet} + +{#snippet loadingState()} +
+
+
+ Loading workout data... +
+
+{/snippet} + +{#snippet errorState(error: unknown, retry: () => void)} +
+
+ ⚠️ +

Failed to load workout data

+
+

+ {error instanceof Error ? error.message : String(error)} +

+ +
+{/snippet} diff --git a/src/routes/summary/ExerciseGraph.svelte b/src/routes/summary/ExerciseGraph.svelte new file mode 100644 index 0000000..958d1a6 --- /dev/null +++ b/src/routes/summary/ExerciseGraph.svelte @@ -0,0 +1,209 @@ + + +
+ {#if loading} +
Loading...
+ {:else if dailyData.length === 0} +
+ No data available for this period +
+ {:else} + + + + + + + Goal: {dailyGoal} + + + + {#each dailyData as { date, value }, i} + + {formatDate(date)}: {value} + + {/each} + + + + + + + + + + {maxValue} + + + 0 + + + + {#if dailyData.length > 0} + + {formatDate(dailyData[0].date)} + + {#if dailyData.length > 1} + + {formatDate(dailyData[dailyData.length - 1].date)} + + {/if} + {/if} + + {/if} +
diff --git a/src/routes/summary/ExerciseStatistics.svelte b/src/routes/summary/ExerciseStatistics.svelte new file mode 100644 index 0000000..deba389 --- /dev/null +++ b/src/routes/summary/ExerciseStatistics.svelte @@ -0,0 +1,119 @@ + + +
+
+

+ Detailed Statistics for {selectedPeriod.label.toLowerCase()} +

+
+ +
+ {#each exercises as { key, config }} + {@const totalValue = workoutSummary[key]} + {@const dailyAverage = getAveragePerDay(totalValue, workoutSummary)} + {@const goalProgress = getGoalProgress(totalValue, config.dailyGoal, workoutSummary)} + +
(selectedExercise = selectedExercise === key ? null : key)} + onkeydown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + selectedExercise = selectedExercise === key ? null : key; + } + }} + > +
+
+ {config.icon} +

{config.name}

+
+
+
+ {config.formatter(totalValue)} +
+
Total
+
+
+ +
+
+ Daily Average: + {config.formatter(Number(dailyAverage.toFixed(2)))} +
+ +
+
+
+
+ {goalProgress.toFixed(0)}% +
+
+ + +
+
+ Goal: {config.formatter(config.dailyGoal)}/day +
+ {#if goalProgress >= 100} +
✅ Goal achieved!
+ {:else if goalProgress >= 80} +
🎯 Close to goal
+ {:else if totalValue > 0} +
Keep going!
+ {/if} +
+ + {#if selectedExercise === key} +
+ +
+ {/if} +
+ {/each} +
+
diff --git a/src/routes/summary/OverviewCards.svelte b/src/routes/summary/OverviewCards.svelte new file mode 100644 index 0000000..52187d2 --- /dev/null +++ b/src/routes/summary/OverviewCards.svelte @@ -0,0 +1,57 @@ + + +
+ +
+
+
+

Days with training

+

+ {getTotalWorkouts(workoutSummary)} +

+
+
🗓️
+
+
+ + +
+
+
+

Overall Progress

+

+ {getOverallProgress(workoutSummary).toFixed(1)}% +

+
+
🎯
+
+
+ + +
+
+
+

{selectedPeriod.label}

+

{formatPeriodInfo(workoutSummary)}

+
+
📅
+
+
+
diff --git a/src/routes/summary/TimePeriodSelector.svelte b/src/routes/summary/TimePeriodSelector.svelte new file mode 100644 index 0000000..c3c27df --- /dev/null +++ b/src/routes/summary/TimePeriodSelector.svelte @@ -0,0 +1,36 @@ + + +
+

Time Period

+
+ {#each timePeriods as period} + + {/each} +
+
diff --git a/src/routes/summary/summary.remote.ts b/src/routes/summary/summary.remote.ts new file mode 100644 index 0000000..fd91f66 --- /dev/null +++ b/src/routes/summary/summary.remote.ts @@ -0,0 +1,40 @@ +import { query } from '$app/server'; +import { db } from '$lib/db'; +import { type WorkoutDataWithDate } from '$lib/workout'; +import zod from 'zod'; + +export const getWorkoutForEachDayCountingBackwards = query( + zod.number(), + async (days: number = 7) => { + try { + // Get workout data for each day counting backwards from today + const result = await db.query( + ` + SELECT * FROM daily_exercises + WHERE date >= CURRENT_DATE - INTERVAL '1 day' * $1 + ORDER BY date DESC + `, + [days] + ); + + // Convert each row to WorkoutDataWithDate + const dailyWorkouts: WorkoutDataWithDate[] = result.rows.map((row: any) => ({ + date: new Date(row.date), + pushups: Number(row.pushups) || 0, + situps: Number(row.situps) || 0, + plankSeconds: Number(row.plank_time_seconds) || 0, + hangups: Number(row.hangups) || 0, + runKm: Number(row.run_distance_km) || 0 + })); + + return { + success: true, + data: dailyWorkouts, + message: `Retrieved ${result.rows.length} daily workout records` + }; + } catch (error) { + console.error('Error fetching daily workouts:', error); + throw new Error('Failed to fetch daily workouts from database'); + } + } +); diff --git a/src/routes/summary/summaryUtils.ts b/src/routes/summary/summaryUtils.ts new file mode 100644 index 0000000..b2b6c9e --- /dev/null +++ b/src/routes/summary/summaryUtils.ts @@ -0,0 +1,125 @@ +import type { ColorExercise, WorkoutData, WorkoutDataSummary } from '$lib/workout/types'; +import { exercises } from '$lib/workout'; + +/** + * Calculate actual days for calendar periods + */ +function calculateActualDays(label: string): number { + const now = new Date(); + switch (label) { + case 'This week': { + const dayOfWeek = now.getDay(); + const monday = new Date(now); + monday.setDate(now.getDate() - (dayOfWeek === 0 ? 6 : dayOfWeek - 1)); + return Math.ceil((now.getTime() - monday.getTime()) / (1000 * 60 * 60 * 24)) + 1; + } + case 'This month': { + return now.getDate(); + } + case 'This year': { + const startOfYear = new Date(now.getFullYear(), 0, 1); + return Math.ceil((now.getTime() - startOfYear.getTime()) / (1000 * 60 * 60 * 24)) + 1; + } + default: + return 0; + } +} + +export type TimePeriod = { + label: string; + get days(): number; +}; + +function createTimePeriod(label: string): TimePeriod { + return { + label, + get days() { + return calculateActualDays(this.label); + } + }; +} + +export const timePeriods: TimePeriod[] = [ + createTimePeriod('This week'), + createTimePeriod('This month'), + createTimePeriod('This year') +]; + +/** + * Get actual days for a time period (for backwards compatibility) + */ +export function getActualDays(period: TimePeriod): number { + return period.days; +} + +/** + * Calculate average per day for a given value using WorkoutDataSummary + */ +export function getAveragePerDay(value: number, workoutSummary: WorkoutDataSummary): number { + const timeDiff = workoutSummary.endDate.getTime() - workoutSummary.startDate.getTime(); + const actualDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24)) + 1; + return actualDays > 0 ? value / actualDays : 0; +} + +/** + * Calculate total workout days + */ +export function getTotalWorkouts(workoutSummary: WorkoutDataSummary): number { + const timeDiff = workoutSummary.endDate.getTime() - workoutSummary.startDate.getTime(); + return Math.ceil(timeDiff / (1000 * 60 * 60 * 24)) + 1; +} + +/** + * Calculate goal-based progress percentage for an exercise + */ +export function getGoalProgress( + totalValue: number, + dailyGoal: number, + workoutSummary: WorkoutDataSummary +): number { + const timeDiff = workoutSummary.endDate.getTime() - workoutSummary.startDate.getTime(); + const actualDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24)) + 1; + const totalGoal = dailyGoal * actualDays; + return totalGoal > 0 ? (totalValue / totalGoal) * 100 : 0; +} + +/** + * Calculate overall progress across all exercises + */ +export function getOverallProgress(workoutSummary: WorkoutDataSummary): number { + let totalProgress = 0; + + exercises.forEach(({ key, config }) => { + const exerciseProgress = getGoalProgress(workoutSummary[key], config.dailyGoal, workoutSummary); + totalProgress += Math.min(exerciseProgress, 100); + }); + + return totalProgress / exercises.length; +} + +/** + * Format period information + */ +export function formatPeriodInfo(workoutSummary: WorkoutDataSummary): string { + const formatDate = (date: Date) => + date.toLocaleDateString('en-GB', { + day: '2-digit', + month: '2-digit' + }); + + return `${formatDate(workoutSummary.startDate)} - ${formatDate(workoutSummary.endDate)}`; +} + +/** + * Get gradient class for progress bars + */ +export function getProgressGradient(color: ColorExercise): string { + const gradients = { + blue: 'from-blue-400 to-blue-600', + green: 'from-green-400 to-green-600', + orange: 'from-orange-400 to-orange-600', + purple: 'from-purple-400 to-purple-600', + red: 'from-red-400 to-red-600' + }; + return gradients[color] || gradients.red; +} diff --git a/src/routes/workout.remote.ts b/src/routes/workout.remote.ts index 4ec2753..eb85b4c 100644 --- a/src/routes/workout.remote.ts +++ b/src/routes/workout.remote.ts @@ -1,8 +1,9 @@ import { command, form, query } from '$app/server'; import { db } from '$lib/db'; import { error } from '@sveltejs/kit'; -import type { WorkoutData } from '../lib/workout/types'; +import type { WorkoutData, WorkoutDataSummary } from '../lib/workout/types'; import { exerciseConfigs, exercises } from '$lib/workout'; +import zod from 'zod'; export const saveWorkout = form(async (data) => { let pushups = Number(data.get(exerciseConfigs.pushups.name)); @@ -48,7 +49,7 @@ export const saveWorkout = form(async (data) => { ); await getTodaysWorkout().refresh(); - await getWorkoutHistory().refresh(); + await getWorkoutHistory(7).refresh(); //TODO fixme return { success: true, @@ -93,7 +94,7 @@ export const getTodaysWorkout = query(async () => { } }); -export const getWorkoutHistory = query(async (days: number = 7) => { +export const getWorkoutHistory = query(zod.number(), async (days: number = 7) => { try { // Get workout history for the last N days const result = await db.query( @@ -107,7 +108,9 @@ export const getWorkoutHistory = query(async (days: number = 7) => { // Take all rows and add them up into each category const rows = result.rows; - const workoutData: WorkoutData = { + const workoutDataSummary: WorkoutDataSummary = { + endDate: rows[0].date, + startDate: rows[rows.length - 1].date, pushups: rows.reduce((sum, row) => sum + Number(row.pushups), 0), situps: rows.reduce((sum, row) => sum + Number(row.situps), 0), plankSeconds: rows.reduce((sum, row) => sum + Number(row.plank_time_seconds), 0), @@ -117,7 +120,7 @@ export const getWorkoutHistory = query(async (days: number = 7) => { return { success: true, - data: workoutData, + data: workoutDataSummary, message: `Retrieved ${result.rows.length} workout records` }; } catch (error) {