74 Commits
Author SHA1 Message Date
florian.wilde f561ac29ba Merge feature/v1.8: Umschaltbarer Hinweismodus für Allergene/Zusatzstoffe v1.8.0 2026-07-19 23:58:45 +02:00
florian.wildeandClaude Sonnet 5 de8914f9cc feat: Umschaltbarer Hinweismodus für Allergene/Zusatzstoffe
Neue Migration 0002_labeling_notice.sql: settings.labeling_mode
("detailed"/"notice") + labeling_notice(_en/_pl). Auf der Seite
"Allergene & Zusatzstoffe" (LabelingNoticeEditor) kann zwischen der
detaillierten Auswahl je Gericht und einem editierbaren Hinweistext
umgeschaltet werden. Im Hinweismodus entfällt auf der öffentlichen
Speisekarte (Desktop + Mobile) die Legende und die Kürzel an den
Gerichten, in der Gerichte-Verwaltung wird die Chip-Auswahl beim
Bearbeiten ausgeblendet. Umschalten/Bearbeiten ist für staff und admin
freigegeben, Farben in /api/settings bleiben admin-only.

Version 1.8.0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 23:57:43 +02:00
florian.wilde 96eaf3989e Merge feature/v1.7: CSV-Import/-Export, RSS-Fix für mehrere Essenszeiten v1.7.0 2026-07-19 23:14:02 +02:00
florian.wildeandClaude Sonnet 5 474467811c feat: CSV-Import/-Export für Gerichte/Allergene/Zusatzstoffe, RSS-Fix für mehrere Essenszeiten
CSV-Import/-Export (Gerichte, Allergene, Zusatzstoffe): neue Export-/
Import-Routen unter app/api/*/export und */import, hand-geschriebener
CSV-Parser/-Serializer (lib/csv.ts, keine neue Abhängigkeit), Upsert
über Kürzel (Allergene/Zusatzstoffe) bzw. exakten Namen (Gerichte,
mit Ambiguitäts-Check bei Namensdopplung). UI-Buttons in DishManager
und CodeListManager.

RSS-Feed: <content:encoded> zusätzlich zu <description> ausgeben,
da mehrere Feed-Reader description als kürzbare Vorschau behandeln
und dadurch bei mehreren Essenszeiten pro Tag nur die erste zeigten.

Version 1.7.0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 23:10:36 +02:00
florian.wilde 5874eb31b6 Merge fix/branding-dir-env: BRANDING_DIR im app-Dienst setzen (Upload-500-Fix) v1.6.3 2026-07-18 15:11:27 +02:00
florian.wildeandClaude Sonnet 5 358872d56c fix: BRANDING_DIR im app-Dienst setzen, sonst 500 beim Logo/Favicon-Upload
Ohne die Variable weicht lib/branding.ts auf ${cwd}/data/branding
(= /app/data/branding) aus statt das im Dockerfile vorbereitete und
gemountete /data/branding zu nutzen. /app gehört root, der App-Prozess
läuft aber als nicht-root – das Anlegen des Fallback-Verzeichnisses
schlägt mit EACCES fehl.

Version 1.6.3.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 15:10:46 +02:00
florian.wilde 36be08cea7 Merge fix/bootstrap-admin-file-permission: Admin-Passwort nicht mehr verlieren bei EACCES
bootstrap-admin.mjs verliert das generierte Admin-Passwort nicht mehr,
wenn ZUGANGSDATEN.md nicht beschreibbar ist (Fallback: Log statt
Absturz nach erfolgreichem DB-Insert). DEPLOYMENT.md ergänzt um
chmod 666 in Schritt 2 sowie Fehlerbehebung für ECONNREFUSED und
diesen EACCES-Fall.
v1.6.2
2026-07-18 14:51:55 +02:00
florian.wilde b63822060d Version 1.6.2 2026-07-18 14:51:29 +02:00
florian.wilde 94a2c98252 bootstrap-admin.mjs: Zugangsdaten nicht mehr verlieren, wenn ZUGANGSDATEN.md nicht beschreibbar ist
Der Admin-Benutzer wird immer in der Datenbank angelegt, bevor
versucht wird, die generierten Zugangsdaten in ZUGANGSDATEN.md zu
schreiben. Schlug dieser Schreibvorgang fehl (z. B. EACCES, weil die
per Bind-Mount eingebundene Host-Datei root gehört und der
App-Container als nicht-root läuft), flog bisher eine ungefangene
Exception – das Skript brach ab, BEVOR das Passwort irgendwo
protokolliert wurde. Der Admin existierte dann in der DB, aber niemand
kannte sein Passwort mehr.

