From 63870ba2e68c1d3e165906a6716693e3738ae463 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Mon, 20 Jul 2026 12:49:04 +0200 Subject: [PATCH] fix: Schloss-Symbol statt Text + durchgehendes Geschlossen-Banner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ersetzt den "Tag schließen"-Textlink durch ein Schloss-Symbol (🔓/🔒) und lässt den "Geschlossen"-Hinweis eines geschlossenen Tages über alle Essenszeiten-Zeilen durchlaufen (per rowSpan), statt ihn in jeder Zeile einzeln zu wiederholen – sowohl im Wochenplan als auch auf der öffentlichen Speisekarte. --- CHANGELOG.md | 11 +++ app/globals.css | 4 + app/page.tsx | 92 +++++++++++----------- components/PublicMobileMenu.tsx | 3 +- components/WeekEditor.tsx | 130 ++++++++++++++++++++------------ package.json | 2 +- 6 files changed, 149 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a658e51..02a6821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ Alle nennenswerten Änderungen an diesem Projekt werden hier dokumentiert. Format angelehnt an [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), Versionierung nach [Semantic Versioning](https://semver.org/lang/de/). +## [1.12.1] – 2026-07-20 + +### Geändert +- Bedienung geschlossener Tage im Wochenplan überarbeitet: Statt eines + Text-Links am Datum markiert jetzt ein Schloss-Symbol (🔓 zum + Schließen, 🔒 zeigt „geschlossen" an und öffnet per Klick wieder) den + Tag. Ein geschlossener Tag zeigt sowohl im Wochenplan als auch auf + der öffentlichen Speisekarte (Desktop-Tabelle) einen einzigen, über + alle Essenszeiten-Zeilen durchgehenden „Geschlossen"-Hinweis statt + einer Wiederholung in jeder einzelnen Zeile. + ## [1.12.0] – 2026-07-20 ### Hinzugefügt diff --git a/app/globals.css b/app/globals.css index 1abad04..92137f4 100644 --- a/app/globals.css +++ b/app/globals.css @@ -102,6 +102,10 @@ body { font-size: 0.85rem; } .menu-table tr:nth-child(even) td.cell { background: var(--c-row-bg-alt); } +.menu-table td.cell.closed-cell { + vertical-align: middle; + background: #fee2e2; +} .menu-table td.cell .dish { margin-bottom: 0.35rem; } .menu-table td.cell .dish sup { font-size: 0.6rem; diff --git a/app/page.tsx b/app/page.tsx index 8c03d5d..2e2346b 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -160,58 +160,62 @@ export default async function Home({ - {days.map((d) => { - const closed = closedDayFor(d.iso); - return ( - - {d.weekday} -
- {formatDateLong(d.iso, dict.dateLocale)} - {closed && ( -
- - {dict.publicMenu.closedLabel} - - {localized(closed, locale) && ( -
- {localized(closed, locale)} -
- )} -
- )} - - ); - })} + {days.map((d) => ( + + {d.weekday} +
+ {formatDateLong(d.iso, dict.dateLocale)} + + ))} - {sections.map((s) => ( + {sections.map((s, sIdx) => (
{localized(s, locale)}
{s.time_range}
- {days.map((d) => ( - - {closedDayFor(d.iso) - ? null - : entriesFor(entries, d.iso, s.key).map((e) => { - const codes = dishCodes(e.dish); - return ( -
- - {localized(e.dish, locale)} - - {labelingDetailed && codes && ( - {codes} - )} - {" – "} - {formatPrice(e.dish.price)} -
- ); - })} - - ))} + {days.map((d) => { + const closed = closedDayFor(d.iso); + if (closed) { + if (sIdx > 0) return null; + return ( + +
+ + 🔒 + + {dict.publicMenu.closedLabel} + {localized(closed, locale) && ( + + {localized(closed, locale)} + + )} +
+ + ); + } + return ( + + {entriesFor(entries, d.iso, s.key).map((e) => { + const codes = dishCodes(e.dish); + return ( +
+ + {localized(e.dish, locale)} + + {labelingDetailed && codes && ( + {codes} + )} + {" – "} + {formatPrice(e.dish.price)} +
+ ); + })} + + ); + })} ))} diff --git a/components/PublicMobileMenu.tsx b/components/PublicMobileMenu.tsx index e118c4a..46b1bdd 100644 --- a/components/PublicMobileMenu.tsx +++ b/components/PublicMobileMenu.tsx @@ -69,7 +69,8 @@ export default function PublicMobileMenu({ {day.weekday} · {formatDateLong(day.iso, dict.dateLocale)} {closed ? ( -
+
+ 🔒{" "} {dict.publicMenu.closedLabel} {localized(closed, locale) && – {localized(closed, locale)}}
diff --git a/components/WeekEditor.tsx b/components/WeekEditor.tsx index bbfa47c..f64eca1 100644 --- a/components/WeekEditor.tsx +++ b/components/WeekEditor.tsx @@ -208,38 +208,49 @@ export default function WeekEditor({ ? dishes.filter((d) => localized(d, locale).toLowerCase().includes(query)) : dishes; - // Kleine Anzeige/Umschaltung unter dem Tagesdatum: "Geschlossen" + - // Wieder-öffnen-Button, sonst ein dezenter "Tag schließen"-Link. - function renderClosureBadge(iso: string) { - const closed = closedDayFor(iso); - if (closed) { - return ( -
- - {dict.weekEditor.closedLabel} - - -
- ); - } + // Kleines Schloss-Symbol neben dem Tagesdatum: 🔓 zum Schließen + // anklicken, 🔒 zeigt einen geschlossenen Tag und öffnet ihn per Klick + // wieder – der Zustand des Symbols selbst ist die Beschriftung. + function renderClosureToggle(iso: string) { + const closed = Boolean(closedDayFor(iso)); return ( ); } + // Banner, das bei einem geschlossenen Tag alle Essenszeiten-Zellen + // dieses Tages ersetzt (Desktop: eine über rowSpan verbundene Zelle, + // Mobil: anstelle der Essenszeiten-Karten) – "Bearbeiten" öffnet das + // Formular erneut, um nur den Grund anzupassen, ohne den Tag über das + // Symbol im Header erst wieder öffnen zu müssen. + function renderClosedBanner(iso: string, closed: ClosedDay) { + return ( +
+ + 🔒 + + {dict.weekEditor.closedLabel} + {closed.reason && {closed.reason}} + +
+ ); + } + // Formular zum Schließen/Bearbeiten eines Tages – ein gemeinsames // Formular für Desktop-Tabelle und mobile Tagesansicht, da immer nur // ein Tag gleichzeitig bearbeitet werden kann. @@ -330,7 +341,6 @@ export default function WeekEditor({ // unabhängig davon, welche Ansicht gerade rendert. function renderCell(iso: string, sectionKey: string) { const isAdding = adding?.iso === iso && adding?.section === sectionKey; - const closed = Boolean(closedDayFor(iso)); return ( <> {entriesFor(entries, iso, sectionKey).map((e) => { @@ -353,7 +363,7 @@ export default function WeekEditor({
); })} - {closed ? null : isAdding ? ( + {isAdding ? (
{selectedDishObj ? (
@@ -484,22 +494,34 @@ export default function WeekEditor({ ))}

- {selectedDay.weekday} · {formatDateLong(selectedDay.iso, dict.dateLocale)} + {selectedDay.weekday} · {formatDateLong(selectedDay.iso, dict.dateLocale)}{" "} + {renderClosureToggle(selectedDay.iso)}

-
{renderClosureBadge(selectedDay.iso)}
-
- {sections.map((s) => ( -
-
- {localized(s, locale)}{" "} - · {s.time_range} -
-
- {renderCell(selectedDay.iso, s.key)} + {(() => { + const closed = closedDayFor(selectedDay.iso); + if (closed) { + return ( +
+ {renderClosedBanner(selectedDay.iso, closed)}
+ ); + } + return ( +
+ {sections.map((s) => ( +
+
+ {localized(s, locale)}{" "} + · {s.time_range} +
+
+ {renderCell(selectedDay.iso, s.key)} +
+
+ ))}
- ))} -
+ ); + })()}
{/* Ab "sm": klassische Wochentabelle */} @@ -512,24 +534,38 @@ export default function WeekEditor({ {d.weekday}
- {formatDateLong(d.iso, dict.dateLocale)} - {renderClosureBadge(d.iso)} + {formatDateLong(d.iso, dict.dateLocale)} {renderClosureToggle(d.iso)} ))} - {sections.map((s) => ( + {sections.map((s, sIdx) => (
{localized(s, locale)}
{s.time_range}
- {days.map((d) => ( - - {renderCell(d.iso, s.key)} - - ))} + {days.map((d) => { + const closed = closedDayFor(d.iso); + if (closed) { + if (sIdx > 0) return null; + return ( + + {renderClosedBanner(d.iso, closed)} + + ); + } + return ( + + {renderCell(d.iso, s.key)} + + ); + })} ))} diff --git a/package.json b/package.json index b09aae8..a18cd57 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "canteen-menu", - "version": "1.12.0", + "version": "1.12.1", "private": true, "scripts": { "predev": "node --env-file-if-exists=.env.local db/migrate.mjs && node --env-file-if-exists=.env.local scripts/bootstrap-admin.mjs",