Implement progressive enhancement for favorites button
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				CI / update (push) Failing after 6s
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	CI / update (push) Failing after 6s
				
			- 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 <noreply@anthropic.com>
This commit is contained in:
		@@ -1,33 +1,42 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import { browser } from '$app/environment';
 | 
			
		||||
  import { enhance } from '$app/forms';
 | 
			
		||||
  
 | 
			
		||||
  export let recipeId: string;
 | 
			
		||||
  export let isFavorite: boolean = false;
 | 
			
		||||
  export let isLoggedIn: boolean = false;
 | 
			
		||||
  
 | 
			
		||||
  let isLoading = false;
 | 
			
		||||
 | 
			
		||||
  async function toggleFavorite() {
 | 
			
		||||
    if (!isLoggedIn || isLoading) return;
 | 
			
		||||
  async function toggleFavorite(event: Event) {
 | 
			
		||||
    // If JavaScript is available, prevent form submission and handle client-side
 | 
			
		||||
    if (browser) {
 | 
			
		||||
      event.preventDefault();
 | 
			
		||||
      
 | 
			
		||||
    isLoading = true;
 | 
			
		||||
      if (!isLoggedIn || isLoading) return;
 | 
			
		||||
      
 | 
			
		||||
    try {
 | 
			
		||||
      const method = isFavorite ? 'DELETE' : 'POST';
 | 
			
		||||
      const response = await fetch('/api/rezepte/favorites', {
 | 
			
		||||
        method,
 | 
			
		||||
        headers: {
 | 
			
		||||
          'Content-Type': 'application/json',
 | 
			
		||||
        },
 | 
			
		||||
        body: JSON.stringify({ recipeId }),
 | 
			
		||||
      });
 | 
			
		||||
      isLoading = true;
 | 
			
		||||
      
 | 
			
		||||
      if (response.ok) {
 | 
			
		||||
        isFavorite = !isFavorite;
 | 
			
		||||
      try {
 | 
			
		||||
        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) {
 | 
			
		||||
          isFavorite = !isFavorite;
 | 
			
		||||
        }
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        console.error('Failed to toggle favorite:', error);
 | 
			
		||||
      } finally {
 | 
			
		||||
        isLoading = false;
 | 
			
		||||
      }
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error('Failed to toggle favorite:', error);
 | 
			
		||||
    } finally {
 | 
			
		||||
      isLoading = false;
 | 
			
		||||
    }
 | 
			
		||||
    // If no JS, form will submit normally
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
@@ -55,12 +64,17 @@
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
{#if isLoggedIn}
 | 
			
		||||
  <button 
 | 
			
		||||
    class="favorite-button"
 | 
			
		||||
    disabled={isLoading}
 | 
			
		||||
    on:click={toggleFavorite}
 | 
			
		||||
    title={isFavorite ? 'Favorit entfernen' : 'Als Favorit speichern'}
 | 
			
		||||
  >
 | 
			
		||||
    {isFavorite ? '❤️' : '🖤'}
 | 
			
		||||
  </button>
 | 
			
		||||
  <form method="post" action="?/toggleFavorite" style="display: inline;" use:enhance>
 | 
			
		||||
    <input type="hidden" name="recipeId" value={recipeId} />
 | 
			
		||||
    <input type="hidden" name="isFavorite" value={isFavorite} />
 | 
			
		||||
    <button 
 | 
			
		||||
      type="submit"
 | 
			
		||||
      class="favorite-button"
 | 
			
		||||
      disabled={isLoading}
 | 
			
		||||
      on:click={toggleFavorite}
 | 
			
		||||
      title={isFavorite ? 'Favorit entfernen' : 'Als Favorit speichern'}
 | 
			
		||||
    >
 | 
			
		||||
      {isFavorite ? '❤️' : '🖤'}
 | 
			
		||||
    </button>
 | 
			
		||||
  </form>
 | 
			
		||||
{/if}
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user