Commit Graph

12 Commits

Author SHA1 Message Date
hsiegeln
02b9cdbc68 refactor(client): requireProfile(message?) + Wunschliste migriert (Item G)
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m21s
Option B aus dem Roadmap-Plan. requireProfile bekommt einen optionalen
message-Parameter mit dem bisherigen Text als Default — die 5 Bestands-
Aufrufe aendern sich nicht, die Wunschliste nutzt die Custom-Message
„um mitzuwünschen" sauber ueber den Helper statt mit dupliziertem
alertAction-Block.

Netto: -3 Zeilen in wishlist/+page.svelte, eine Duplikation weniger,
Helper dokumentiert jetzt explizit den Message-Override-Use-Case.

Gate: svelte-check 0 Warnings, 184/184 Tests, Wunschliste zeigt
korrekte Message beim Klick ohne Profil.

Refs docs/superpowers/plans/2026-04-19-post-review-roadmap.md Item G.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:45:00 +02:00
hsiegeln
5a291a53dd refactor(ui): --pill-radius CSS-Variable (Item F)
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m22s
border-radius: 999px war 15x im CSS dupliziert. Ausgelagert als
:root --pill-radius Variable im globalen :root-Block in +layout.svelte,
Call-Sites auf var(--pill-radius) umgestellt.

Bewusst NICHT angefasst (plan war "nur Werte die mehrfach vorkommen"):
- z-index: 10 Distinct Values in 14 Sites, bilden ein implizites
  Layer-System. Konsolidieren = behavior-change-Risiko ohne konkreten
  Nutzen. Wenn kuenftig einheitliche Modal-/Popover-Layer noetig,
  separate Phase.
- setTimeout(): 3 Sites, jeder mit eigener Semantik (Debounce/Print/
  Spinner). Kein DRY-Nutzen durch Extraktion.

Gate: svelte-check 0 Warnings, 184/184 Tests, Build clean, kein
sichtbarer Unterschied (einzige Aenderung: selber Wert ueber Variable).

Refs docs/superpowers/plans/2026-04-19-post-review-roadmap.md Item F.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:43:19 +02:00
hsiegeln
3906781c4e feat(pwa): Schreib-Aktionen zeigen Offline-Toast statt stillem Fail
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m18s
Neuer Helper requireOnline(action) prüft vor jedem Schreib-Fetch
den Online-Status. Offline: ein Toast erscheint ("Die Aktion braucht
eine Internet-Verbindung."), Aktion bricht sauber ab. Der Button-
State bleibt unverändert (kein optimistisches Update, das gleich
wieder zurückgedreht werden müsste).

Eingebaut in Rezept-Detail (8 Handler), Register (2), Wunschliste
(2), Admin Domains/Profile/Backup, Home-Dismiss.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:54:03 +02:00
hsiegeln
7233cc3a13 style(wishlist): Chip-Label "Beliebteste" → "Meist gewünscht"
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m17s
"Beliebteste" ist mehrdeutig (Sterne? Favoriten? Wünsche?). Der
Sort-Key popular sortiert nach wanted_by_count DESC, also der
Anzahl Profile mit dem Rezept auf ihrer Wunschliste. Das Label
macht das jetzt eindeutig.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:12:22 +02:00
hsiegeln
297281e201 style(wishlist): Sortierung als Pill-Chips wie auf der Startseite
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m16s
Das native <select> fiel stilistisch aus dem App-Bild. Jetzt
identisch zur "Alle Rezepte"-Sortierung auf der Startseite: drei
grüne Pill-Chips (Beliebteste / Neueste / Älteste), aktive Pille
invertiert. Verhalten gleich, nur die Optik angepasst.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:10:52 +02:00
hsiegeln
361164febd style(wishlist): Herz durch Utensils-Icon ersetzt
Das Herz auf der Wunschliste doppelte sich semantisch mit dem
Favoriten-Herz in der Rezeptansicht. Jetzt deckungsgleich mit dem
Wunschlisten-Icon in der Rezept-Action-Bar: Utensils. Aktiv-Farbe
von rot auf das Kochwas-Grün gewechselt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:06:07 +02:00
hsiegeln
5e7e37cc3c feat(wishlist): "für alle löschen" + Badge refresht auf jede Navigation
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m14s
1) Trash-Button auf Wunschliste wieder da. Im Gegensatz zum Heart
   entfernt er den Eintrag NICHT nur für das aktive Profil, sondern
   löscht alle Memberships auf diesem Rezept. Bestätigungsdialog macht
   das explizit ("wird für alle Profile aus der Wunschliste gestrichen").

   - repository.ts: neue Funktion removeFromWishlistForAll(recipeId)
   - DELETE /api/wishlist/:id?all=true → family-wide
     DELETE /api/wishlist/:id?profile_id=X → nur mein Eintrag
   - UI: zwei Action-Buttons untereinander (Heart, Trash)

2) wishlistStore.refresh() läuft jetzt in afterNavigate des Root-Layouts.
   Vorher wurde der Badge nur aktualisiert, wenn derselbe Tab die Aktion
   ausgelöst hat. Wenn ein anderer Tab / anderes Gerät etwas ändert,
   bleibt der Badge stale bis zum nächsten Full-Reload. Mit afterNavigate
   reicht eine Client-Navigation, um ihn zu aktualisieren — was deutlich
   näher an dem liegt, was der User erwartet.
2026-04-17 19:29:00 +02:00
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
4f7c76c908 feat(ui): custom dialog replaces all remaining window.alert() calls
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 53s
alertAction({title, message}) returns Promise<void> and renders the
same ConfirmDialog with infoOnly:true — single OK button, no Abbrechen.
Replaces:
- 'Bitte Profil wählen.' (recipe rating / favorite / cooked / comment)
- 'Bitte Profil wählen, um zu liken.' (wishlist)
- 'Profil konnte nicht angelegt werden' (ProfileSwitcher)
- 'Umbenennen fehlgeschlagen' (admin/profiles)
- 'Speichern fehlgeschlagen' (preview)

No window.alert() or window.confirm() left in the codebase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 17:23:07 +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