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>
This commit is contained in:
2026-07-19 23:10:36 +02:00
co-authored by Claude Sonnet 5
parent 5874eb31b6
commit 474467811c
15 changed files with 658 additions and 5 deletions
+42
View File
@@ -106,6 +106,13 @@ const de = {
loadError: "Laden fehlgeschlagen: {message}",
saveError: "Speichern fehlgeschlagen: {message}",
deleteError: "Löschen fehlgeschlagen: {message}",
exportButton: "Exportieren",
importButton: "Importieren",
exportError: "Export fehlgeschlagen: {message}",
importError: "Import fehlgeschlagen: {message}",
importSuccess: "{created} neu angelegt, {updated} aktualisiert.",
importSkipped: "{count} Zeile(n) übersprungen: {reasons}",
importSkippedRow: "Zeile {row}: {reason}",
},
codeList: {
allergensTitle: "Allergene",
@@ -119,6 +126,13 @@ const de = {
loadError: "Laden fehlgeschlagen: {message}",
saveError: "Speichern fehlgeschlagen: {message}",
deleteError: "Löschen fehlgeschlagen: {message}",
exportButton: "Exportieren",
importButton: "Importieren",
exportError: "Export fehlgeschlagen: {message}",
importError: "Import fehlgeschlagen: {message}",
importSuccess: "{created} neu angelegt, {updated} aktualisiert.",
importSkipped: "{count} Zeile(n) übersprungen: {reasons}",
importSkippedRow: "Zeile {row}: {reason}",
},
sectionManager: {
title: "Essenszeiten",
@@ -362,6 +376,13 @@ const en: Dict = {
loadError: "Loading failed: {message}",
saveError: "Saving failed: {message}",
deleteError: "Deleting failed: {message}",
exportButton: "Export",
importButton: "Import",
exportError: "Export failed: {message}",
importError: "Import failed: {message}",
importSuccess: "{created} created, {updated} updated.",
importSkipped: "{count} row(s) skipped: {reasons}",
importSkippedRow: "Row {row}: {reason}",
},
codeList: {
allergensTitle: "Allergens",
@@ -375,6 +396,13 @@ const en: Dict = {
loadError: "Loading failed: {message}",
saveError: "Saving failed: {message}",
deleteError: "Deleting failed: {message}",
exportButton: "Export",
importButton: "Import",
exportError: "Export failed: {message}",
importError: "Import failed: {message}",
importSuccess: "{created} created, {updated} updated.",
importSkipped: "{count} row(s) skipped: {reasons}",
importSkippedRow: "Row {row}: {reason}",
},
sectionManager: {
title: "Meal times",
@@ -616,6 +644,13 @@ const pl: Dict = {
loadError: "Wczytywanie nie powiodło się: {message}",
saveError: "Zapisywanie nie powiodło się: {message}",
deleteError: "Usuwanie nie powiodło się: {message}",
exportButton: "Eksportuj",
importButton: "Importuj",
exportError: "Eksport nie powiódł się: {message}",
importError: "Import nie powiódł się: {message}",
importSuccess: "Utworzono: {created}, zaktualizowano: {updated}.",
importSkipped: "Pominięto wierszy: {count}: {reasons}",
importSkippedRow: "Wiersz {row}: {reason}",
},
codeList: {
allergensTitle: "Alergeny",
@@ -629,6 +664,13 @@ const pl: Dict = {
loadError: "Wczytywanie nie powiodło się: {message}",
saveError: "Zapisywanie nie powiodło się: {message}",
deleteError: "Usuwanie nie powiodło się: {message}",
exportButton: "Eksportuj",
importButton: "Importuj",
exportError: "Eksport nie powiódł się: {message}",
importError: "Import nie powiódł się: {message}",
importSuccess: "Utworzono: {created}, zaktualizowano: {updated}.",
importSkipped: "Pominięto wierszy: {count}: {reasons}",
importSkippedRow: "Wiersz {row}: {reason}",
},
sectionManager: {
title: "Pory posiłków",