Commit Graph
9 Commits
Author SHA1 Message Date
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 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 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 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 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 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 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