From b4444e7bd2fe9eeae3f4c3e83eb9630aa68f0aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20St=C3=B8rdal?= <30749741+hakon55@users.noreply.github.com> Date: Tue, 2 Sep 2025 22:51:17 +0200 Subject: [PATCH] Working example --- .prettierrc | 16 -- package-lock.json | 186 ++++++++++++++++++ package.json | 5 + prettier.config.ts | 20 ++ src/hooks.client.ts | 21 +++ src/hooks.server.ts | 35 ++++ src/lib/WorkoutDisplay.svelte | 148 +++++++++++++++ src/lib/WorkoutLogger.svelte | 271 +++++++++++++++++++++++++++ src/lib/db.ts | 81 ++++++++ src/routes/+page.svelte | 35 +++- src/routes/DbConnection.svelte | 12 ++ src/routes/db.remote.js | 6 + src/routes/workout.remote.ts | 109 +++++++++++ svelte.config.js => svelte.config.ts | 12 +- 14 files changed, 935 insertions(+), 22 deletions(-) delete mode 100644 .prettierrc create mode 100644 prettier.config.ts create mode 100644 src/hooks.client.ts create mode 100644 src/hooks.server.ts create mode 100644 src/lib/WorkoutDisplay.svelte create mode 100644 src/lib/WorkoutLogger.svelte create mode 100644 src/lib/db.ts create mode 100644 src/routes/DbConnection.svelte create mode 100644 src/routes/db.remote.js create mode 100644 src/routes/workout.remote.ts rename svelte.config.js => svelte.config.ts (56%) diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 8103a0b..0000000 --- a/.prettierrc +++ /dev/null @@ -1,16 +0,0 @@ -{ - "useTabs": true, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 100, - "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], - "overrides": [ - { - "files": "*.svelte", - "options": { - "parser": "svelte" - } - } - ], - "tailwindStylesheet": "./src/app.css" -} diff --git a/package-lock.json b/package-lock.json index a5dd651..f5ae55c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,11 @@ "": { "name": "egentrening", "version": "0.0.1", + "dependencies": { + "@types/pg": "^8.15.5", + "pg": "^8.16.3", + "zod": "^4.1.5" + }, "devDependencies": { "@sveltejs/adapter-auto": "^6.0.0", "@sveltejs/kit": "^2.22.0", @@ -1225,6 +1230,26 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/pg": { + "version": "8.15.5", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.5.tgz", + "integrity": "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -1833,6 +1858,95 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1882,6 +1996,45 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prettier": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", @@ -2096,6 +2249,15 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/svelte": { "version": "5.38.6", "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.38.6.tgz", @@ -2226,6 +2388,12 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "license": "MIT" + }, "node_modules/vite": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.4.tgz", @@ -2321,6 +2489,15 @@ } } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", @@ -2337,6 +2514,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 05ae257..9c16393 100644 --- a/package.json +++ b/package.json @@ -26,5 +26,10 @@ "tailwindcss": "^4.0.0", "typescript": "^5.0.0", "vite": "^7.0.4" + }, + "dependencies": { + "@types/pg": "^8.15.5", + "pg": "^8.16.3", + "zod": "^4.1.5" } } diff --git a/prettier.config.ts b/prettier.config.ts new file mode 100644 index 0000000..3eb33d8 --- /dev/null +++ b/prettier.config.ts @@ -0,0 +1,20 @@ +import type { Config } from 'prettier'; + +const config: Config = { + useTabs: true, + singleQuote: true, + trailingComma: 'none', + printWidth: 100, + plugins: ['prettier-plugin-svelte', 'prettier-plugin-tailwindcss'], + overrides: [ + { + files: '*.svelte', + options: { + parser: 'svelte' + } + } + ], + tailwindStylesheet: './src/app.css' +}; + +export default config; diff --git a/src/hooks.client.ts b/src/hooks.client.ts new file mode 100644 index 0000000..b3af44e --- /dev/null +++ b/src/hooks.client.ts @@ -0,0 +1,21 @@ +import type { HandleClientError } from '@sveltejs/kit'; +import { dev } from '$app/environment'; + +// Handle client-side errors +export const handleError: HandleClientError = ({ error, event }) => { + console.error('Client error:', error); + + // Log additional context in development + if (dev) { + console.error('Event details:', { + url: event.url, + route: event.route?.id + }); + } + + // Return user-friendly error message + return { + message: dev ? String(error) : 'Something went wrong', + code: 'CLIENT_ERROR' + }; +}; diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..e91472a --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,35 @@ +import type { Handle, ServerInit } from '@sveltejs/kit'; +import { dev } from '$app/environment'; +import { db } from '$lib/db'; + +export const init: ServerInit = async () => { + db.createTables(); + console.log('Tables created'); +}; + +// The handle function runs on every request +export const handle: Handle = async ({ event, resolve }) => { + // Initialize on first request (lazy initialization) + + // Add custom headers or modify request/response if needed + const response = await resolve(event); + + // Optional: Add security headers + if (!dev) { + response.headers.set('X-Content-Type-Options', 'nosniff'); + response.headers.set('X-Frame-Options', 'DENY'); + response.headers.set('X-XSS-Protection', '1; mode=block'); + } + + return response; +}; + +// Optional: Handle server errors +export const handleError = ({ error, event }) => { + console.error('Server error:', error); + + // Don't expose sensitive error details in production + return { + message: dev ? String(error) : 'Internal server error' + }; +}; diff --git a/src/lib/WorkoutDisplay.svelte b/src/lib/WorkoutDisplay.svelte new file mode 100644 index 0000000..fcc7e7b --- /dev/null +++ b/src/lib/WorkoutDisplay.svelte @@ -0,0 +1,148 @@ + + +
+

