Files
Speisekarte/FEATURES.md
T
florian.wildeandClaude Opus 5 c168d3cca3 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) <noreply@anthropic.com>
2026-08-16 00:04:24 +02:00

13 KiB
Raw Blame History

Was kann die Speisekarte?

Vorstellung aller Funktionen der digitalen Kantinen-Speisekarte (Stand Version 1.12.1). Technische Details zur Installation stehen in DEPLOYMENT.md, die Änderungen je Version in 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-<head>, 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, der Zeitaufwand je Version in ZEITAUFWAND.md.