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,11 +1,18 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					  import { browser } from '$app/environment';
 | 
				
			||||||
 | 
					  import { enhance } from '$app/forms';
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  export let recipeId: string;
 | 
					  export let recipeId: string;
 | 
				
			||||||
  export let isFavorite: boolean = false;
 | 
					  export let isFavorite: boolean = false;
 | 
				
			||||||
  export let isLoggedIn: boolean = false;
 | 
					  export let isLoggedIn: boolean = false;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  let isLoading = false;
 | 
					  let isLoading = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async function toggleFavorite() {
 | 
					  async function toggleFavorite(event: Event) {
 | 
				
			||||||
 | 
					    // If JavaScript is available, prevent form submission and handle client-side
 | 
				
			||||||
 | 
					    if (browser) {
 | 
				
			||||||
 | 
					      event.preventDefault();
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
      if (!isLoggedIn || isLoading) return;
 | 
					      if (!isLoggedIn || isLoading) return;
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      isLoading = true;
 | 
					      isLoading = true;
 | 
				
			||||||
@@ -29,6 +36,8 @@
 | 
				
			|||||||
        isLoading = false;
 | 
					        isLoading = false;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    // If no JS, form will submit normally
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
@@ -55,7 +64,11 @@
 | 
				
			|||||||
</style>
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{#if isLoggedIn}
 | 
					{#if isLoggedIn}
 | 
				
			||||||
 | 
					  <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 
 | 
					    <button 
 | 
				
			||||||
 | 
					      type="submit"
 | 
				
			||||||
      class="favorite-button"
 | 
					      class="favorite-button"
 | 
				
			||||||
      disabled={isLoading}
 | 
					      disabled={isLoading}
 | 
				
			||||||
      on:click={toggleFavorite}
 | 
					      on:click={toggleFavorite}
 | 
				
			||||||
@@ -63,4 +76,5 @@
 | 
				
			|||||||
    >
 | 
					    >
 | 
				
			||||||
      {isFavorite ? '❤️' : '🖤'}
 | 
					      {isFavorite ? '❤️' : '🖤'}
 | 
				
			||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
 | 
					  </form>
 | 
				
			||||||
{/if}
 | 
					{/if}
 | 
				
			||||||
@@ -1,6 +1,50 @@
 | 
				
			|||||||
import { redirect } from '@sveltejs/kit';
 | 
					import { redirect, error } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const actions = {
 | 
					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 }) => {
 | 
					    swapYeast: async ({ request, url }) => {
 | 
				
			||||||
        const formData = await request.formData();
 | 
					        const formData = await request.formData();
 | 
				
			||||||
        const yeastId = parseInt(formData.get('yeastId') as string);
 | 
					        const yeastId = parseInt(formData.get('yeastId') as string);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user