From 6a8478f8a65123f7303acc083d0cf92ae6c39e6f Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Thu, 4 Sep 2025 17:05:07 +0200 Subject: [PATCH] Implement progressive enhancement for favorites button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add server-side form handling for favorites without JavaScript - Create toggleFavorite server action that uses existing API endpoint - Update FavoriteButton component with form-based fallback - Maintain JavaScript enhancement for smoother UX when available - Use server-side fetch to reuse centralized favorites API logic 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/lib/components/FavoriteButton.svelte | 70 ++++++++++++++--------- src/routes/rezepte/[name]/+page.server.ts | 46 ++++++++++++++- 2 files changed, 87 insertions(+), 29 deletions(-) diff --git a/src/lib/components/FavoriteButton.svelte b/src/lib/components/FavoriteButton.svelte index ce3831d..2d63227 100644 --- a/src/lib/components/FavoriteButton.svelte +++ b/src/lib/components/FavoriteButton.svelte @@ -1,33 +1,42 @@ @@ -55,12 +64,17 @@ {#if isLoggedIn} - +
+ + + +
{/if} \ No newline at end of file diff --git a/src/routes/rezepte/[name]/+page.server.ts b/src/routes/rezepte/[name]/+page.server.ts index 0b4cd3c..6280387 100644 --- a/src/routes/rezepte/[name]/+page.server.ts +++ b/src/routes/rezepte/[name]/+page.server.ts @@ -1,6 +1,50 @@ -import { redirect } from '@sveltejs/kit'; +import { redirect, error } from '@sveltejs/kit'; export const actions = { + toggleFavorite: async ({ request, locals, url, fetch }) => { + const session = await locals.auth(); + + if (!session?.user?.nickname) { + throw error(401, 'Authentication required'); + } + + const formData = await request.formData(); + const recipeId = formData.get('recipeId') as string; + const isFavorite = formData.get('isFavorite') === 'true'; + + if (!recipeId) { + throw error(400, 'Recipe ID required'); + } + + try { + // Use the existing API endpoint + const method = isFavorite ? 'DELETE' : 'POST'; + const response = await fetch('/api/rezepte/favorites', { + method, + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ recipeId }), + }); + + if (!response.ok) { + const errorData = await response.text(); + console.error('API error:', response.status, errorData); + throw error(response.status, `Failed to toggle favorite: ${errorData}`); + } + + // Redirect back to the same page to refresh the state + throw redirect(303, url.pathname); + } catch (e) { + // If it's a redirect, let it through + if (e && typeof e === 'object' && 'status' in e && e.status === 303) { + throw e; + } + console.error('Favorite toggle error:', e); + throw error(500, 'Failed to toggle favorite'); + } + }, + swapYeast: async ({ request, url }) => { const formData = await request.formData(); const yeastId = parseInt(formData.get('yeastId') as string);