Today's Workout

+ +
+ 📅 {todayDate} +
+ + {#if loading} +
+
⏳ Loading workout data...
+
+ {:else if error} +
+
Error loading data
+
{error}
+ +
+ {:else if !workoutData} +
+
📝 No workout recorded for today
+
Start logging your exercises below!
+
+ {:else} + +
+ +
+
💪
+
{workoutData.pushups}
+
Push-ups
+
+ + +
+
🏋️
+
{workoutData.situps}
+
Sit-ups
+
+ + +
+
🧘
+
+ {formatTime(workoutData.plank_time_seconds)} +
+
Plank
+
+ + +
+
🏃
+
{workoutData.run_distance_km} km
+
Running
+
+
+ + +
+

📊 Summary

+
+
+ Total exercises: + {workoutData.pushups + workoutData.situps} +
+
+ Plank time: + {formatTime(workoutData.plank_time_seconds)} +
+
+ Distance: + {workoutData.run_distance_km} km +
+
+
+ + + {#if workoutData.created_at || workoutData.updated_at} +
+ {#if workoutData.created_at} +
Created: {new Date(workoutData.created_at).toLocaleString()}
+ {/if} + {#if workoutData.updated_at && workoutData.updated_at !== workoutData.created_at} +
Updated: {new Date(workoutData.updated_at).toLocaleString()}
+ {/if} +
+ {/if} + {/if} + + +
+ +
+
diff --git a/src/lib/WorkoutLogger.svelte b/src/lib/WorkoutLogger.svelte new file mode 100644 index 0000000..940f998 --- /dev/null +++ b/src/lib/WorkoutLogger.svelte @@ -0,0 +1,271 @@ + + +
+

Log Today's Workout

+ +
+ 📅 {todayDate} +
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ + +
+ + + {#if message} +
+ {message} +
+ {/if} + + +
+

Your Example:

+
+
💪 Push-ups: 100
+
🏋️ Sit-ups: 50
+
🧘 Plank: 0 seconds
+
🏃 Running: 4.0 km
+
+ +
+
diff --git a/src/lib/db.ts b/src/lib/db.ts new file mode 100644 index 0000000..33f3edf --- /dev/null +++ b/src/lib/db.ts @@ -0,0 +1,81 @@ +import { DATABASE_URL } from '$env/static/private'; +import { Pool } from 'pg'; + +// Create a connection pool +const pool = new Pool({ + connectionString: DATABASE_URL, + ssl: false +}); + +// Simple database client +export const db = { + // Execute a query + async query(text: string, params?: any[]) { + const client = await pool.connect(); + try { + const result = await client.query(text, params); + return result; + } finally { + client.release(); + } + }, + + // Test connection + async testConnection() { + try { + const result = await this.query('SELECT NOW() as current_time'); + console.log('Database connected successfully:', result.rows[0]); + return true; + } catch (error) { + console.error('Database connection failed:', error); + return false; + } + }, + + // Create tables for tracking daily exercises + async createTables() { + // Create exercises table with all activities in one table + await this.query(` + CREATE TABLE IF NOT EXISTS daily_exercises ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + date DATE NOT NULL UNIQUE, + pushups INTEGER DEFAULT 0, + situps INTEGER DEFAULT 0, + plank_time_seconds INTEGER DEFAULT 0, + run_distance_km DECIMAL(5,2) DEFAULT 0.0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `); + + // Create index for faster date lookups + await this.query(` + CREATE INDEX IF NOT EXISTS idx_daily_exercises_date + ON daily_exercises(date) + `); + + // Create trigger to update updated_at timestamp + await this.query(` + CREATE OR REPLACE FUNCTION update_updated_at_column() + RETURNS TRIGGER AS $$ + BEGIN + NEW.updated_at = CURRENT_TIMESTAMP; + RETURN NEW; + END; + $$ language 'plpgsql' + `); + + await this.query(` + DROP TRIGGER IF EXISTS update_daily_exercises_updated_at ON daily_exercises; + CREATE TRIGGER update_daily_exercises_updated_at + BEFORE UPDATE ON daily_exercises + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column() + `); + }, + + // Close all connections + async close() { + await pool.end(); + } +}; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 76bf958..0dd9c62 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,3 +1,32 @@ -

Welcome to SvelteKit

-

Here by text

-

Visit svelte.dev/docs/kit to read the documentation

+ + +
+
+

🏋️ Egentrening

+

Track your daily fitness progress

+ +
+ +
+ +
+ + +
+ +
+
+
+
diff --git a/src/routes/DbConnection.svelte b/src/routes/DbConnection.svelte new file mode 100644 index 0000000..dc84b0c --- /dev/null +++ b/src/routes/DbConnection.svelte @@ -0,0 +1,12 @@ + + + +

Here be connection: {await testConnection().current}

+ + + {#snippet pending()} +

loading...

+ {/snippet} +
diff --git a/src/routes/db.remote.js b/src/routes/db.remote.js new file mode 100644 index 0000000..9fb7616 --- /dev/null +++ b/src/routes/db.remote.js @@ -0,0 +1,6 @@ +import { query } from '$app/server'; +import { db } from '$lib/db'; + +export const testConnection = query(async () => { + return db.testConnection(); +}); diff --git a/src/routes/workout.remote.ts b/src/routes/workout.remote.ts new file mode 100644 index 0000000..4460a8c --- /dev/null +++ b/src/routes/workout.remote.ts @@ -0,0 +1,109 @@ +import { query } from '$app/server'; +import { db } from '$lib/db'; +import { z } from 'zod'; + +interface WorkOutData { + pushups: number; + situps: number; + plankSeconds: number; + runKm: number; +} + +export const saveWorkout = query( + z.object({ + pushups: z.number().min(0), + situps: z.number().min(0), + plankSeconds: z.number().min(0), + runKm: z.number().min(0) + }), + async (workoutData: WorkOutData) => { + const { pushups, situps, plankSeconds, runKm } = workoutData; + + // Validate input data + if (typeof pushups !== 'number' || pushups < 0) { + throw new Error('Invalid pushups value'); + } + if (typeof situps !== 'number' || situps < 0) { + throw new Error('Invalid situps value'); + } + if (typeof plankSeconds !== 'number' || plankSeconds < 0) { + throw new Error('Invalid plank time value'); + } + if (typeof runKm !== 'number' || runKm < 0) { + throw new Error('Invalid run distance value'); + } + + try { + // Insert or update today's workout + const result = await db.query( + ` + INSERT INTO daily_exercises (date, pushups, situps, plank_time_seconds, run_distance_km) + VALUES (CURRENT_DATE, $1, $2, $3, $4) + ON CONFLICT (date) + DO UPDATE SET + pushups = $1, + situps = $2, + plank_time_seconds = $3, + run_distance_km = $4, + updated_at = CURRENT_TIMESTAMP + RETURNING * + `, + [pushups, situps, plankSeconds, runKm] + ); + + return { + success: true, + data: result.rows[0], + message: 'Workout saved successfully!' + }; + } catch (error) { + console.error('Error saving workout:', error); + throw new Error('Failed to save workout to database'); + } + } +); + +export const getTodaysWorkout = query(async () => { + try { + // Get today's workout data + const result = await db.query('SELECT * FROM daily_exercises WHERE date = CURRENT_DATE'); + + if (result.rows.length === 0) { + return { + data: null, + message: 'No workout recorded for today' + }; + } + + return { + success: true, + data: result.rows[0] + }; + } catch (error) { + console.error('Error fetching workout:', error); + throw new Error('Failed to fetch workout from database'); + } +}); + +export const getWorkoutHistory = query(async (days: number = 7) => { + try { + // Get workout history for the last N days + const result = await db.query( + ` + SELECT * FROM daily_exercises + WHERE date >= CURRENT_DATE - INTERVAL '$1 days' + ORDER BY date DESC + `, + [days] + ); + + return { + success: true, + data: result.rows, + message: `Retrieved ${result.rows.length} workout records` + }; + } catch (error) { + console.error('Error fetching workout history:', error); + throw new Error('Failed to fetch workout history from database'); + } +}); diff --git a/svelte.config.js b/svelte.config.ts similarity index 56% rename from svelte.config.js rename to svelte.config.ts index 6adee23..2fe6dae 100644 --- a/svelte.config.js +++ b/svelte.config.ts @@ -1,12 +1,18 @@ import adapter from '@sveltejs/adapter-auto'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +import type { Config } from '@sveltejs/kit'; -/** @type {import('@sveltejs/kit').Config} */ -const config = { +const config: Config = { // Consult https://svelte.dev/docs/kit/integrations // for more information about preprocessors preprocess: vitePreprocess(), - kit: { adapter: adapter() } + compilerOptions: { + experimental: { async: true } + }, + kit: { + adapter: adapter(), + experimental: { remoteFunctions: true } + } }; export default config;