appendFileSync() läuft jetzt in einem try/catch; schlägt der
Dateizugriff fehl, werden E-Mail und Passwort stattdessen direkt ins
Log geschrieben, und das Skript beendet sich weiterhin mit Exitcode 0
(die eigentlich kritische Operation – der DB-Insert – ist ja bereits
erfolgreich). Lokal verifiziert: Skript bricht bei einer nicht
beschreibbaren ZUGANGSDATEN.md nicht mehr ab, Zugangsdaten landen im
Log, zweiter Lauf bleibt idempotent.

DEPLOYMENT.md: Schritt 2 ergänzt um "chmod 666 ZUGANGSDATEN.md" (der
eigentliche Auslöser), neue Fehlerbehebung-Einträge für
ECONNREFUSED-Startreihenfolge und diesen EACCES-Fall inkl.
Recovery-Anleitung für einen bereits verwaisten Admin-Account.
2026-07-18 14:44:38 +02:00
florian.wilde 0d9d26b885 Merge fix/deployment-guide-pg-password: DB-Port nicht mehr exponiert, Deployment-Doku-Fixes
Datenbank ist nur noch im internen Compose-Netzwerk erreichbar (kein
Host-Port mehr). DEPLOYMENT.md: Fehlerbehebung für "password
authentication failed" (PostgreSQL übernimmt das Passwort nur beim
allerersten Start), .env.example warnt vor Sonderzeichen in
LOCAL_PG_PASSWORD.
v1.6.1
2026-07-18 14:30:00 +02:00
florian.wilde 233027fd17 Version 1.6.1 2026-07-18 14:29:13 +02:00
florian.wilde 469b93ee05 Datenbank-Port nicht mehr auf den Host exponieren
docker-compose.yml: "ports: 5433:5432" beim db-Dienst entfernt. Die
Datenbank ist damit nur noch innerhalb des Compose-Netzwerks unter
"db:5432" erreichbar (für app/dev), nicht mehr vom Host oder von außen
– reduziert die Angriffsfläche, ohne dass sich am App-Betrieb etwas
ändert (Verbindung läuft ohnehin über den internen Service-Namen).
Zugriff für Debugging/Backups weiterhin über "docker compose exec db
psql"/"pg_dump" möglich.

Lokal verifiziert: App verbindet sich weiterhin erfolgreich (Migration/
Bootstrap/Login laufen durch), Verbindungsversuch vom Host auf Port
5433 wird jetzt abgelehnt (connection refused).

README.md an die entfallene Portangabe angepasst.
2026-07-18 14:25:55 +02:00
florian.wilde 536f51d516 DEPLOYMENT.md: Fehlerbehebung für "password authentication failed"
Beim Ausrollen auf einem echten Server aufgetreten: PostgreSQL setzt
sein Passwort nur beim allerersten Start auf leerem Datenverzeichnis –
lief "docker compose up" vorher schon einmal ohne "--env-file
.env.local" (oder wurde LOCAL_PG_PASSWORD nachträglich geändert),
bleibt im Volume ein anderes Passwort stehen als die App danach
verwendet.

Neue Fehlerbehebung-Sektion mit der konkreten Fehlermeldung und zwei
Fixes (Volume neu anlegen bei frischem Deployment, sonst Passwort im
laufenden Container angleichen). Warnhinweis in Schritt 3 ergänzt.

