# Mealplanner — Design Spec **Datum:** 2026-04-14 **Domain:** `essen.kuns.dev` **Repo:** `kuns/mealplanner` ## Zusammenfassung Webapp zur Wochenplanung von Mahlzeiten. Generiert automatisch einen Wochenplan (7 Tage, je 1 Hauptmahlzeit), der bearbeitbar ist. Aus dem Plan wird eine intelligente Einkaufsliste generiert (Zutaten zusammengefasst, abhakbar). Rezepte kommen aus einer externen API + eigene Rezepte. ## Anforderungen - **Haushalt:** Feste Personenzahl in Einstellungen (Standard: 2) - **Mahlzeiten:** 1 pro Tag (Abendessen) - **Wochenplan:** Auto-generiert, voll editierbar (Gericht austauschen, neu generieren) - **Rezepte:** Externe DB (TheMealDB, kostenlos, kein API-Key nötig) + eigene Rezepte - **Einkaufsliste:** Zutaten aggregiert, nach Kategorie gruppiert, abhakbar - **Ernährungspräferenzen:** Keine - **Auth:** Zitadel (manueller OIDC PKCE Flow, kein oidc-client-ts) ## Architektur ``` Vue 3 SPA (Tailwind 4, Bun, Vite) ↓ REST API (/api/*) .NET 8 Web API ├── Auth (Zitadel JWT Bearer + NuGet-Paket) ├── RecipeService (TheMealDB Client + eigene Rezepte CRUD) ├── MealPlanService (Generierung + CRUD) └── ShoppingListService (Aggregation aus Wochenplan) ↓ Shared PostgreSQL (DB: mealplanner) ``` ### Projektstruktur ``` mealplanner/ ├── backend/ │ ├── Controllers/ │ │ ├── RecipeController.cs │ │ ├── MealPlanController.cs │ │ ├── ShoppingListController.cs │ │ └── SettingsController.cs │ ├── Models/ │ │ ├── Recipe.cs │ │ ├── MealPlan.cs │ │ ├── MealPlanEntry.cs │ │ ├── ShoppingItem.cs │ │ └── UserSettings.cs │ ├── Services/ │ │ ├── TheMealDbClient.cs │ │ ├── RecipeService.cs │ │ ├── MealPlanService.cs │ │ └── ShoppingListService.cs │ ├── Data/ │ │ └── AppDbContext.cs │ ├── Program.cs │ └── backend.csproj ├── frontend/ │ ├── src/ │ │ ├── views/ │ │ │ ├── WeekPlanView.vue (Hauptansicht) │ │ │ ├── ShoppingListView.vue │ │ │ ├── RecipesView.vue (Eigene Rezepte verwalten) │ │ │ └── SettingsView.vue │ │ ├── components/ │ │ │ ├── MealCard.vue │ │ │ ├── RecipeDetail.vue │ │ │ ├── ShoppingItem.vue │ │ │ └── WeekDay.vue │ │ ├── composables/ │ │ │ └── useApi.ts │ │ ├── auth.ts (Manueller OIDC PKCE Flow) │ │ ├── router.ts │ │ ├── App.vue │ │ └── main.ts │ ├── index.html │ └── package.json ├── Dockerfile ├── docker-compose.yml └── docs/ ``` ## Datenmodell ### Recipe | Feld | Typ | Beschreibung | |------|-----|-------------| | id | UUID | PK | | user_id | string | Zitadel User ID (null = extern) | | external_id | string? | TheMealDB ID | | title | string | Name des Gerichts | | instructions | text | Zubereitungsanleitung | | image_url | string? | Bild-URL | | source | enum | `own`, `themealdb` | | created_at | timestamp | | ### RecipeIngredient | Feld | Typ | Beschreibung | |------|-----|-------------| | id | UUID | PK | | recipe_id | UUID | FK → Recipe | | name | string | Zutat | | amount | decimal? | Menge | | unit | string? | Einheit (g, ml, Stück, etc.) | | category | string? | Gemüse, Fleisch, Milch, etc. | ### MealPlan | Feld | Typ | Beschreibung | |------|-----|-------------| | id | UUID | PK | | user_id | string | Zitadel User ID | | week_start | date | Montag der Woche | | created_at | timestamp | | ### MealPlanEntry | Feld | Typ | Beschreibung | |------|-----|-------------| | id | UUID | PK | | meal_plan_id | UUID | FK → MealPlan | | date | date | Tag | | recipe_id | UUID | FK → Recipe | ### UserSettings | Feld | Typ | Beschreibung | |------|-----|-------------| | user_id | string | PK, Zitadel User ID | | household_size | int | Anzahl Personen (Default: 2) | ## API Endpoints ### Wochenplan - `POST /api/mealplan/generate` — Neuen Wochenplan generieren (ab nächstem Montag) - `GET /api/mealplan/current` — Aktuellen Wochenplan laden - `GET /api/mealplan/{weekStart}` — Plan für bestimmte Woche - `PUT /api/mealplan/{id}/entry/{date}` — Gericht für einen Tag austauschen - `POST /api/mealplan/{id}/entry/{date}/reroll` — Neues zufälliges Gericht für Tag ### Rezepte - `GET /api/recipes` — Eigene Rezepte auflisten - `GET /api/recipes/{id}` — Rezeptdetails - `POST /api/recipes` — Eigenes Rezept anlegen - `PUT /api/recipes/{id}` — Eigenes Rezept bearbeiten - `DELETE /api/recipes/{id}` — Eigenes Rezept löschen - `GET /api/recipes/search?q=` — Suche (eigene + TheMealDB) ### Einkaufsliste - `GET /api/shoppinglist/{mealPlanId}` — Aggregierte Einkaufsliste für Wochenplan - `PUT /api/shoppinglist/{mealPlanId}/check/{itemId}` — Item abhaken/enthaken ### Einstellungen - `GET /api/settings` — User-Einstellungen laden - `PUT /api/settings` — User-Einstellungen speichern ## Wochenplan-Generierung 1. Alle verfügbaren Rezepte sammeln (eigene + TheMealDB Random) 2. 7 zufällige, unterschiedliche Gerichte auswählen 3. Montag bis Sonntag zuweisen 4. In DB speichern **Reroll:** Einzelnes Gericht durch neues zufälliges ersetzen (nicht doppelt im Plan). ## Einkaufslisten-Logik 1. Alle Rezepte des Wochenplans laden 2. Zutaten sammeln, gleiche Zutaten zusammenfassen (Name-Match, Mengen addieren) 3. Mengen × Haushaltsgröße skalieren 4. Nach Kategorie gruppieren (Gemüse, Fleisch, Milchprodukte, Gewürze, Sonstiges) 5. Check-Status pro User in DB persistieren ## Frontend Design **Theme:** Dark Mode (wie task-scheduler-v2) - **Hintergrund:** zinc-950 (#09090b) - **Cards:** zinc-900 (#18181b) - **Borders:** zinc-800 (#27272a) - **Primary Text:** zinc-100 (#f5f5f5) - **Secondary Text:** zinc-500 (#71717a) - **Accent:** emerald (#10b981) - **Accent Hover:** #059669 ### Hauptansichten 1. **Wochenplan** (Startseite) — 7 Cards (Mo-So), je mit Gerichtname + Bild. Buttons: "Austauschen" (Rezeptsuche), "Neu würfeln" (Random). Button oben: "Neue Woche generieren". 2. **Einkaufsliste** — Gruppiert nach Kategorie, Checkboxen zum Abhaken. Badge mit Anzahl offener Items. 3. **Rezepte** — Eigene Rezepte CRUD. Karten-Ansicht. Formular zum Anlegen/Bearbeiten mit dynamischer Zutatenliste. 4. **Einstellungen** — Haushaltsgröße anpassen. ### Navigation Sidebar oder Bottom-Nav mit 4 Tabs: Wochenplan, Einkaufsliste, Rezepte, Einstellungen. ## Deployment - **Coolify** via Git Push (wie alle anderen Projekte) - **Dockerfile:** Multi-stage (Bun Frontend Build → .NET Publish → Runtime) - **DB:** `mealplanner` auf Shared PostgreSQL - **Domain:** `essen.kuns.dev` - **Env-Vars in Coolify:** `DATABASE_URL`, `ZITADEL_ISSUER`, `ZITADEL_CLIENT_ID` ## Externe Abhängigkeiten - **TheMealDB API** (`www.themealdb.com/api/json/v1/1/`) — kostenlos, kein Key nötig - `random.php` — Zufälliges Rezept - `search.php?s=` — Suche - `lookup.php?i=` — Details per ID - Rezepte auf Englisch — akzeptabel für MVP