v2.1.2026 — PostgreSQL, Auth, Household, Shopping Smart-Add, Docker
Backend: - SQLite → PostgreSQL (pg_trgm search, async services) - All services rewritten to async with pg Pool - Data imported (50 recipes, 8 categories) - better-sqlite3 removed Frontend: - ProfilePage complete (edit profile, change password, no more stubs) - HouseholdCard (create, join via code, manage members, leave) - Shopping scope toggle (personal/household) - IngredientPickerModal (smart add with basics filter) - Auth token auto-attached to all API calls (token.ts) - Removed PlaceholderPage Infrastructure: - Docker Compose (backend + frontend + postgres) - Dockerfile for backend (node:22-alpine + tsx) - Dockerfile for frontend (vite build + nginx) - nginx.conf with API proxy + SPA fallback - .env.example for production secrets Spec: - AUTH-V2-SPEC updated: household join flow, manual shopping items
This commit is contained in:
@@ -342,13 +342,18 @@ ALTER TABLE recipes ADD COLUMN created_by UUID REFERENCES users(id);
|
||||
### 3.2 Profile Pages
|
||||
|
||||
#### /profile
|
||||
- **Layout:** Tabbed Interface (Profil, Haushalt)
|
||||
- **Profil Tab:**
|
||||
- **Layout:** Card-basiert, vertikal gestapelt (kein Tab-Interface)
|
||||
- **Profil-Karte:**
|
||||
- Avatar (Upload oder URL)
|
||||
- Display Name (inline editierbar)
|
||||
- E-Mail (nicht editierbar, mit "Ändern" Link für v2.1)
|
||||
- "Passwort ändern" Button
|
||||
- **Mobile:** Große Avatar-Anzeige, Touch-freundliche Edit-Buttons
|
||||
- **Haushalt-Karte:** (immer sichtbar, direkt unter Profil)
|
||||
- **Kein Haushalt:** Card mit 🏠 Icon, "Haushalt erstellen" + "Beitreten" Buttons
|
||||
- **In Haushalt:** Haushalt-Name, Mitglieder-Avatare, "Verwalten" Button → `/profile/household`
|
||||
- Visuell prominent — nicht versteckt in einem Tab!
|
||||
- **Quick-Actions:** Logout-Button unten
|
||||
- **Mobile:** Große Avatar-Anzeige, Touch-freundliche Edit-Buttons, 44px Targets
|
||||
|
||||
#### /profile/edit
|
||||
- **Modal oder Fullscreen (Mobile):** Profil bearbeiten
|
||||
@@ -475,10 +480,36 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
||||
|
||||
### 5.2 Haushalt beitreten
|
||||
|
||||
1. User erhält Einladungscode (per Link, QR-Code oder Text)
|
||||
2. User klickt "Haushalt beitreten" und gibt Code ein
|
||||
3. Backend validiert Code und fügt User als Member hinzu
|
||||
4. Einkaufsliste wird automatisch geteilt
|
||||
#### Flow
|
||||
1. User erhält Einladungscode (per Link, QR-Code oder manuell)
|
||||
2. In `/profile/household` → Button "Haushalt beitreten"
|
||||
3. **Eingabe:** 6-stelliger Code (uppercase, alphanumerisch, z.B. `COOK2A`)
|
||||
4. Backend validiert Code → fügt User als `member` hinzu
|
||||
5. Erfolg: Konfetti 🎉 + Toast "Willkommen bei [Haushalt-Name]!"
|
||||
6. Einkaufsliste zeigt ab sofort Haushalt-Items
|
||||
|
||||
#### Deep Link Support
|
||||
- URL-Format: `https://luna.supertoll.xyz/join/{invite_code}`
|
||||
- Wenn eingeloggt → direkt beitreten (mit Bestätigungs-Dialog)
|
||||
- Wenn nicht eingeloggt → Redirect zu `/login?redirect=/join/{code}`
|
||||
- Nach Login → automatisch Join-Flow fortsetzen
|
||||
|
||||
#### QR-Code
|
||||
- Owner kann QR-Code generieren (enthält Deep Link)
|
||||
- Share-Button: QR als Bild teilen oder Code kopieren
|
||||
- Library: `qrcode.react` (lightweight)
|
||||
|
||||
#### Fehlerbehandlung
|
||||
- `INVALID_INVITE_CODE` → "Code ungültig oder abgelaufen"
|
||||
- `ALREADY_IN_HOUSEHOLD` → "Du bist bereits in einem Haushalt. Zuerst verlassen?"
|
||||
- `HOUSEHOLD_FULL` → "Haushalt hat maximale Mitgliederzahl erreicht" (Limit: 10)
|
||||
- Netzwerkfehler → Retry-Button
|
||||
|
||||
#### Haushalt verlassen
|
||||
- Member können jederzeit verlassen (Bestätigungs-Dialog)
|
||||
- Owner kann nur verlassen wenn ein anderer Member zum Owner gemacht wird
|
||||
- Letzter Member → Haushalt wird gelöscht
|
||||
- Nach Verlassen: eigene Shopping-Items bleiben privat erhalten
|
||||
|
||||
### 5.3 Gemeinsame Einkaufsliste
|
||||
|
||||
@@ -493,6 +524,109 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
||||
- **Filter:** "Alle", "Haushalt", "Privat" Tabs in Shopping Liste
|
||||
- **Mobile:** Swipe-Actions: "Als privat markieren" / "Mit Haushalt teilen"
|
||||
|
||||
### 5.4 Smarte Mengenabfrage beim Rezept-Einkauf
|
||||
|
||||
Aktuell werden beim "Zur Einkaufsliste hinzufügen" alle Zutaten 1:1 übernommen. Aber oft hat man schon Mehl, Eier, Zucker etc. daheim. Statt blind alles draufzupacken → User fragen was noch fehlt.
|
||||
|
||||
#### Flow: "Was brauchst du noch?"
|
||||
1. User klickt "Zutaten zur Einkaufsliste" auf der Rezeptseite
|
||||
2. **Modal/Sheet öffnet sich** mit allen Zutaten als Checkliste
|
||||
3. Jede Zutat hat:
|
||||
- ☑️ Checkbox (Standard: alle an)
|
||||
- Name + Menge aus Rezept
|
||||
- Optional: Menge anpassen (z.B. "hab noch 200g, brauch nur 300g")
|
||||
4. User deaktiviert was schon da ist
|
||||
5. Button: "X Artikel hinzufügen" (zeigt Anzahl der aktiven)
|
||||
6. Nur ausgewählte Zutaten landen auf der Liste
|
||||
|
||||
#### Quick-Actions im Modal
|
||||
- **"Alles"** — alle Checkboxen an (default)
|
||||
- **"Basics abwählen"** — typische Vorrats-Zutaten automatisch deaktivieren (Salz, Pfeffer, Öl, Wasser)
|
||||
- **"Nichts"** — alle aus, manuell auswählen
|
||||
|
||||
#### Basics-Liste (konfigurierbar pro User/Haushalt)
|
||||
Standard-Basics die man meistens daheim hat:
|
||||
- Salz, Pfeffer, Zucker, Mehl, Öl, Wasser, Butter, Eier
|
||||
- User kann in Settings eigene Basics definieren
|
||||
- Passt zu Luna: Mehl ✅ Zucker ✅ Eier ✅ Backpulver ✅ (Butter ❌ muss immer drauf 😄)
|
||||
|
||||
#### Späterer Ausbau (v2+: Vorratskammer/Pantry)
|
||||
- Automatischer Abgleich mit Vorratskammer
|
||||
- "Hab ich schon" wird automatisch vorausgewählt
|
||||
- Fehlmengen werden berechnet (Rezept braucht 500g, hast 200g → 300g auf Liste)
|
||||
|
||||
### 5.5 Manuelle Artikel zur Einkaufsliste hinzufügen
|
||||
|
||||
Aktuell kommen Shopping-Items nur aus Rezepten. User sollen auch eigene Artikel hinzufügen können (z.B. "Klopapier", "Spülmittel", Zutaten ohne Rezept).
|
||||
|
||||
#### API
|
||||
|
||||
##### POST /api/shopping/manual
|
||||
```json
|
||||
// Request
|
||||
{
|
||||
"name": "Klopapier",
|
||||
"amount": "1",
|
||||
"unit": "Packung",
|
||||
"private": false
|
||||
}
|
||||
|
||||
// Response (201 Created)
|
||||
{
|
||||
"success": true,
|
||||
"item": {
|
||||
"id": "uuid",
|
||||
"name": "Klopapier",
|
||||
"amount": "1",
|
||||
"unit": "Packung",
|
||||
"checked": false,
|
||||
"recipe_id": null,
|
||||
"user_id": "user-uuid",
|
||||
"household_id": "household-uuid",
|
||||
"created_at": "2026-02-18T16:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### DB-Erweiterung
|
||||
```sql
|
||||
-- recipe_id wird NULLABLE (ist es vermutlich schon)
|
||||
-- Manuelle Items haben recipe_id = NULL
|
||||
-- Optional: source-Feld um Herkunft zu unterscheiden
|
||||
ALTER TABLE shopping_items ADD COLUMN source TEXT DEFAULT 'recipe'
|
||||
CHECK (source IN ('recipe', 'manual'));
|
||||
```
|
||||
|
||||
#### Frontend UI
|
||||
|
||||
##### Eingabefeld (oben in der Einkaufsliste)
|
||||
- **Sticky Input-Bar** oben auf der Shopping-Page
|
||||
- Textfeld mit Placeholder "Artikel hinzufügen..." + ➕ Button
|
||||
- **Smart Parsing:** "2 Liter Milch" → amount: "2", unit: "Liter", name: "Milch"
|
||||
- **Autocomplete:** Vorschläge aus bisherigen Items (häufig gekaufte)
|
||||
- Enter oder ➕ fügt hinzu, Feld wird geleert
|
||||
- Standard: Haushalt-Item (wenn in Haushalt), sonst privat
|
||||
|
||||
##### Smart Parsing Regeln
|
||||
```
|
||||
"Milch" → name: "Milch", amount: null, unit: null
|
||||
"2 Milch" → name: "Milch", amount: "2", unit: null
|
||||
"2 Liter Milch" → name: "Milch", amount: "2", unit: "Liter"
|
||||
"500g Mehl" → name: "Mehl", amount: "500", unit: "g"
|
||||
"1 Packung Butter" → name: "Butter", amount: "1", unit: "Packung"
|
||||
```
|
||||
|
||||
##### Visuelle Unterscheidung
|
||||
- Rezept-Items: normaler Style + Rezept-Name als Subtitle
|
||||
- Manuelle Items: leicht anderer Style (z.B. 📝 Icon oder kursiver Subtitle "Manuell hinzugefügt")
|
||||
- Haushalt-Items: 🏠 Badge
|
||||
- Private Items: 🔒 Badge
|
||||
|
||||
##### Quick-Add Vorschläge
|
||||
- Unter dem Input: Chips mit häufig hinzugefügten Artikeln
|
||||
- Max 5 Vorschläge, basierend auf History
|
||||
- Tap = sofort hinzufügen
|
||||
|
||||
### 5.4 Persönliche Favoriten
|
||||
|
||||
- Favoriten sind immer pro User (`user_favorites` Tabelle)
|
||||
|
||||
Reference in New Issue
Block a user