Implement progressive enhancement for yeast swapper with state persistence
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				CI / update (push) Failing after 9s
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	CI / update (push) Failing after 9s
				
			- Add server-side form handling for yeast swapping without JavaScript
- Implement toggle-based URL parameter system (y0=1, y1=1) for clean URLs
- Add server action to toggle yeast flags and preserve all URL state
- Update multiplier forms to preserve yeast toggle states across submissions
- Calculate yeast conversions server-side from original recipe data
- Fix {{multiplier}} placeholder replacement to handle non-numeric amounts
- Enable multiple independent yeast swappers with full state preservation
- Maintain perfect progressive enhancement: works with and without JS
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
			
			
This commit is contained in:
		@@ -1,51 +1,39 @@
 | 
				
			|||||||
<script>
 | 
					<script>
 | 
				
			||||||
	import { createEventDispatcher } from 'svelte';
 | 
						import { createEventDispatcher } from 'svelte';
 | 
				
			||||||
 | 
						import { browser } from '$app/environment';
 | 
				
			||||||
 | 
						import { enhance } from '$app/forms';
 | 
				
			||||||
 | 
						import { page } from '$app/stores';
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	export let item;
 | 
						export let item;
 | 
				
			||||||
	export let multiplier = 1;
 | 
						export let multiplier = 1;
 | 
				
			||||||
 | 
						export let yeastId = 0;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	const dispatch = createEventDispatcher();
 | 
						const dispatch = createEventDispatcher();
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	function toggleHefe() {
 | 
						// Get all current URL parameters to preserve state
 | 
				
			||||||
		let newName, newAmount, newUnit = item.unit;
 | 
						$: currentParams = browser ? new URLSearchParams(window.location.search) : $page.url.searchParams;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
		if (item.name === "Frischhefe") {
 | 
						function toggleHefe(event) {
 | 
				
			||||||
			// Convert fresh yeast to dry yeast
 | 
							// If JavaScript is available, prevent form submission and handle client-side
 | 
				
			||||||
			const originalAmount = parseFloat(item.amount) / multiplier;
 | 
							if (browser) {
 | 
				
			||||||
			newName = "Trockenhefe";
 | 
								event.preventDefault();
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			if (item.unit === "Prise") {
 | 
								// Simply toggle the yeast flag in the URL
 | 
				
			||||||
				// "1 Prise Frischhefe" → "1 Prise Trockenhefe"
 | 
								const url = new URL(window.location);
 | 
				
			||||||
				newAmount = item.amount;
 | 
								const yeastParam = `y${yeastId}`;
 | 
				
			||||||
				newUnit = "Prise";
 | 
								
 | 
				
			||||||
			} else if (item.unit === "g" && originalAmount === 1) {
 | 
								if (url.searchParams.has(yeastParam)) {
 | 
				
			||||||
				// "1 g Frischhefe" → "1 Prise Trockenhefe"
 | 
									url.searchParams.delete(yeastParam);
 | 
				
			||||||
				newAmount = "1";
 | 
					 | 
				
			||||||
				newUnit = "Prise";
 | 
					 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				// Normal conversion: "9 g Frischhefe" → "3 g Trockenhefe" (divide by 3)
 | 
									url.searchParams.set(yeastParam, '1');
 | 
				
			||||||
				newAmount = (originalAmount / 3).toString();
 | 
					 | 
				
			||||||
				newUnit = "g";
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else if (item.name === "Trockenhefe") {
 | 
					 | 
				
			||||||
			// Convert dry yeast to fresh yeast
 | 
					 | 
				
			||||||
			const originalAmount = parseFloat(item.amount) / multiplier;
 | 
					 | 
				
			||||||
			newName = "Frischhefe";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			if (item.unit === "Prise") {
 | 
					 | 
				
			||||||
				// "1 Prise Trockenhefe" → "1 g Frischhefe"
 | 
					 | 
				
			||||||
				newAmount = "1";
 | 
					 | 
				
			||||||
				newUnit = "g";
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				// Normal conversion: "1 g Trockenhefe" → "3 g Frischhefe" (multiply by 3)
 | 
					 | 
				
			||||||
				newAmount = (originalAmount * 3).toString();
 | 
					 | 
				
			||||||
				newUnit = "g";
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
		if (newName && newAmount) {
 | 
								window.history.replaceState({}, '', url);
 | 
				
			||||||
			dispatch('toggle', { name: newName, amount: newAmount, unit: newUnit });
 | 
								
 | 
				
			||||||
 | 
								// Trigger page reload to recalculate ingredients server-side
 | 
				
			||||||
 | 
								window.location.reload();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// If no JS, form will submit normally
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
@@ -60,6 +48,13 @@
 | 
				
			|||||||
		fill: var(--blue);
 | 
							fill: var(--blue);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
<button on:click={toggleHefe} title="Zwischen Frischhefe und Trockenhefe wechseln">
 | 
					<form method="post" action="?/swapYeast" style="display: inline;" use:enhance>
 | 
				
			||||||
 | 
						<input type="hidden" name="yeastId" value={yeastId} />
 | 
				
			||||||
 | 
						<!-- Include all current URL parameters to preserve state -->
 | 
				
			||||||
 | 
						{#each Array.from(currentParams.entries()) as [key, value]}
 | 
				
			||||||
 | 
							<input type="hidden" name="currentParam_{key}" value={value} />
 | 
				
			||||||
 | 
						{/each}
 | 
				
			||||||
 | 
						<button type="submit" on:click={toggleHefe} title="Zwischen Frischhefe und Trockenhefe wechseln">
 | 
				
			||||||
		<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M105.1 202.6c7.7-21.8 20.2-42.3 37.8-59.8c62.5-62.5 163.8-62.5 226.3 0L386.3 160 352 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l111.5 0c0 0 0 0 0 0l.4 0c17.7 0 32-14.3 32-32l0-112c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 35.2L414.4 97.6c-87.5-87.5-229.3-87.5-316.8 0C73.2 122 55.6 150.7 44.8 181.4c-5.9 16.7 2.9 34.9 19.5 40.8s34.9-2.9 40.8-19.5zM39 289.3c-5 1.5-9.8 4.2-13.7 8.2c-4 4-6.7 8.8-8.1 14c-.3 1.2-.6 2.5-.8 3.8c-.3 1.7-.4 3.4-.4 5.1L16 432c0 17.7 14.3 32 32 32s32-14.3 32-32l0-35.1 17.6 17.5c0 0 0 0 0 0c87.5 87.4 229.3 87.4 316.7 0c24.4-24.4 42.1-53.1 52.9-83.8c5.9-16.7-2.9-34.9-19.5-40.8s-34.9 2.9-40.8 19.5c-7.7 21.8-20.2 42.3-37.8 59.8c-62.5 62.5-163.8 62.5-226.3 0l-.1-.1L125.6 352l34.4 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L48.4 288c-1.6 0-3.2 .1-4.8 .3s-3.1 .5-4.6 1z"/></svg>
 | 
							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M105.1 202.6c7.7-21.8 20.2-42.3 37.8-59.8c62.5-62.5 163.8-62.5 226.3 0L386.3 160 352 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l111.5 0c0 0 0 0 0 0l.4 0c17.7 0 32-14.3 32-32l0-112c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 35.2L414.4 97.6c-87.5-87.5-229.3-87.5-316.8 0C73.2 122 55.6 150.7 44.8 181.4c-5.9 16.7 2.9 34.9 19.5 40.8s34.9-2.9 40.8-19.5zM39 289.3c-5 1.5-9.8 4.2-13.7 8.2c-4 4-6.7 8.8-8.1 14c-.3 1.2-.6 2.5-.8 3.8c-.3 1.7-.4 3.4-.4 5.1L16 432c0 17.7 14.3 32 32 32s32-14.3 32-32l0-35.1 17.6 17.5c0 0 0 0 0 0c87.5 87.4 229.3 87.4 316.7 0c24.4-24.4 42.1-53.1 52.9-83.8c5.9-16.7-2.9-34.9-19.5-40.8s-34.9 2.9-40.8 19.5c-7.7 21.8-20.2 42.3-37.8 59.8c-62.5 62.5-163.8 62.5-226.3 0l-.1-.1L125.6 352l34.4 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L48.4 288c-1.6 0-3.2 .1-4.8 .3s-3.1 .5-4.6 1z"/></svg>
 | 
				
			||||||
	</button>
 | 
						</button>
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,10 +2,34 @@
 | 
				
			|||||||
import { onMount } from 'svelte';
 | 
					import { onMount } from 'svelte';
 | 
				
			||||||
import { onNavigate } from "$app/navigation";
 | 
					import { onNavigate } from "$app/navigation";
 | 
				
			||||||
import { browser } from '$app/environment';
 | 
					import { browser } from '$app/environment';
 | 
				
			||||||
 | 
					import { page } from '$app/stores';
 | 
				
			||||||
import HefeSwapper from './HefeSwapper.svelte';
 | 
					import HefeSwapper from './HefeSwapper.svelte';
 | 
				
			||||||
export let data
 | 
					export let data
 | 
				
			||||||
let multiplier = data.multiplier || 1;
 | 
					let multiplier = data.multiplier || 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Calculate yeast IDs for each yeast ingredient
 | 
				
			||||||
 | 
					let yeastIds = {};
 | 
				
			||||||
 | 
					$: {
 | 
				
			||||||
 | 
						yeastIds = {};
 | 
				
			||||||
 | 
						let yeastCounter = 0;
 | 
				
			||||||
 | 
						if (data.ingredients) {
 | 
				
			||||||
 | 
							for (let listIndex = 0; listIndex < data.ingredients.length; listIndex++) {
 | 
				
			||||||
 | 
								const list = data.ingredients[listIndex];
 | 
				
			||||||
 | 
								if (list.list) {
 | 
				
			||||||
 | 
									for (let ingredientIndex = 0; ingredientIndex < list.list.length; ingredientIndex++) {
 | 
				
			||||||
 | 
										const ingredient = list.list[ingredientIndex];
 | 
				
			||||||
 | 
										if (ingredient.name === "Frischhefe" || ingredient.name === "Trockenhefe") {
 | 
				
			||||||
 | 
											yeastIds[`${listIndex}-${ingredientIndex}`] = yeastCounter++;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get all current URL parameters to preserve state in multiplier forms
 | 
				
			||||||
 | 
					$: currentParams = browser ? new URLSearchParams(window.location.search) : $page.url.searchParams;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Progressive enhancement - use JS if available
 | 
					// Progressive enhancement - use JS if available
 | 
				
			||||||
onMount(() => {
 | 
					onMount(() => {
 | 
				
			||||||
	if (browser) {
 | 
						if (browser) {
 | 
				
			||||||
@@ -154,14 +178,7 @@ function adjust_amount(string, multiplier){
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function handleHefeToggle(event, item) {
 | 
					// No need for complex yeast toggle handling - everything is calculated server-side now
 | 
				
			||||||
	item.name = event.detail.name;
 | 
					 | 
				
			||||||
	item.amount = event.detail.amount;
 | 
					 | 
				
			||||||
	if (event.detail.unit) {
 | 
					 | 
				
			||||||
		item.unit = event.detail.unit;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	data = data; // Trigger reactivity
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
*{
 | 
					*{
 | 
				
			||||||
@@ -309,25 +326,55 @@ span
 | 
				
			|||||||
<div class=multipliers>
 | 
					<div class=multipliers>
 | 
				
			||||||
	<form method="get" style="display: inline;">
 | 
						<form method="get" style="display: inline;">
 | 
				
			||||||
		<input type="hidden" name="multiplier" value="0.5" />
 | 
							<input type="hidden" name="multiplier" value="0.5" />
 | 
				
			||||||
 | 
							{#each Array.from(currentParams.entries()) as [key, value]}
 | 
				
			||||||
 | 
								{#if key !== 'multiplier'}
 | 
				
			||||||
 | 
									<input type="hidden" name={key} value={value} />
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
							{/each}
 | 
				
			||||||
		<button type="submit" class:selected={multiplier==0.5} on:click={(e) => handleMultiplierClick(e, 0.5)}>{@html "<sup>1</sup>/<sub>2</sub>x"}</button>
 | 
							<button type="submit" class:selected={multiplier==0.5} on:click={(e) => handleMultiplierClick(e, 0.5)}>{@html "<sup>1</sup>/<sub>2</sub>x"}</button>
 | 
				
			||||||
	</form>
 | 
						</form>
 | 
				
			||||||
	<form method="get" style="display: inline;">
 | 
						<form method="get" style="display: inline;">
 | 
				
			||||||
		<input type="hidden" name="multiplier" value="1" />
 | 
							<input type="hidden" name="multiplier" value="1" />
 | 
				
			||||||
 | 
							{#each Array.from(currentParams.entries()) as [key, value]}
 | 
				
			||||||
 | 
								{#if key !== 'multiplier'}
 | 
				
			||||||
 | 
									<input type="hidden" name={key} value={value} />
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
							{/each}
 | 
				
			||||||
		<button type="submit" class:selected={multiplier==1} on:click={(e) => handleMultiplierClick(e, 1)}>1x</button>
 | 
							<button type="submit" class:selected={multiplier==1} on:click={(e) => handleMultiplierClick(e, 1)}>1x</button>
 | 
				
			||||||
	</form>
 | 
						</form>
 | 
				
			||||||
	<form method="get" style="display: inline;">
 | 
						<form method="get" style="display: inline;">
 | 
				
			||||||
		<input type="hidden" name="multiplier" value="1.5" />
 | 
							<input type="hidden" name="multiplier" value="1.5" />
 | 
				
			||||||
 | 
							{#each Array.from(currentParams.entries()) as [key, value]}
 | 
				
			||||||
 | 
								{#if key !== 'multiplier'}
 | 
				
			||||||
 | 
									<input type="hidden" name={key} value={value} />
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
							{/each}
 | 
				
			||||||
		<button type="submit" class:selected={multiplier==1.5} on:click={(e) => handleMultiplierClick(e, 1.5)}>{@html "<sup>3</sup>/<sub>2</sub>x"}</button>
 | 
							<button type="submit" class:selected={multiplier==1.5} on:click={(e) => handleMultiplierClick(e, 1.5)}>{@html "<sup>3</sup>/<sub>2</sub>x"}</button>
 | 
				
			||||||
	</form>
 | 
						</form>
 | 
				
			||||||
	<form method="get" style="display: inline;">
 | 
						<form method="get" style="display: inline;">
 | 
				
			||||||
		<input type="hidden" name="multiplier" value="2" />
 | 
							<input type="hidden" name="multiplier" value="2" />
 | 
				
			||||||
 | 
							{#each Array.from(currentParams.entries()) as [key, value]}
 | 
				
			||||||
 | 
								{#if key !== 'multiplier'}
 | 
				
			||||||
 | 
									<input type="hidden" name={key} value={value} />
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
							{/each}
 | 
				
			||||||
		<button type="submit" class:selected={multiplier==2} on:click={(e) => handleMultiplierClick(e, 2)}>2x</button>
 | 
							<button type="submit" class:selected={multiplier==2} on:click={(e) => handleMultiplierClick(e, 2)}>2x</button>
 | 
				
			||||||
	</form>
 | 
						</form>
 | 
				
			||||||
	<form method="get" style="display: inline;">
 | 
						<form method="get" style="display: inline;">
 | 
				
			||||||
		<input type="hidden" name="multiplier" value="3" />
 | 
							<input type="hidden" name="multiplier" value="3" />
 | 
				
			||||||
 | 
							{#each Array.from(currentParams.entries()) as [key, value]}
 | 
				
			||||||
 | 
								{#if key !== 'multiplier'}
 | 
				
			||||||
 | 
									<input type="hidden" name={key} value={value} />
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
							{/each}
 | 
				
			||||||
		<button type="submit" class:selected={multiplier==3} on:click={(e) => handleMultiplierClick(e, 3)}>3x</button>
 | 
							<button type="submit" class:selected={multiplier==3} on:click={(e) => handleMultiplierClick(e, 3)}>3x</button>
 | 
				
			||||||
	</form>
 | 
						</form>
 | 
				
			||||||
	<form method="get" style="display: inline;" class="custom-multiplier" on:submit={handleCustomSubmit}>
 | 
						<form method="get" style="display: inline;" class="custom-multiplier" on:submit={handleCustomSubmit}>
 | 
				
			||||||
 | 
							{#each Array.from(currentParams.entries()) as [key, value]}
 | 
				
			||||||
 | 
								{#if key !== 'multiplier'}
 | 
				
			||||||
 | 
									<input type="hidden" name={key} value={value} />
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
							{/each}
 | 
				
			||||||
		<input 
 | 
							<input 
 | 
				
			||||||
			type="text" 
 | 
								type="text" 
 | 
				
			||||||
			name="multiplier" 
 | 
								name="multiplier" 
 | 
				
			||||||
@@ -343,17 +390,18 @@ span
 | 
				
			|||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<h2>Zutaten</h2>
 | 
					<h2>Zutaten</h2>
 | 
				
			||||||
{#each data.ingredients as list}
 | 
					{#each data.ingredients as list, listIndex}
 | 
				
			||||||
{#if list.name}
 | 
					{#if list.name}
 | 
				
			||||||
	<h3>{list.name}</h3>
 | 
						<h3>{list.name}</h3>
 | 
				
			||||||
{/if}
 | 
					{/if}
 | 
				
			||||||
<div class=ingredients_grid>
 | 
					<div class=ingredients_grid>
 | 
				
			||||||
	{#each list.list as item}
 | 
						{#each list.list as item, ingredientIndex}
 | 
				
			||||||
		<div class=amount>{@html adjust_amount(item.amount, multiplier)} {item.unit}</div>
 | 
							<div class=amount>{@html adjust_amount(item.amount, multiplier)} {item.unit}</div>
 | 
				
			||||||
		<div class=name>
 | 
							<div class=name>
 | 
				
			||||||
			{@html item.name.replace("{{multiplier}}", multiplier * item.amount)}
 | 
								{@html item.name.replace("{{multiplier}}", isNaN(parseFloat(item.amount)) ? multiplier : multiplier * parseFloat(item.amount))}
 | 
				
			||||||
			{#if item.name === "Frischhefe" || item.name === "Trockenhefe"}
 | 
								{#if item.name === "Frischhefe" || item.name === "Trockenhefe"}
 | 
				
			||||||
				<HefeSwapper {item} {multiplier} on:toggle={(event) => handleHefeToggle(event, item)} />
 | 
									{@const yeastId = yeastIds[`${listIndex}-${ingredientIndex}`] ?? 0}
 | 
				
			||||||
 | 
									<HefeSwapper {item} {multiplier} {yeastId} />
 | 
				
			||||||
			{/if}
 | 
								{/if}
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	{/each}
 | 
						{/each}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										29
									
								
								src/routes/rezepte/[name]/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/routes/rezepte/[name]/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					import { redirect } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const actions = {
 | 
				
			||||||
 | 
					    swapYeast: async ({ request, url }) => {
 | 
				
			||||||
 | 
					        const formData = await request.formData();
 | 
				
			||||||
 | 
					        const yeastId = parseInt(formData.get('yeastId') as string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Build new URL
 | 
				
			||||||
 | 
					        const newUrl = new URL(url.pathname, url.origin);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Restore all parameters from the form data (they were submitted as currentParam_*)
 | 
				
			||||||
 | 
					        for (const [key, value] of formData.entries()) {
 | 
				
			||||||
 | 
					            if (key.startsWith('currentParam_')) {
 | 
				
			||||||
 | 
					                const paramName = key.substring('currentParam_'.length);
 | 
				
			||||||
 | 
					                newUrl.searchParams.set(paramName, value as string);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Toggle the yeast flag - if it exists, remove it; if not, add it
 | 
				
			||||||
 | 
					        const yeastParam = `y${yeastId}`;
 | 
				
			||||||
 | 
					        if (newUrl.searchParams.has(yeastParam)) {
 | 
				
			||||||
 | 
					            newUrl.searchParams.delete(yeastParam);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            newUrl.searchParams.set(yeastParam, '1');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        throw redirect(303, newUrl.toString());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -22,6 +22,85 @@ export async function load({ fetch, params, url}) {
 | 
				
			|||||||
    // Get multiplier from URL parameters
 | 
					    // Get multiplier from URL parameters
 | 
				
			||||||
    const multiplier = parseFloat(url.searchParams.get('multiplier') || '1');
 | 
					    const multiplier = parseFloat(url.searchParams.get('multiplier') || '1');
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    // Handle yeast swapping from URL parameters based on toggle flags
 | 
				
			||||||
 | 
					    // Look for parameters like y0=1, y1=1 (yeast #0 and #1 are toggled)
 | 
				
			||||||
 | 
					    if (item.ingredients) {
 | 
				
			||||||
 | 
					        let yeastCounter = 0;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Iterate through all ingredients to find yeast and apply conversions
 | 
				
			||||||
 | 
					        for (let listIndex = 0; listIndex < item.ingredients.length; listIndex++) {
 | 
				
			||||||
 | 
					            const list = item.ingredients[listIndex];
 | 
				
			||||||
 | 
					            if (list.list) {
 | 
				
			||||||
 | 
					                for (let ingredientIndex = 0; ingredientIndex < list.list.length; ingredientIndex++) {
 | 
				
			||||||
 | 
					                    const ingredient = list.list[ingredientIndex];
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Check if this is a yeast ingredient
 | 
				
			||||||
 | 
					                    if (ingredient.name === "Frischhefe" || ingredient.name === "Trockenhefe") {
 | 
				
			||||||
 | 
					                        // Check if this yeast should be toggled
 | 
				
			||||||
 | 
					                        const yeastParam = `y${yeastCounter}`;
 | 
				
			||||||
 | 
					                        const isToggled = url.searchParams.has(yeastParam);
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        if (isToggled) {
 | 
				
			||||||
 | 
					                            // Perform yeast conversion from original recipe data
 | 
				
			||||||
 | 
					                            const originalName = ingredient.name;
 | 
				
			||||||
 | 
					                            const originalAmount = parseFloat(ingredient.amount);
 | 
				
			||||||
 | 
					                            const originalUnit = ingredient.unit;
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            let newName: string, newAmount: string, newUnit: string;
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            if (originalName === "Frischhefe") {
 | 
				
			||||||
 | 
					                                // Convert fresh yeast to dry yeast
 | 
				
			||||||
 | 
					                                newName = "Trockenhefe";
 | 
				
			||||||
 | 
					                                
 | 
				
			||||||
 | 
					                                if (originalUnit === "Prise") {
 | 
				
			||||||
 | 
					                                    // "1 Prise Frischhefe" → "1 Prise Trockenhefe"
 | 
				
			||||||
 | 
					                                    newAmount = ingredient.amount;
 | 
				
			||||||
 | 
					                                    newUnit = "Prise";
 | 
				
			||||||
 | 
					                                } else if (originalUnit === "g" && originalAmount === 1) {
 | 
				
			||||||
 | 
					                                    // "1 g Frischhefe" → "1 Prise Trockenhefe"
 | 
				
			||||||
 | 
					                                    newAmount = "1";
 | 
				
			||||||
 | 
					                                    newUnit = "Prise";
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    // Normal conversion: "9 g Frischhefe" → "3 g Trockenhefe" (divide by 3)
 | 
				
			||||||
 | 
					                                    newAmount = (originalAmount / 3).toString();
 | 
				
			||||||
 | 
					                                    newUnit = "g";
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            } else if (originalName === "Trockenhefe") {
 | 
				
			||||||
 | 
					                                // Convert dry yeast to fresh yeast
 | 
				
			||||||
 | 
					                                newName = "Frischhefe";
 | 
				
			||||||
 | 
					                                
 | 
				
			||||||
 | 
					                                if (originalUnit === "Prise") {
 | 
				
			||||||
 | 
					                                    // "1 Prise Trockenhefe" → "1 g Frischhefe"
 | 
				
			||||||
 | 
					                                    newAmount = "1";
 | 
				
			||||||
 | 
					                                    newUnit = "g";
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    // Normal conversion: "1 g Trockenhefe" → "3 g Frischhefe" (multiply by 3)
 | 
				
			||||||
 | 
					                                    newAmount = (originalAmount * 3).toString();
 | 
				
			||||||
 | 
					                                    newUnit = "g";
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                // Fallback
 | 
				
			||||||
 | 
					                                newName = originalName;
 | 
				
			||||||
 | 
					                                newAmount = ingredient.amount;
 | 
				
			||||||
 | 
					                                newUnit = originalUnit;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            // Apply the conversion
 | 
				
			||||||
 | 
					                            item.ingredients[listIndex].list[ingredientIndex] = {
 | 
				
			||||||
 | 
					                                ...item.ingredients[listIndex].list[ingredientIndex],
 | 
				
			||||||
 | 
					                                name: newName,
 | 
				
			||||||
 | 
					                                amount: newAmount,
 | 
				
			||||||
 | 
					                                unit: newUnit
 | 
				
			||||||
 | 
					                            };
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        yeastCounter++;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
        ...item,
 | 
					        ...item,
 | 
				
			||||||
        isFavorite,
 | 
					        isFavorite,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user