From c168d3cca37bd31934a36bd12507a0a91cf346c5 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Sun, 16 Aug 2026 00:04:04 +0200 Subject: [PATCH] docs: FEATURES.md - Vorstellung des Funktionsumfangs Dokumentiert alle implementierten Features als "Was kann die Speisekarte?"-Vorstellung: oeffentliche Speisekarte, Kantinen-Bereich, Admin-Bereich, Anmeldung/Sicherheit, Mehrsprachigkeit, Betrieb sowie eine kompakte Versionstabelle. Co-Authored-By: Claude Opus 5 (1M context) --- FEATURES.md | 305 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 FEATURES.md diff --git a/FEATURES.md b/FEATURES.md new file mode 100644 index 0000000..2a50e72 --- /dev/null +++ b/FEATURES.md @@ -0,0 +1,305 @@ +# Was kann die Speisekarte? + +Vorstellung aller Funktionen der digitalen Kantinen-Speisekarte +(Stand Version 1.12.1). Technische Details zur Installation stehen in +[DEPLOYMENT.md](DEPLOYMENT.md), die Änderungen je Version in +[CHANGELOG.md](CHANGELOG.md). + +## In einem Satz + +Eine selbst gehostete Web-Anwendung, mit der das Kantinen-Team den +Wochenplan pflegt und alle Mitarbeiter ihn ohne Login am PC oder Handy +lesen können – dreisprachig, mit Allergen-Kennzeichnung, RSS-Abo, +Rezeptvorschlägen und eigenem Branding. + +Drei Bereiche mit getrennten Rechten: + +| Bereich | Wer | Zweck | +| --- | --- | --- | +| Öffentliche Speisekarte (`/`) | alle Mitarbeiter, ohne Login | Wochenplan lesen | +| Kantinen-Bereich (`/intern`) | Kantinen-Team (`staff`) und Admins | Gerichte und Wochenplan pflegen | +| Admin-Bereich (`/admin`) | nur Admins | Benutzer, Design, Sprachen | + +--- + +## 1. Für alle Mitarbeiter: die öffentliche Speisekarte + +Erreichbar ohne Anmeldung – Link oder QR-Code genügt. + +**Wochenplan Montag bis Freitag** +Tabelle mit einer Spalte je Wochentag und einer Zeile je Essenszeit +(z. B. Brotzeit, Mittagessen, Mittagessen vegetarisch, Suppe). Pro +Zelle können mehrere Gerichte stehen. Jedes Gericht wird mit Preis +angezeigt. + +**Navigation** +Buttons für vorherige Woche, nächste Woche und „Heute". Beliebige +Wochen lassen sich so vor- und zurückblättern – auch als direkter Link +(`/?woche=YYYY-MM-DD`), praktisch für Aushänge oder Bookmarks. + +**Handy-Ansicht** +Auf schmalen Bildschirmen erscheint standardmäßig eine Tagesansicht mit +Tab-Leiste (Mo–Fr) statt der Tabelle; auf Wunsch kann pro Besucher auf +die Wochenansicht umgeschaltet werden. Die Desktop-Tabelle bleibt davon +unberührt. + +**Allergene und Zusatzstoffe** +Je Gericht erscheinen die Kürzel (z. B. `A`, `G`) direkt neben dem +Namen, die vollständige Legende steht unterhalb der Karte. Alternativ +lässt sich die Kennzeichnung auf einen einzelnen Hinweistext umstellen +(siehe Abschnitt 2). + +**Geschlossene Tage** +Feiertage, Betriebsausflüge oder Urlaub werden als „Geschlossen" +angezeigt – ein einziges, über alle Essenszeiten-Zeilen durchgehendes +Banner mit optionalem Grund („Betriebsferien"), statt leerer Zellen. + +**Sprachumschalter** +Umschalten der kompletten Oberfläche zwischen den freigeschalteten +Sprachen (ausgeliefert mit Deutsch, Englisch, Polnisch). Die Auswahl +wird per Cookie gemerkt, die Seite muss dafür nicht neu geladen werden. +Ist nur eine Sprache aktiv, verschwindet der Umschalter automatisch. + +**RSS-Feed zum Abonnieren** +`/feed.xml` liefert einen Eintrag pro Tag (aktuelle und nächste Woche) +mit allen Gerichten je Essenszeit inkl. Preis; geschlossene Tage +erscheinen mit Schließungsgrund. Sichtbares RSS-Icon im Kopfbereich +plus Autodiscovery im Seiten-``, damit Outlook, Thunderbird & Co. +den Feed automatisch finden. Der Inhalt wird zusätzlich als +`content:encoded` ausgeliefert, damit Reader ihn nicht nach der ersten +Essenszeit abschneiden. + +**Rezept vorschlagen** +Jeder Besucher kann ohne Login ein fehlendes Gericht vorschlagen (Name ++ optionale Beschreibung). Eingebaut: Dublettenprüfung gegen bestehende +Gerichte und ein unsichtbares Honeypot-Feld als Spamschutz. + +**Eigenes Erscheinungsbild** +Firmenlogo zentriert über der Karte, eigenes Favicon im Browser-Tab, +Farben nach Corporate Design – alles vom Admin einstellbar, ohne +Codeänderung. + +--- + +## 2. Für das Kantinen-Team (`/intern`) + +Nach dem Login sichtbar, gemeinsame Navigation für alle Unterseiten, +auf dem Handy als Hamburger-Menü. + +### Wochenplan + +- Gerichte per **Suchfeld** einer Zelle (Tag + Essenszeit) zuweisen – + keine langen Dropdown-Listen zum Durchscrollen. +- Mehrere Gerichte pro Zelle, einzeln wieder entfernbar. +- Wochen-Navigation inkl. „Heute"-Button wie in der öffentlichen Ansicht. +- **Tag schließen**: Schloss-Symbol am Datum (🔓 schließt, 🔒 öffnet + wieder) mit optionalem, mehrsprachigem Grund. Für geschlossene Tage + wird „Mahlzeit hinzufügen" ausgeblendet; bereits eingetragene + Gerichte bleiben entfernbar. +- Auf dem Handy Tag-für-Tag-Ansicht mit Tab-Leiste; beide Ansichten + teilen denselben Bearbeitungsstand. + +### Gerichte + +- Anlegen, bearbeiten, löschen – mit Name, Preis, Allergenen und + Zusatzstoffen (Chip-Auswahl). +- **Live-Suche** in der Gerichteliste. +- **Mehrfachauswahl** zum Löschen vieler Gerichte in einem Schritt. +- Ein Namensfeld je aktiver Sprache, mit Button **„Automatisch + übersetzen"** (DeepL) – das Ergebnis bleibt danach frei korrigierbar. +- **CSV-Export/-Import**: aktuelle Liste als CSV herunterladen, + bearbeiten, wieder hochladen. Spalten + `name,name_en,…,price,allergens,additives` (Kürzel semikolongetrennt, + z. B. `A;G`), Spaltenreihenfolge und Groß-/Kleinschreibung der + Kopfzeile egal. Bestehende Gerichte werden über den Namen erkannt und + aktualisiert, neue angelegt. Fehlerhafte Zeilen (Pflichtfeld fehlt, + ungültiger Preis, mehrdeutiger Name) werden übersprungen und mit + Grund aufgelistet, statt den ganzen Import abzubrechen. + +### Essenszeiten + +Vollständig verwaltbar statt fest im Code: anlegen, umbenennen (je +Sprache), Uhrzeit ändern, per Pfeil-Buttons neu anordnen, löschen. +Beim Löschen werden zugewiesene Gerichte automatisch aus allen +Wochenplänen entfernt. + +### Allergene & Zusatzstoffe + +- Globale Listen pflegen (Kürzel + Name je Sprache, Sortierung), + ebenfalls mit DeepL-Übersetzung und CSV-Export/-Import. +- **Zwei Betriebsmodi, umschaltbar:** + 1. *Detailliert* – Auswahl je Gericht, Kürzel am Gericht, Legende + unter der Karte. + 2. *Hinweistext* – ein einziger, mehrsprachig editierbarer Satz + (z. B. „Bei Fragen zu Allergenen und Zusatzstoffen wenden Sie sich + bitte an das Kantinen-Team."). Kürzel, Legende und die + Allergen-Auswahl in der Gerichteverwaltung verschwinden; + zugeordnete Daten bleiben in der Datenbank erhalten und sind beim + Zurückschalten wieder da. + +### Rezeptvorschläge + +Eingereichte Vorschläge sichten, direkt als Gericht **übernehmen** oder +**ablehnen**. Die Anzahl offener Vorschläge steht als rote Zahl im +Navigationspunkt „Vorschläge". + +### Eigenes Konto + +Name, E-Mail und Passwort ändern, Passkeys registrieren und entfernen, +Passwort-Anmeldung für das eigene Konto abschalten. + +--- + +## 3. Für Admins (`/admin`) + +### Benutzerverwaltung + +Kantinenmitarbeiter anlegen, bearbeiten und löschen; Rolle vergeben +(Kantinenmitarbeiter oder Admin); Passwort zurücksetzen – jeweils mit +Wiederholungsfeld gegen Tippfehler. + +### Design + +- Acht Farben per Farbwähler einstellbar: Primär, Primär dunkel, + Akzent, Kopfzeilentext, Zeilenhintergrund, Zeilenhintergrund + alternierend, Hinzufügen-Button, Löschen-Button. Auf Werkszustand + zurücksetzbar. +- Logo und Favicon hochladen oder entfernen. Aus Sicherheitsgründen + nur Rasterformate (png/jpg/webp/gif/ico) – SVG ist bewusst nicht + erlaubt, da eingebettetes Script auf der eigenen Domain ausgeführt + würde. + +### Sprachen + +Sprachen sind zur Laufzeit verwaltbar, ohne neues Deployment: + +- Vorlage der deutschen Oberflächentexte herunterladen, übersetzt + wieder hochladen – die neue Sprache ist sofort aktiv. +- Je Sprache wird **Vollständigkeit in %** angezeigt sowie „N neue + Begriffe seit letztem Import", falls eine App-Version zusätzliche + Texte mitgebracht hat. Fehlende Begriffe fallen automatisch auf + Deutsch zurück, bleiben also nie leer. +- Neue Sprachen selbst anlegen (Code + Anzeigename). Sobald eine + Sprache aktiv ist, erscheint in der Gerichte-, Essenszeiten- und + Kennzeichnungsverwaltung automatisch ein Eingabefeld dafür – und im + CSV-Export eine zusätzliche Spalte (`name_fr`, …). +- Sprachen einzeln deaktivieren, bis hinunter auf eine einzige; + Deutsch bleibt als garantiert vollständige Basissprache unlöschbar. +- Der Import prüft die Datei gegen die aktuelle Vorlage: Platzhalter + wie `{message}` müssen erhalten bleiben, unbekannte Schlüssel werden + ignoriert statt einen Fehler zu werfen. + +--- + +## 4. Anmeldung und Sicherheit + +**Drei Anmeldewege, kombinierbar** + +1. **E-Mail + Passwort** – Hashing mit `scrypt` aus der + Node-Standardbibliothek. +2. **Passkey** (WebAuthn, z. B. Windows Hello, Face ID, Sicherheits- + schlüssel) – im eigenen Konto nach dem ersten Login registrierbar. + Die Passwort-Anmeldung lässt sich pro Konto abschalten, sobald + mindestens ein Passkey vorhanden ist; das wird serverseitig + erzwungen, nicht nur im Browser geprüft. +3. **SSO per OpenID Connect** (z. B. gegen ein selbst gehostetes + Authentik) – Authorization-Code-Flow mit PKCE, Signaturprüfung des + ID-Tokens gegen das JWKS des Providers, State/Nonce serverseitig + kurzlebig und nur einmal verwendbar. Aktiviert sich über drei + Umgebungsvariablen; ohne sie bleibt der SSO-Button unsichtbar. + Wichtig: SSO legt **nie** neue Konten an und übernimmt **keine** + Rollen aus Claims – nur bereits unter „Benutzer" angelegte + E-Mail-Adressen kommen herein, die Rolle bleibt allein in der + Benutzerverwaltung steuerbar. + +**Sitzungen** +Opake Tokens, nur als Hash in der Datenbank, Cookie mit +`httpOnly`/`sameSite=lax`. Laufzeit 30 Tage mit gleitender +Verlängerung: fällt die Restlaufzeit unter 7 Tage, wird sie beim +nächsten Aufruf erneuert – statt bei jedem Request zu schreiben. + +**Zugriffsschutz** +`/intern`, `/admin` und `/konto` sind serverseitig geschützt; jede +API-Route prüft zusätzlich die Rolle. Öffentlich lesbar sind nur +Speisekarte, Essenszeiten, Kennzeichnungen und Logo/Favicon. + +**Weitere Härtung** +CSRF-Origin-Check, HTTP-Security-Header für alle Routen +(`X-Frame-Options: DENY`, `frame-ancestors 'none'`, `nosniff`, +`Referrer-Policy`, restriktive `Permissions-Policy`), Datenbank ohne +Host-Port (nur im internen Docker-Netz erreichbar), Upload-Whitelist +für Bildformate. + +**Erster Start** +Ist die Datenbank leer, wird automatisch genau ein Admin mit +Zufallspasswort angelegt und die Zugangsdaten nach `ZUGANGSDATEN.md` +geschrieben (nicht im Repository). Klappt das Schreiben nicht, landen +sie im Container-Log statt verloren zu gehen. + +--- + +## 5. Mehrsprachigkeit im Detail + +Zwei Ebenen, beide zur Laufzeit erweiterbar: + +| Ebene | Was | Verwaltung | +| --- | --- | --- | +| Oberfläche | alle Buttons, Beschriftungen, Meldungen | Admin → Sprachen (Vorlage exportieren/importieren) | +| Inhalte | Gerichtnamen, Essenszeiten, Allergene, Zusatzstoffe, Schließungsgründe | ein Feld je aktiver Sprache in der jeweiligen Verwaltung | + +Beides ist **nicht** auf Deutsch/Englisch/Polnisch begrenzt – eine neu +angelegte Sprache erscheint automatisch überall. Übersetzungen können +per DeepL vorbefüllt werden (in alle aktiven, von DeepL unterstützten +Sprachen gleichzeitig) und bleiben danach manuell korrigierbar. Fehlt +eine Übersetzung, greift der Deutsch-Fallback; ist Deutsch selbst +deaktiviert, die erste aktive Sprache. + +DeepL ist optional: ohne `DEEPL_API_KEY` verschwindet nur der Button +„Automatisch übersetzen", alle Felder bleiben von Hand ausfüllbar. + +--- + +## 6. Betrieb + +- **Vollständig selbst gehostet.** Einzige optionale externe + Abhängigkeit ist DeepL. Keine Cloud-Datenbank, kein + Auth-Dienstleister, kein Storage-Anbieter. +- **Stack:** Next.js 16 (App Router, TypeScript, Tailwind CSS), + PostgreSQL über `pg` mit eigenen SQL-Migrationen. +- **Docker Compose** für Produktion und Entwicklung (Dev-Profil mit + Hot-Reload, ohne lokal installiertes Node.js/PostgreSQL). +- **Migrationen laufen bei jedem Start automatisch** und idempotent – + ein Update braucht keinen manuellen Datenbankschritt. +- **`update.sh`** für Linux-Deployments: Stack stoppen, lokale + Änderungen an `docker-compose.yml` sichern, `git pull`, Änderungen + zurückspielen, neu bauen und starten. Bricht bei Konflikten + kontrolliert ab, statt mit halbem Stand weiterzulaufen. +- **`npm run db:seed-demo`** spielt einen Beispieldatensatz für Demos + ein (Essenszeiten, Kennzeichnungen, Testgerichte, zwei Wochen). + Frische Deployments starten bewusst leer. +- **Versionsnummer im Fußbereich** jeder Seite, Git-Tags markieren die + Versionsstände. + +--- + +## 7. Wann kam was + +| Version | Kern | +| --- | --- | +| 1.0.0 | Öffentliche Speisekarte, Login (Passwort + Passkey), Kantinen- und Admin-Bereich, Docker-Setup | +| 1.1.0 | Versionsnummer im Fußbereich | +| 1.2.0 | „Heute"-Button, mobile Tages-/Wochenansicht, Allergen-Chips, Passwort-Wiederholung, Favicon, `SITE_TITLE` | +| 1.3.0 | Mehrsprachigkeit DE/EN/PL, DeepL, Rezeptvorschläge inkl. Sichtung | +| 1.4.0 | Essenszeiten frei verwaltbar | +| 1.5.0 | Supabase komplett entfernt – eigene Datenbank, Auth, Passkeys, Datei-Ablage | +| 1.6.x | RSS-Feed, Vorschläge-Badge, DEPLOYMENT.md, DB-Port geschlossen, Bugfixes | +| 1.7.0 | CSV-Import/-Export für Gerichte, Allergene, Zusatzstoffe | +| 1.8.x | Umschaltbarer Hinweismodus für Kennzeichnungen, `update.sh` | +| 1.9.0 | SSO per OpenID Connect | +| 1.9.5 | Interner Bereich für Mobilgeräte nutzbar | +| 1.10.0 | Sprachen zur Laufzeit verwaltbar (Oberfläche) | +| 1.11.x | Sprachen zur Laufzeit verwaltbar (Inhalte), Sprach-Fallback-Fix | +| 1.12.x | Tage als geschlossen markierbar, Schloss-Symbol und durchgehendes Banner | + +Ausführlich in [CHANGELOG.md](CHANGELOG.md), der Zeitaufwand je Version +in [ZEITAUFWAND.md](ZEITAUFWAND.md).