Commit Graph

13 Commits

Author SHA1 Message Date
hsiegeln
60021b879f feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m16s
Schema-Änderung (Migration 005):
- Tabelle wishlist umgestellt auf PK (recipe_id, profile_id)
- wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch",
  also werden alle bestehenden Likes Memberships auf der neuen Tabelle.
- Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen
  verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL).

Repository:
- listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names
  (kommagetrennt), on_my_wishlist für das aktive Profil
- listWishlistProfileIds(recipeId) für den Recipe-Page-Loader
- countWishlistRecipes für das Header-Badge (DISTINCT recipe_id)
- addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id
  als Pflicht

API:
- POST /api/wishlist: profile_id jetzt Pflicht (nullable raus)
- DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry)
- /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet)
- Neu: GET /api/wishlist/count → { count: <distinct recipes> }

UI:
- Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte.
  wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv;
  Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste.
- Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist
  ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert
  die lokale Liste + ruft wishlistStore.refresh.
- Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt-
  wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button
  entfernt — Heart-off reicht jetzt.

Tests (99 → 99, 8 neu geschrieben):
- Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei
  Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
hsiegeln
7cac02de5a 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
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.
2026-04-17 18:57:17 +02:00
hsiegeln
cf31e79fb0 feat(loader): SearchLoader mit wackelnder Pfanne und rotierenden Sprüchen
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 54s
Neue Komponente src/lib/components/SearchLoader.svelte ersetzt die
stumpfen "Suche läuft …"-Zeilen an allen vier Stellen:
- Startseite (scope=local und scope=web)
- Header-Dropdown (size=sm, beide Scopes)

