From 3b1950713f7423664ee3f7ea92195981a2bde7f1 Mon Sep 17 00:00:00 2001 From: Hendrik Date: Fri, 17 Apr 2026 17:08:22 +0200 Subject: [PATCH] feat(ui): wishlist page, recipe toggle button, header link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /wishlist renders cards with avatar-badge of who added it, like count, heart toggle for active profile, delete button. Sort dropdown switches between popular / newest / oldest. - /recipes/[id] gets 'Auf Wunschliste (setzen)' button alongside favorite. - Layout header shows 🍽️ link to /wishlist next to the admin ⚙️. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/routes/+layout.svelte | 7 +- src/routes/recipes/[id]/+page.svelte | 35 ++++ src/routes/wishlist/+page.svelte | 259 +++++++++++++++++++++++++++ 3 files changed, 298 insertions(+), 3 deletions(-) create mode 100644 src/routes/wishlist/+page.svelte diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index e1e042f..5284db5 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -13,7 +13,8 @@
Kochwas
@@ -59,7 +60,7 @@ align-items: center; gap: 0.5rem; } - .admin-link { + .nav-link { display: inline-flex; align-items: center; justify-content: center; @@ -69,7 +70,7 @@ text-decoration: none; font-size: 1.15rem; } - .admin-link:hover { + .nav-link:hover { background: #f4f8f5; } main { diff --git a/src/routes/recipes/[id]/+page.svelte b/src/routes/recipes/[id]/+page.svelte index 60cde78..be6f4de 100644 --- a/src/routes/recipes/[id]/+page.svelte +++ b/src/routes/recipes/[id]/+page.svelte @@ -13,6 +13,7 @@ let comments = $state([]); let cookingLog = $state([]); let isFav = $state(false); + let onWishlist = $state(false); let newComment = $state(''); $effect(() => { @@ -124,6 +125,31 @@ location.reload(); } + async function toggleWishlist() { + if (onWishlist) { + await fetch(`/api/wishlist/${data.recipe.id}`, { method: 'DELETE' }); + onWishlist = false; + } else { + await fetch('/api/wishlist', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + recipe_id: data.recipe.id, + profile_id: profileStore.active?.id ?? null + }) + }); + onWishlist = true; + } + } + + async function refreshWishlistState() { + // No dedicated GET for a single entry; scan the list and check. + const res = await fetch('/api/wishlist?sort=newest'); + if (!res.ok) return; + const body = await res.json(); + onWishlist = body.entries.some((e: { recipe_id: number }) => e.recipe_id === data.recipe.id); + } + // Wake-Lock let wakeLock: WakeLockSentinel | null = null; async function requestWakeLock() { @@ -139,6 +165,7 @@ onMount(() => { void requestWakeLock(); void checkFavorite(); + void refreshWishlistState(); const onVisibility = () => { if (document.visibilityState === 'visible' && !wakeLock) void requestWakeLock(); }; @@ -171,6 +198,9 @@ + @@ -267,6 +297,11 @@ border-color: #f1b4b4; background: #fdf3f3; } + .btn.wish { + color: #2b6a3d; + border-color: #b7d6c2; + background: #eaf4ed; + } .btn.primary { background: #2b6a3d; color: white; diff --git a/src/routes/wishlist/+page.svelte b/src/routes/wishlist/+page.svelte new file mode 100644 index 0000000..6b24569 --- /dev/null +++ b/src/routes/wishlist/+page.svelte @@ -0,0 +1,259 @@ + + +
+

Wunschliste

+

Das wollen wir bald mal essen.

+
+ +
+ +
+ +{#if loading} +

Lädt …

+{:else if entries.length === 0} +
+

🥘

+

Noch nichts gewünscht.

+

Öffne ein Rezept und klick dort auf „Auf Wunschliste".

+
+{:else} + +{/if} + +