.env.example: Hinweis, dass LOCAL_PG_PASSWORD nur aus Buchstaben/
Zahlen bestehen sollte (#, $, Leerzeichen, Anführungszeichen können
beim .env-Parsing zu stillen Fehlern führen).
2026-07-18 14:20:01 +02:00
florian.wilde 3b8f7de5e4 Merge feature/v1.6: RSS-Feed, Vorschläge-Badge, Deployment-Anleitung
RSS-Feed des Speiseplans (/feed.xml, ein Eintrag pro Tag) mit
Autodiscovery-Link und sichtbarem Icon im Header. Anzahl offener
Rezeptvorschläge als Badge in der internen Navigation. Letzte
Supabase-Erwähnungen aus aktiver Dokumentation entfernt. Neues
DEPLOYMENT.md mit Schritt-für-Schritt-Anleitung für einen neuen Server.
v1.6.0
2026-07-18 12:32:18 +02:00
florian.wilde c986327c1c Version 1.6.0
Verifiziert: RSS-Feed (/feed.xml, gültiges RSS 2.0, korrekte Tages-
Einträge inkl. Essenszeiten/Preisen), Autodiscovery-Link + sichtbares
Icon auf Desktop und Mobile, Vorschläge-Badge (Anzahl offener
Rezeptvorschläge, aktualisiert bei Navigation), keine verbliebenen
Supabase-Erwähnungen in aktiver Doku/Kommentaren.
2026-07-18 12:26:20 +02:00
florian.wilde 65bfd931cc Schritt-für-Schritt-Deployment-Anleitung für neuen Server
Neues DEPLOYMENT.md: Voraussetzungen, Repository holen, .env.local
konfigurieren, docker compose up --build, Admin-Zugangsdaten aus
ZUGANGSDATEN.md abholen, Reverse-Proxy/HTTPS (Caddy-Beispiel), Updates,
Backup der Docker-Volumes. Aus README.md verlinkt.

.env.example: LOCAL_PG_PASSWORD war bisher nirgends dokumentiert,
obwohl docker-compose.yml die DATABASE_URL des App-Containers daraus
zusammenbaut (die dortige DATABASE_URL-Zeile wird beim
Docker-Compose-Betrieb ignoriert) – beim Schreiben der Anleitung
aufgefallen und ergänzt.

README.md: RSS-Feed und Vorschläge-Badge in der Funktionstabelle
ergänzt.
2026-07-18 12:23:46 +02:00
florian.wilde 6f78762e27 Letzte Supabase-Erwähnungen aus aktiver Doku/Kommentaren entfernt
README.md, db/migrations/0001_init.sql, lib/auth/webauthn-config.ts,
lib/branding.ts nannten Supabase noch in Kommentaren, die erklären,
was diese Stellen ersetzen. Umformuliert, ohne den alten Anbieter beim
Namen zu nennen. CHANGELOG.md bleibt unverändert – die Erwähnungen dort
sind historischer Bestand (u. a. der v1.5.0-Eintrag über die
Supabase-Ablösung selbst) und sollen als Zeitdokument stehen bleiben.
2026-07-18 12:21:21 +02:00
florian.wilde 08a5ef8a2b v1.6: RSS-Feed für den Speiseplan + Badge für offene Rezeptvorschläge
app/feed.xml: RSS-2.0-Feed mit einem Eintrag pro Tag (aktuelle +
nächste Woche), Gerichte je Essenszeit im CDATA-Beschreibungstext.
Immer auf Deutsch, da Feedreader kein Sprach-Cookie mitschicken.

Autodiscovery-Link im <head> (app/layout.tsx) sowie sichtbares
RSS-Icon im Header der öffentlichen Speisekarte (neben Sprachumschalter/
Anmelden – die einzige Stelle, die auf allen Bildschirmgrößen sichtbar
ist).