Was passiert:
- Ein Pfannen-Emoji (🍳🥘🍲🍜🥣) wechselt alle 900 ms
- Wobble-Animation kippt es im 1.4-s-Takt hin und her
- Drei Dampf-Punkte steigen zeitversetzt auf und fadeen
- Caption unten rotiert alle 1.8 s durch vier passende Sprüche
  (lokal: "Stöbere im Rezeptbuch …", web: "Schnuppere in fremden
  Küchen …" etc.)

Zwei Size-Varianten: md (Homepage) und sm (Header-Dropdown).
2026-04-17 18:40:38 +02:00
hsiegeln
3cd22544d3 feat(search): mobile header search expands on focus; drop hero button
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 52s
Home:
- "Suchen"-Button entfernt. Die Suche feuert ohnehin debounced beim
  Tippen; der Button war ein Relikt aus dem Submit-Modell. Enter auf
  dem Input löst weiterhin einen Submit aus (geht zur /search-Seite).

Header (< 520 px):
- Sobald das Suchfeld fokussiert wird, wandert das nav-search-wrap
  via :focus-within auf position: absolute und dehnt sich bis zum
  rechten Rand (1 rem Abstand) aus. Die Action-Icons werden dabei
  vom Suchfeld überlagert (z-index 60), sodass der Anwender auf
  engen Displays deutlich mehr Platz zum Tippen hat.
- Bar-Inner bekam position: relative, damit das absolute Ausdehnen
  innerhalb der Header-Zeile greift.
2026-04-17 17:52:51 +02:00
hsiegeln
d693cb422d feat(search): auto web search when no local hits, offer link otherwise
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 55s
Homepage (/):
- Keine lokalen Treffer → automatisch die Internet-Suche auslösen und
  die Ergebnisse als Karten unterhalb der Suche anzeigen.
- Mindestens ein lokaler Treffer → Karten zeigen + darunter ein
  dezenter Link "🌐 Im Internet weitersuchen" (geht zur /search/web
  Vollseite), keine automatische Internet-Suche.

Header-Dropdown (auf Rezept- und Vorschau-Seiten):
- Gleiche Logik: lokale Treffer oben + Fuß-Link; keine lokalen
  Treffer → Internet-Ergebnisse werden direkt im Dropdown angezeigt.
- Abschnittsüberschrift "Keine lokalen Rezepte – aus dem Internet:"
  trennt den Fallback visuell ab.

Race-Safety bleibt bestehen: Query-Vergleich vor jedem State-Write,
sodass spät ankommende Antworten keinen neueren Suchstand überschreiben.
2026-04-17 17:47:26 +02:00
hsiegeln
76110f9841 fix(nav): right-align action icons even when search is hidden
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 53s
Auf Nicht-Rezept-Seiten (Home, Wishlist, Admin) ist das Header-Suchfeld
ausgeblendet. Ohne flex-Spacer rutschten 🍽️/⚙️/Profil direkt neben das
Brand-Badge — besonders auffällig im Mobile-Layout.

margin-left: auto auf .bar-right schiebt die Action-Icons immer an den
rechten Rand, unabhängig davon ob das Suchfeld sichtbar ist.
2026-04-17 17:43:08 +02:00
hsiegeln
d737618312 feat(search): live debounced search with inline hits and header dropdown
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 53s
Homepage (/):
- Tippen > 3 Zeichen + 300 ms Debounce → lokale Suche feuert automatisch
- Treffer erscheinen direkt unter dem Suchfeld als Karten-Grid
- "Zuletzt hinzugefügt" wird ausgeblendet, sobald aktiv gesucht wird
- 0 Treffer + fertig gesucht → Inline-Button "Im Internet weitersuchen"

Header (nur auf /recipes/[id] und /preview):
- Gleiche Debounce-Logik, aber Treffer in einem Dropdown unterm Feld
- Dropdown: kompakte Zeilen mit Thumbnail, Titel, Domain
- Fußzeile des Dropdown: "Im Internet weitersuchen"
- Click-outside und Escape schließen das Dropdown
- afterNavigate setzt Query nach dem Klick auf einen Treffer zurück
- Header-Breite ist jetzt auf 760 px begrenzt (gleich wie Rezept-Content),
  damit die Suchleiste nie breiter wird als das Rezept darunter

Race-Safety: Ein zweites Tippen während laufender Fetch überschreibt
die Ergebnisse des ersten Requests nicht (Query-Vergleich vor Write).
2026-04-17 17:41:10 +02:00
hsiegeln
84655151be feat(nav): search lives in the header on all non-home pages
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 53s
- "← Lokale Suche"-Breadcrumb auf der Web-Suchseite entfernt (überflüssig,
  da die lokale Suche automatisch zur Web-Suche weiterleitet, wenn leer).
- Header-Bar enthält jetzt ein Pill-Suchfeld, das von jeder Unterseite
  aus direkt auf /search?q=... navigiert — kein Zurück mehr nötig,
  wenn man aus einem offenen Rezept weiter sucht.
- Auf der Startseite bleibt die große Hero-Suche; das Header-Feld ist
  dort ausgeblendet, damit es keine doppelte Eingabestelle gibt.
- Auf /search und /search/web spiegelt das Header-Feld die aktuelle
  Query wider, sodass man den Begriff verfeinern kann.
- Mobile < 520px: Brand schrumpft zu einem 🍳-Badge, damit Platz für
  das Suchfeld + Icons bleibt.
2026-04-17 17:31:08 +02:00
1b9928f806 feat(ui): custom confirmation dialog replacing native window.confirm
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 51s
Single reusable dialog with a promise-based API: confirmAction({...})
returns Promise<boolean>. Supports title, optional message body,
confirm/cancel labels, and a 'destructive' flag that paints the confirm
button red.

Accessibility: Escape cancels, Enter confirms, confirm button auto-focus,
role=dialog + aria-labelledby, backdrop click = cancel.

Rolled out to: recipe delete, domain remove, profile delete, wishlist
remove. Native confirm() is gone from the codebase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 17:15:21 +02:00
3b1950713f feat(ui): wishlist page, recipe toggle button, header link
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 54s
- /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) <noreply@anthropic.com>
2026-04-17 17:08:22 +02:00
4d7783dd8b feat(ui): add admin area (domains, profiles, backup) with gear link in header
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 15:35:20 +02:00
4d5e0aa963 feat(ui): add layout with profile bar, home, search, recipe pages
- Homepage with search and recent recipes
- Search page listing local hits (FTS5)
- Recipe page with ratings, favorites, cooking log, comments
- Wake-Lock on recipe view for mobile kitchen use

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 15:28:22 +02:00
c4d3163a49 feat(scaffold): init SvelteKit + TypeScript project
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 15:00:58 +02:00