feat(ui): Favoriten-Liste, Dismiss-from-Recent, Inline-Rename, Lucide-Icons
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m31s
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m31s
Homepage:
- Neue Sektion "Deine Favoriten" über "Zuletzt hinzugefügt" (alphabetisch
sortiert, lädt wenn Profil aktiv ist; versteckt sonst)
- Jede Karte in "Zuletzt hinzugefügt" hat jetzt oben-rechts ein X-Icon
zum Ausblenden. Das Rezept selbst bleibt in der DB — nur die
Anzeige in der Recent-Liste wird per recipe.hidden_from_recent = 1
unterdrückt. Section versteckt sich, wenn die Liste leer wird.
DB:
- Neue Migration 004_recipe_hidden_from_recent.sql (+Index)
- listFavoritesForProfile in search-local.ts (ORDER BY title NOCASE)
- setRecipeHiddenFromRecent in actions.ts
API:
- GET /api/recipes/favorites?profile_id=X
- PATCH /api/recipes/[id] akzeptiert jetzt title und/oder
hidden_from_recent (Zod-Schema mit refine)
Rezept-Detail:
- Titel ist jetzt inline editierbar: kleines Stift-Icon rechts neben
H1. Click öffnet Input, Enter speichert (PATCH), Escape bricht ab.
Kein location.reload() mehr.
- RecipeView bekommt neuen Snippet-Prop titleSlot für Title-Override.
- Neue Aktionsreihenfolge:
Zeile 1: Favorit | Wunschliste | Drucken
Zeile 2: Heute gekocht | Löschen
(Umbenennen ist jetzt am Titel statt in der Leiste.)
Icons (lucide-svelte, neues Dep):
- Emoji-Icons durch Lucide-SVGs ersetzt auf Startseite, Header,
Rezept-Detail, Wunschliste, Header-Dropdown:
🍽️→Heart/Utensils, ⚙️→Settings, 🥘→CookingPot, 🌐→Globe,
♥/♡→Heart(filled), 🖨→Printer, ✎→Pencil, 🗑→Trash2, ✓→Check,
🍳→ChefHat, X→X
- Header-Brand-Badge auf Mobile behält sein 🍳 (ist im ::after-Pseudo,
Lucide käme da nicht sauber rein).
- SearchLoader-Emojis bleiben — die sind Teil der Animations-Charme.
Tests: 99/99 grün (bestehend), Typecheck 0 Fehler.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { Heart, Trash2, CookingPot } from 'lucide-svelte';
|
||||
import { profileStore } from '$lib/client/profile.svelte';
|
||||
import { confirmAction, alertAction } from '$lib/client/confirm.svelte';
|
||||
import type { WishlistEntry, SortKey } from '$lib/server/wishlist/repository';
|
||||
@@ -82,7 +83,7 @@
|
||||
<p class="muted">Lädt …</p>
|
||||
{:else if entries.length === 0}
|
||||
<section class="empty">
|
||||
<p class="big">🥘</p>
|
||||
<div class="big"><CookingPot size={48} strokeWidth={1.5} /></div>
|
||||
<p>Noch nichts gewünscht.</p>
|
||||
<p class="hint">Öffne ein Rezept und klick dort auf „Auf Wunschliste".</p>
|
||||
</section>
|
||||
@@ -94,7 +95,7 @@
|
||||
{#if resolveImage(e.image_path)}
|
||||
<img src={resolveImage(e.image_path)} alt="" loading="lazy" />
|
||||
{:else}
|
||||
<div class="placeholder">🥘</div>
|
||||
<div class="placeholder"><CookingPot size={32} /></div>
|
||||
{/if}
|
||||
<div class="text">
|
||||
<div class="title">{e.title}</div>
|
||||
@@ -118,12 +119,14 @@
|
||||
aria-label={e.liked_by_me ? 'Unlike' : 'Like'}
|
||||
onclick={() => toggleLike(e)}
|
||||
>
|
||||
{e.liked_by_me ? '♥' : '♡'}
|
||||
<Heart size={18} strokeWidth={2} fill={e.liked_by_me ? 'currentColor' : 'none'} />
|
||||
{#if e.like_count > 0}
|
||||
<span class="count">{e.like_count}</span>
|
||||
{/if}
|
||||
</button>
|
||||
<button class="del" aria-label="Entfernen" onclick={() => remove(e)}>🗑</button>
|
||||
<button class="del" aria-label="Entfernen" onclick={() => remove(e)}>
|
||||
<Trash2 size={18} strokeWidth={2} />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
Reference in New Issue
Block a user