Files
Speisekarte/components/PublicMobileMenu.tsx
T
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

183 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
// Mobile Ansicht der öffentlichen Speisekarte: Tag (Standard) oder Woche,
// umschaltbar ohne Seiten-Reload. Ab dem "sm"-Breakpoint übernimmt die
// normale Wochentabelle (siehe app/page.tsx), diese Komponente blendet
// sich dort per CSS aus.
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import {
addDays,
dishCodes,
entriesFor,
formatDateLong,
formatPrice,
mondayOf,
todayISO,
weekdayIndex,
} from "@/lib/menu";
import { localized } from "@/lib/types";
import type { MenuEntry, Section } from "@/lib/types";
import { useDict, useLocale } from "@/components/i18n/LocaleProvider";
import LanguageSwitcher from "@/components/i18n/LanguageSwitcher";
export default function PublicMobileMenu({
monday,
sections,
entries,
}: {
monday: string;
sections: Section[];
entries: MenuEntry[];
}) {
const router = useRouter();
const dict = useDict();
const locale = useLocale();
const days = dict.weekdays.long.map((weekday, i) => ({
weekday,
short: dict.weekdays.short[i],
iso: addDays(monday, i),
}));
const isCurrentWeek = monday === mondayOf(todayISO());
const todayIdx = isCurrentWeek ? weekdayIndex(todayISO()) : -1;
const [tab, setTab] = useState<"tag" | "woche">("tag");
const [selectedIdx, setSelectedIdx] = useState(todayIdx >= 0 ? todayIdx : 0);
function goToday() {
const currentMonday = mondayOf(todayISO());
if (currentMonday === monday) {
const idx = weekdayIndex(todayISO());
setSelectedIdx(idx >= 0 ? idx : 0);
setTab("tag");
} else {
router.push(`/?woche=${currentMonday}`);
}
}
function renderDay(day: { weekday: string; iso: string }) {
return (
<div key={day.iso} className="mb-4 last:mb-0">
<h3 className="font-bold text-[var(--c-primary)] mb-2">
{day.weekday} · {formatDateLong(day.iso, dict.dateLocale)}
</h3>
<div className="space-y-3">
{sections.map((s) => {
const items = entriesFor(entries, day.iso, s.key);
return (
<div
key={s.key}
className="rounded-md border border-gray-200 overflow-hidden"
>
<div className="bg-[var(--c-primary)] text-[var(--c-header-text)] px-3 py-1.5 text-sm">
<span className="font-semibold">{localized(s, locale)}</span>{" "}
<span className="opacity-80">· {s.time_range}</span>
</div>
<div className="bg-[var(--c-row-bg)] px-3 py-2 text-sm">
{items.length === 0 ? (
<p className="text-gray-500">{dict.publicMenu.noEntry}</p>
) : (
items.map((e) => {
const codes = dishCodes(e.dish);
return (
<p key={e.id} className="mb-1 last:mb-0">
<span className="font-semibold">
{localized(e.dish, locale)}
</span>
{codes && <sup className="dish-codes">{codes}</sup>}{" "}
– {formatPrice(e.dish.price)}
</p>
);
})
)}
</div>
</div>
);
})}
</div>
</div>
);
}
return (
<div className="sm:hidden mb-6">
<div className="flex justify-end mb-3">
<LanguageSwitcher variant="light" />
</div>
<div className="flex gap-2 mb-3">
<Link
href={`/?woche=${addDays(monday, -7)}`}
className="btn btn-add flex-1 justify-center"
>
{dict.publicMenu.prevWeekShort}
</Link>
<button
type="button"
onClick={goToday}
className="btn btn-primary flex-1"
>
{dict.publicMenu.today}
</button>
<Link
href={`/?woche=${addDays(monday, 7)}`}
className="btn btn-add flex-1 justify-center"
>
{dict.publicMenu.nextWeekShort}
</Link>
</div>
<div className="flex gap-2 mb-3" role="tablist" aria-label="Ansicht">
<button
type="button"
role="tab"
aria-selected={tab === "tag"}
onClick={() => setTab("tag")}
className={`btn flex-1 ${tab === "tag" ? "btn-primary" : "btn-neutral"}`}
>
{dict.publicMenu.dayTab}
</button>
<button
type="button"
role="tab"
aria-selected={tab === "woche"}
onClick={() => setTab("woche")}
className={`btn flex-1 ${tab === "woche" ? "btn-primary" : "btn-neutral"}`}
>
{dict.publicMenu.weekTab}
</button>
</div>
{tab === "tag" ? (
<>
<div className="flex gap-1 mb-3">
{days.map((d, i) => (
<button
key={d.iso}
type="button"
onClick={() => setSelectedIdx(i)}
className={`flex-1 rounded-md py-1.5 text-sm font-semibold border transition-colors ${
i === selectedIdx
? "bg-[var(--c-primary)] text-white border-[var(--c-primary)]"
: "bg-white text-gray-600 border-gray-200"
} ${i === todayIdx ? "ring-2 ring-[var(--c-accent)] ring-offset-1" : ""}`}
>
{d.short}
</button>
))}
</div>
{renderDay(days[selectedIdx])}
</>
) : (
<div>{days.map((d) => renderDay(d))}</div>
)}
<Link href="/vorschlag" className="btn btn-neutral w-full justify-center mt-2">
{dict.publicMenu.suggestDish}
</Link>
</div>
);
}