AppHeader: "Vorschläge"-Navigationspunkt zeigt jetzt die Anzahl
offener Rezeptvorschläge als Zahl im roten Kreis (GET
/api/dish-suggestions, bereits staff/admin-beschränkt), aktualisiert
sich bei Navigation innerhalb des internen Bereichs.
2026-07-18 12:18:46 +02:00
florian.wilde b7c24364b8 Merge feature/v1.5: Supabase entfernt, vollständig selbst gehostet
Postgres statt Supabase-Cloud, eigene Session-/Passkey-Auth, lokale
Branding-Dateiablage, Autorisierung in den API-Routen statt RLS,
automatischer Admin-Bootstrap bei leerer Datenbank, Gerichtsuche im
Wochenplan. Vor dem Merge per Sicherheitsprüfung geprüft, ein
gefundenes SVG-Upload-XSS-Problem behoben.
v1.5.0
2026-07-18 11:59:13 +02:00
florian.wilde be80edec61 Changelog: Hinweis auf noch offenen Merge entfernt, v1.5.0 finalisiert 2026-07-18 11:58:50 +02:00
florian.wilde 0958abaac6 Sicherheit: SVG-Upload für Logo/Favicon nicht mehr erlaubt
Der Branding-Upload akzeptierte bisher jeden Dateityp, der clientseitig
mit "image/" beginnt, inkl. SVG. Anders als beim vorherigen Supabase-
Storage-Bucket (andere Origin) wird die Datei jetzt auf der eigenen
Origin unter /branding/[filename] ausgeliefert – eine SVG mit
eingebettetem <script> hätte bei direktem Aufruf der Datei-URL
(z. B. "Bild in neuem Tab öffnen") mit dem Session-Cookie eines
anderen Admins ausgeführt werden können und wäre dabei auch am
CSRF-Schutz vorbeigekommen (Origin stimmt ja).

Upload-Route erlaubt jetzt nur noch eine Whitelist an Rasterformaten
(png/jpg/webp/gif/ico), die Dateiendung wird aus dem geprüften
MIME-Typ abgeleitet statt aus dem vom Client gesendeten Dateinamen.
Die Ausliefer-Route kennt "svg" als Content-Type nicht mehr (Defense
in Depth). Gefunden bei der Sicherheitsprüfung vor dem Merge nach
master.
2026-07-18 11:58:07 +02:00
florian.wilde 3d13ba88f4 Wochenplan: Gerichtsuche statt langer Dropdown-Liste, Beispieldaten zurück
WeekEditor: "Mahlzeit hinzufügen" zeigt jetzt ein Suchfeld mit
gefilterter Trefferliste (wie die Live-Suche bei Gerichten) statt einer
langen Dropdown-Liste zum Durchscrollen. Ausgewähltes Gericht lässt sich
per ×-Button wieder ändern.

