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>
13 KiB
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:
- Detailliert – Auswahl je Gericht, Kürzel am Gericht, Legende unter der Karte.
- 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
- E-Mail + Passwort – Hashing mit
scryptaus der Node-Standardbibliothek. - 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.
- 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
pgmit 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.shfür Linux-Deployments: Stack stoppen, lokale Änderungen andocker-compose.ymlsichern,git pull, Änderungen zurückspielen, neu bauen und starten. Bricht bei Konflikten kontrolliert ab, statt mit halbem Stand weiterzulaufen.npm run db:seed-demospielt 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.