# Canteen Menu – Speisekarte für die Betriebskantine Digitale Speisekarte für eine Firmenkantine (ca. 250 Mitarbeiter) mit öffentlicher Wochenansicht, Kantinen-Bereich zur Pflege der Gerichte und Admin-Bereich für Benutzerverwaltung und Branding. **Stack:** Next.js 16 (App Router, TypeScript, Tailwind CSS) · Supabase (PostgreSQL, Auth inkl. Passkeys, Storage, Row Level Security) Änderungen je Version stehen in [CHANGELOG.md](CHANGELOG.md), Git-Tags markieren die jeweiligen Versionsstände (`git tag`). ## Funktionen | Bereich | Funktionen | | --- | --- | | Öffentlich (ohne Login) | Wochenplan Mo–Fr mit den Bereichen Brotzeit (09:00–10:00), Mittagessen, Mittagessen vegetarisch und Suppe (je 11:30–13:30); Navigation vorherige/nächste Woche/Heute; auf schmalen Bildschirmen umschaltbare Tages-/Wochenansicht (Tag ist Standard); Logo zentriert über der Karte; Allergen-/Zusatzstoff-Kürzel je Gericht, Legende unterhalb der Karte; Sprachumschalter Deutsch/Englisch/Polnisch; Rezeptvorschläge einreichen | | Kantinen-Team (`/intern`) | Gerichte anlegen/bearbeiten/löschen (inkl. Live-Suche, Mehrfachauswahl zum Batch-Löschen, englischem/polnischem Namen mit automatischer DeepL-Übersetzung) mit Allergenen/Zusatzstoffen je Gericht; Gerichte Tagen/Bereichen zuweisen (mehrere pro Zelle) und wieder entfernen; globale Allergen-/Zusatzstofflisten pflegen (ebenfalls mehrsprachig); eingereichte Rezeptvorschläge sichten, übernehmen oder ablehnen; eigenes Konto verwalten (Name, E-Mail, Passwort, Passkeys, Passwort-Anmeldung an/aus) | | Admin (`/admin`) | Kantinenmitarbeiter anlegen/bearbeiten/löschen (inkl. Passwort-Reset mit Wiederholung und Rollenvergabe); Farbgebung der Speisekarte anpassen; Logo und Favicon hochladen/entfernen | Anmeldung mit E-Mail + Passwort oder alternativ per **Passkey** (Registrierung im eigenen Konto nach dem ersten Login). Die Passwort-Anmeldung kann pro Konto deaktiviert werden, sobald mindestens ein Passkey registriert ist. Die gesamte Oberfläche ist auf Deutsch, Englisch und Polnisch verfügbar (Sprachumschalter, Cookie-basiert). Gerichte, Bereiche, Allergene und Zusatzstoffe lassen sich zusätzlich auf Englisch/Polnisch benennen – mit optionaler automatischer Übersetzung über DeepL (siehe `DEEPL_API_KEY` unten) und jederzeit manuell korrigierbar. ## Projektstruktur ``` app/ Next.js-Routen page.tsx Öffentliche Speisekarte login/ Login (Passwort + Passkey) vorschlag/ Öffentliches Rezeptvorschlag-Formular intern/ Kantinen-Bereich (Rolle: staff oder admin) vorschlaege/ Rezeptvorschläge sichten/übernehmen admin/ Verwaltung (Rolle: admin) api/admin/users/ Benutzerverwaltung (serverseitig, Service-Role-Key) api/translate/ DeepL-Anbindung (serverseitig) api/locale/ Sprach-Cookie setzen components/ Client-Komponenten (WeekEditor, DishManager, …) components/i18n/ Sprachkontext, Sprachumschalter lib/i18n/ Wörterbücher (de/en/pl), Locale-Helfer lib/supabase/ Supabase-Clients (Browser / Server / Admin) lib/ Typen, Datums-/Formatierungshelfer proxy.ts Session-Refresh + Schutz von /intern und /admin supabase/migrations/ Schema & Seed-Daten (Referenz/Doku) supabase/local/ Skripte + Dumps für die lokale PostgreSQL-Kopie scripts/ Hilfsskripte (DB-Migration, lokaler Sync) ``` ## Entwicklung Voraussetzung in beiden Varianten: `.env.local` mit den Werten aus `.env.example` (liegt lokal bereits vor und ist per `.gitignore` vom Repo ausgeschlossen). **Variante A – Docker (nichts vorinstallieren außer Docker):** ```bash docker compose --env-file .env.local --profile dev up dev # http://localhost:3000 (Hot-Reload, Quellcode wird gemountet) ``` **Variante B – lokal installiertes Node.js:** ```bash npm install npm run dev # http://localhost:3000 (bindet an 0.0.0.0, auch im LAN erreichbar) ``` ## Umgebungsvariablen (Live-Betrieb) Beim Webserver/Hoster müssen genau diese drei Variablen hinterlegt werden (Werte: Supabase-Dashboard → Project Settings → API Keys): | Variable | Bedeutung | Geheim? | | --- | --- | --- | | `NEXT_PUBLIC_SUPABASE_URL` | URL des Supabase-Projekts, z. B. `https://hvzikwjoyhoakedlywhj.supabase.co` | nein | | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Öffentlicher anon-Key (RLS schützt die Daten) | nein | | `SUPABASE_SERVICE_ROLE_KEY` | Service-Role-Key für die Benutzerverwaltung | **ja – niemals im Browser/Repo!** | | `APP_URL` | Öffentliche URL der App, z. B. `https://menu.example.de` (Origin-Check hinter Reverse-Proxy; zugleich Passkey-Domain) | nein | | `SITE_TITLE` | Titel im Browser-Tab und Überschrift ohne Logo, Default `Canteen Menu` | nein | | `DEEPL_API_KEY` | Optional: aktiviert „Automatisch übersetzen“ für Gerichte/Allergene/Zusatzstoffe (DeepL, kostenloser Free-Tier). Ohne Key funktioniert alles weiter, nur automatisch übersetzen ist deaktiviert | **ja – niemals im Browser/Repo!** | Build & Start auf dem Server – **mit Docker** (empfohlen, Node.js und PostgreSQL müssen nicht installiert sein): ```bash docker compose --env-file .env.local up -d --build # App: Port 3000 (hinter Reverse-Proxy mit HTTPS betreiben) # Lokale DB-Kopie: Port 5433 (Dienst "db", optional) ``` Oder klassisch ohne Docker: ```bash npm ci npm run build npm run start # Port 3000, bindet an 0.0.0.0 (hinter Reverse-Proxy betreiben) ``` ### Passkeys für die Produktions-Domain freischalten Passkeys sind an die Domain gebunden. Aktuell ist `localhost` konfiguriert. Vor dem Livegang: `APP_URL` auf die öffentliche URL setzen und einmalig ```bash SUPABASE_ACCESS_TOKEN= node scripts/configure-passkeys.mjs ``` ausführen (liest `APP_URL` aus der Umgebung; die URL kann alternativ als Argument übergeben werden). Ohne lokales Node.js geht es auch über Docker: ```bash docker run --rm -v .:/app -w /app \ -e SUPABASE_ACCESS_TOKEN= -e APP_URL=https://menu.example.de \ node:24-alpine node scripts/configure-passkeys.mjs ``` Achtung: Bereits registrierte Passkeys der alten Domain verlieren dabei ihre Gültigkeit und müssen neu registriert werden. ## Datenbank ### Supabase (führende Datenbank) - Projekt-Ref: `hvzikwjoyhoakedlywhj` (Region `eu-central-1`, Frankfurt) - Schema und Seed-Daten: `supabase/migrations/0001_init.sql` und `0002_seed.sql` - Ausführen weiterer SQL-Dateien: `SUPABASE_ACCESS_TOKEN= node scripts/db-apply.mjs ` Sicherheitsmodell (Row Level Security): - `anon` darf ausschließlich lesen (Speisekarte, Bereiche, Allergene, Zusatzstoffe, Einstellungen). - `staff`/`admin` (Rolle im `profiles`-Eintrag) pflegen Gerichte, Wochenplan und Kennzeichnungen. - Nur `admin` ändert Einstellungen (Farben/Logo) und verwaltet Benutzer; das Anlegen/Löschen von Benutzern läuft serverseitig über den Service-Role-Key (`app/api/admin/users`). - Storage-Bucket `branding` (Logo): öffentlich lesbar, Schreibzugriff nur Admin. ### Lokale PostgreSQL-Kopie Die Datenbank `canteen_menu` (Kopie von Schema + Daten aus Supabase, inkl. Minimal-Stubs für `auth`/`storage`) gibt es in zwei Varianten: **Per Docker:** Der Dienst `db` aus `docker-compose.yml` spielt die Skripte aus `supabase/local/` beim ersten Start automatisch ein (PostgreSQL 17, Host-Port 5433 – 5432 ist meist durch eine native PostgreSQL-Installation belegt –, Passwort über `LOCAL_PG_PASSWORD`). **Nativ:** Auf dem Entwicklungsrechner läuft PostgreSQL 17 mit derselben Datenbank. Neu aufbauen bzw. aktualisieren: ```powershell $env:SUPABASE_DB_PASSWORD = "" $env:LOCAL_PG_PASSWORD = "" powershell -File scripts\sync-local-db.ps1 ``` Hinweis: Die lokale Kopie dient als Ablage/Backup und für Auswertungen. Logins (Passwort-Hashes, Passkeys) verbleiben ausschließlich in Supabase. ## Zugangsdaten Die Anmeldedaten (Admin, Demo-Mitarbeiter, Datenbank-Passwörter) stehen in `ZUGANGSDATEN.md` – diese Datei ist bewusst **nicht** im Repository (`.gitignore`). Nach dem ersten Login bitte die Startpasswörter ändern.