Neues Skript db/seed-demo.sql (+ db/seed-demo.mjs, "npm run
db:seed-demo"): spielt den ursprünglichen Beispieldatensatz ein
(4 Essenszeiten, 14 Allergene, 11 Zusatzstoffe, 18 Testgerichte, zwei
Beispielwochen) – nur für lokale Entwicklung/Demos, kein Teil der
automatischen Migrationen, da frische Deployments weiterhin leer
starten sollen.
2026-07-18 11:35:13 +02:00
florian.wilde c60f481aec Version 1.5.0
Vollständig verifiziert: natives npm run dev auf frisch geleerter
lokaler Postgres-DB (Migration + Admin-Bootstrap + ZUGANGSDATEN.md,
Login, Essenszeiten/Gerichte/Wochenplan/Kennzeichnung/Vorschläge-CRUD,
Konto-Verwaltung inkl. serverseitiger Passkey-Absicherung,
Branding-Upload/-Löschung, Admin-Benutzerverwaltung mit
rollenbasierter Zugriffskontrolle, Neustart-Idempotenz) sowie über
docker compose --build auf eigenem Volume (Migration/Bootstrap im
Container-Entrypoint, Bind-Mount für ZUGANGSDATEN.md, Login).
2026-07-18 11:21:19 +02:00
florian.wilde b512093a7e v1.5: Docker/Compose/.env.example auf selbst gehostetes Postgres umgestellt
Dockerfile: keine NEXT_PUBLIC_*-Build-Args mehr (Branding-URLs sind jetzt
relativ, kein Anon-Key-Konzept mehr – das Image wird dadurch
umgebungsunabhängig), db/migrate.mjs + scripts/ werden zusätzlich in die
Runner-Stage kopiert (nicht Teil von Next's Standalone-Trace), CMD wendet
Migrationen an und legt bei Bedarf den Admin an, bevor der Server startet.

docker-compose.yml: db-Dienst ist jetzt die primäre Datenbank (kein
Supabase-Dump-Mount mehr), neues Volume branding_data für Logo/Favicon,
Bind-Mount für ZUGANGSDATEN.md.

.env.example: Supabase-Variablen durch DATABASE_URL + optionales
ADMIN_EMAIL ersetzt. package.json: @supabase/ssr und @supabase/supabase-js
entfernt. README.md komplett auf die neue Architektur aktualisiert.
2026-07-18 11:02:16 +02:00
florian.wilde a9a9cf7ba4 v1.5: Alle Manager-Komponenten auf eigene API-Routen umgestellt
Neue interne Route Handler unter app/api/ (dishes inkl. Bulk-Delete,
sections, allergens, additives, menu-entries, dish-suggestions,
settings, admin/branding) ersetzen den direkten Browser→Postgrest-
Zugriff der bisherigen Supabase-Anbindung. Jede Route prüft Autorisierung
selbst (lib/auth/session.ts requireRole) statt sich auf Postgres-RLS zu
verlassen – ersetzt 1:1 die bisherigen Policies (öffentlich lesbar /
staff+admin schreiben / nur admin für Einstellungen & Benutzer).

Rezeptvorschlag-Dublettencheck (SuggestionForm) läuft jetzt zwingend
serverseitig im POST-Handler, da es keinen Browser→Postgres-Pfad mehr
gibt. "Passkey annehmen" (SuggestionReview) läuft transaktional
(Gericht anlegen + Vorschlag löschen in einem Schritt).

Logo/Favicon: neuer lokaler Dateispeicher (lib/branding.ts, Docker-
Volume) statt Supabase-Storage-Bucket, ausgeliefert über
app/branding/[filename]. logoUrl()/faviconUrl() liefern jetzt einfache
relative Pfade statt einer fest verdrahteten Supabase-Storage-URL.

DeepL-Route (api/translate) und Benutzerverwaltung (api/admin/users)
laufen jetzt gegen die users-Tabelle statt Supabase Auth.

Alle "use client"-Manager-Komponenten (DishManager, WeekEditor,
SectionManager, CodeListManager, SuggestionForm/Review, UserManager,
DesignEditor) rufen ihre jeweiligen /api/-Routen per fetch() statt den
Supabase-Browser-Client zu nutzen.
2026-07-18 11:01:52 +02:00
florian.wilde 28c9ec15c8 v1.5: Selbst gehostete Auth (Passwort + Passkeys), Postgres statt Supabase
Fundament: db/migrations/0001_init.sql (users/sessions/passkey_credentials/
webauthn_challenges + Inhaltstabellen ohne RLS), db/migrate.mjs
(idempotenter Migrationsrunner), lib/db (pg-Pool + query-Wrapper),
lib/auth/password.ts (Node-crypto.scrypt, keine neue Abhängigkeit),
lib/auth/session.ts (opake DB-Session-Tokens, Sliding-Refresh in proxy.ts).

Passkeys: @simplewebauthn/server + /browser ersetzen Supabase's
experimentelles WebAuthn-API (dafür gab es bisher kein eigenes
Credential-Schema); RP-Konfiguration kommt jetzt direkt aus APP_URL statt
über scripts/configure-passkeys.mjs und die Supabase-Management-API.

scripts/bootstrap-admin.mjs legt bei leerer users-Tabelle automatisch
einen Admin mit zufälligem Passwort an und hängt die Zugangsdaten an
ZUGANGSDATEN.md an (idempotent, via predev/prestart-Hooks aufgerufen).

Bugfix im Zuge des Umbaus: "mind. 1 Passkey nötig, um Passwort-Login zu
deaktivieren" wurde bisher nur clientseitig geprüft – jetzt zusätzlich
serverseitig in PATCH /api/account.

E-Mail-Änderung im eigenen Konto läuft jetzt direkt nach
Passwort-Bestätigung statt über eine Supabase-Bestätigungsmail (kein
SMTP-Server vorhanden).

Rest der Anwendung (Gerichte/Essenszeiten/Kennzeichnung/Vorschläge/
Benutzerverwaltung/Branding) nutzt noch den alten Supabase-Client –
folgt in weiteren Commits.
2026-07-18 10:42:21 +02:00
florian.wilde 4c5376a8c7 Merge feature/v1.4: Essenszeiten variabel, DeepL-Button-Sichtbarkeit, Bugfix mobiler Sprachumschalter v1.4.0 2026-07-18 08:33:25 +02:00
florian.wilde 0a1f533880 Changelog: Hinweis auf noch offenen Merge für 1.4.0 entfernt 2026-07-18 08:33:17 +02:00
florian.wilde 4d5e7fca04 Bug: Sprachumschalter in mobiler Ansicht doppelt
Der Kopfbereich der öffentlichen Speisekarte zeigte seinen eigenen
LanguageSwitcher auch auf schmalen Bildschirmen an, obwohl
PublicMobileMenu dort bereits einen eigenen rendert. Der Header-Switcher
ist jetzt wie der Rest des Desktop-Layouts erst ab dem sm-Breakpoint
sichtbar.
2026-07-18 08:30:20 +02:00
florian.wilde 0f20ad6527 "Bereiche" zu "Essenszeiten" umbenannt
Betrifft UI-Texte (DE/EN/PL), Route (/intern/bereiche ->
/intern/essenszeiten), Code-Kommentare, Changelog und README.
Fachlich unverändert (weiterhin die Tabelle/Komponente "sections"/
SectionManager im Code), da "Essenszeiten" den Zeitbezug des Features
klarer ausdrückt als der bisherige Sammelbegriff.
2026-07-18 08:26:15 +02:00
florian.wilde d85d2e3fe3 Version 1.4.0 2026-07-18 08:15:31 +02:00
florian.wilde a80cd5bd12 Lokale PostgreSQL-Kopie: Dump nach Migration 0006 aktualisiert 2026-07-18 08:15:20 +02:00
florian.wilde 51fdb93e97 Automatisch-übersetzen-Button nur bei konfiguriertem DEEPL_API_KEY
Statt den Button immer anzuzeigen und erst nach Klick auf die
fehlende Konfiguration hinzuweisen, wird er serverseitig anhand von
process.env.DEEPL_API_KEY ein-/ausgeblendet (Gerichte, Allergene,
Zusatzstoffe). Die dadurch überflüssige "translateUnavailable"-
Meldung wurde aus allen drei Sprachen entfernt.
2026-07-18 08:14:49 +02:00
florian.wilde 868a5d2fcf Bereiche vollständig verwaltbar (anlegen/bearbeiten/löschen/Uhrzeit)
Migration 0006: sections.key bekommt einen automatisch generierten
Schlüssel (wie dishes.id), menu_entries.section_key kaskadiert beim
Löschen eines Bereichs, neue RLS-Policy erlaubt Kantinenmitarbeitern/
Admins Schreibzugriff auf sections.

Neue Seite „Bereiche" im Kantinen-Bereich (SectionManager): Bereiche
anlegen, umbenennen (inkl. EN/PL), Uhrzeit setzen, löschen und per
Pfeil-Buttons neu anordnen.
2026-07-18 08:14:32 +02:00
florian.wilde 8b98294998 Merge feature/v1.3: Mehrsprachigkeit (DE/EN/PL), Rezeptvorschläge, Bugfixes
- Mehrsprachige Oberfläche mit Sprachumschalter (Cookie-basiert)
- Gerichte/Bereiche/Allergene/Zusatzstoffe mit EN/PL-Namen, automatische
  Übersetzung über DeepL (optional, DEEPL_API_KEY) mit manueller
  Korrekturmöglichkeit
- Rezeptvorschläge: öffentliches Formular mit Dubletten-Check und
  Honeypot-Spamschutz, Sichtung/Übernahme im Kantinen-Bereich
- Fix: Passwort-wiederholen-Feld beim Bearbeiten erschien erst nach
  Tippen im ersten Feld
- Fix: Anmelden-Button überlappte auf Mobilgeräten mit breiten Logos

Version 1.3.0
v1.3.0
2026-07-18 02:12:23 +02:00
florian.wilde a5b68d577d Version 1.3.0 2026-07-18 02:08:04 +02:00
florian.wilde babc55e781 Rezeptvorschläge: öffentliches Formular + Sichtung im Kantinen-Bereich
Nicht angemeldete Besucher können unter /vorschlag ein fehlendes
Gericht vorschlagen (Name + optionale Beschreibung); Dublettenprüfung
gegen bestehende Gerichte, einfacher Honeypot gegen Bot-Spam. Staff/
Admin sichten eingereichte Vorschläge unter /intern/vorschlaege und
übernehmen sie als Gericht oder lehnen sie ab.
2026-07-18 02:08:03 +02:00
florian.wilde a6165de10c Mehrsprachige Oberfläche + lokalisierte Gericht-/Bereichsnamen
Alle Seiten und Komponenten (öffentliche Speisekarte, Login, Kantinen-
und Admin-Bereich, eigenes Konto) verwenden jetzt die Wörterbücher aus
lib/i18n statt fest codierter deutscher Texte. Gerichte/Bereiche/
Allergene/Zusatzstoffe zeigen den Namen zur aktuell gewählten Sprache;
Gerichte- und Kennzeichnungs-Formulare bekommen Englisch-/Polnisch-
Felder mit Übersetzen-Button (siehe vorherigen Commit). Datumsformat-
ierung (formatDateLong) nimmt jetzt ein locale-Argument statt fix
'de-DE'.
2026-07-18 02:08:03 +02:00
florian.wilde 30a02ba7b7 DeepL-Anbindung für automatische Übersetzung
app/api/translate ruft DeepL für EN+PL auf und schreibt das Ergebnis
in die jeweilige _en/_pl-Spalte (Staff/Admin-only, Service-Role-Key).
Ohne DEEPL_API_KEY (neue Docker-Variable) antwortet die Route mit
configured:false, das Frontend zeigt dann den Hinweis zur manuellen
Eingabe statt eines Fehlers.
2026-07-18 02:08:02 +02:00
florian.wilde 1cc19de221 Migration: Übersetzungsspalten für Inhalte + Rezeptvorschläge-Tabelle
dishes/sections/allergens/additives bekommen _en/_pl-Spalten; die 14
EU-Allergene, 11 Zusatzstoff-Kennzeichnungen und 4 Bereiche sind direkt
mit Referenz-Übersetzungen befüllt. localized() liefert den Text zur
aktuellen Sprache mit Fallback auf Deutsch. Neue Tabelle
dish_suggestions (RLS: jeder darf einreichen, nur Staff/Admin lesen).
Lokale PostgreSQL-Kopie auf den neuen Stand gebracht.
2026-07-18 02:08:01 +02:00
florian.wilde 33909c660b i18n-Fundament: Cookie-basierte Sprachumschaltung (DE/EN/PL)
Wörterbücher unter lib/i18n statt next-intl (Next.js 16 ist neu genug,
dass eine externe Routing-Middleware zusätzliches Risiko bedeutet
hätte); Sprache liegt in einem Cookie, kein URL-Präfix nötig, dadurch
bleibt proxy.ts unverändert. LocaleProvider stellt Wörterbuch + Sprache
per Context für Client-Komponenten bereit (lib/i18n/shared.ts ist
bewusst getrennt von lib/i18n/index.ts, damit next/headers nicht in
den Client-Bundle gelangt).
2026-07-18 02:08:01 +02:00
florian.wilde d96faad4d5 Changelog: 1.3.0 vorbereiten (geplante Features, bereits behobene Bugs) 2026-07-18 01:29:57 +02:00
florian.wilde 0c4fad9649 Fix: Anmelden-Button überlappte auf Mobilgeräten mit breitem Logo
Absolute Positionierung im Header kollidierte mit Logos in breitem
Seitenverhältnis (z. B. Banner-Format), da das Logo bei h-20 nahezu
bildschirmbreit gerendert wird. Anmelden-Link steht jetzt auf eigener
Zeile über dem Logo statt absolut positioniert in der oberen Ecke.
2026-07-18 01:29:27 +02:00
florian.wilde 5c9df6e33b Fix: Passwort-wiederholen-Feld beim Bearbeiten erst nach Tippen sichtbar
Feld war beim Bearbeiten an form.password gebunden und erschien erst,
nachdem im ersten Feld schon getippt wurde – wirkte, als reiche ein
einziges Passwortfeld. Jetzt immer sichtbar, Pflicht bleibt weiterhin
nur, wenn tatsächlich ein neues Passwort gesetzt wird.
2026-07-18 01:29:27 +02:00
florian.wilde bb6093dbfe Changelog hinzufügen v1.2.0 2026-07-18 00:49:01 +02:00
florian.wilde 088df8a26b Version 1.2.0
Versionsnummer und README auf den aktuellen Funktionsstand gebracht.
2026-07-18 00:46:41 +02:00
florian.wilde 39181be3fb SITE_TITLE per Docker-Variable, Favicon im Admin-Bereich austauschbar
- SITE_TITLE (Default "Canteen Menu") steuert Browser-Tab-Titel und
  H1-Fallback ohne Logo
- Favicon-Upload analog zum Logo über den Design-Bereich (Storage-
  Bucket branding); generateMetadata setzt icons.icon jetzt immer
  explizit, da Next.js sonst app/favicon.ico zusätzlich einbindet –
  die Datei liegt daher als expliziter Fallback unter public/
2026-07-18 00:46:41 +02:00
florian.wilde 72459d8da5 Konto: Passwort-Anmeldung pro Nutzer deaktivierbar
Schalter im eigenen Konto, erzwingt vorher mindestens einen
registrierten Passkey (sonst Aussperrung). Durchsetzung beim Login:
nach erfolgreicher Passwort-Prüfung wird bei deaktiviertem Flag sofort
wieder abgemeldet; Passkey-Anmeldung bleibt davon unberührt.
2026-07-18 00:46:40 +02:00
florian.wilde 00745ae48b Benutzerverwaltung: Passwort-Wiederholung gegen Tippfehler
Gilt beim Anlegen (Pflicht) und beim optionalen Passwort-Reset im
Bearbeiten-Formular.
2026-07-18 00:46:39 +02:00
florian.wilde 675503e1f4 Allergene/Zusatzstoffe je Gericht auswählbar, klein angezeigt
Chip-Auswahl im Gericht-Formular (Klick zum An-/Abwählen) statt
Checkbox-Liste; Kürzel erscheinen hochgestellt neben dem Gerichtnamen
in Speisekarte, mobiler Ansicht und Wochenplan-Editor. Editor bekommt
außerdem einen Heute-Button.
2026-07-18 00:46:39 +02:00