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'.
This commit is contained in:
+47
-38
@@ -11,14 +11,18 @@ import {
|
||||
formatPrice,
|
||||
mondayOf,
|
||||
todayISO,
|
||||
WEEKDAYS,
|
||||
} from "@/lib/menu";
|
||||
import { formatMessage } from "@/lib/i18n/shared";
|
||||
import { localized } from "@/lib/types";
|
||||
import type { Dish, MenuEntry, Section } from "@/lib/types";
|
||||
import { useDict, useLocale } from "@/components/i18n/LocaleProvider";
|
||||
|
||||
export default function WeekEditor({ monday }: { monday: string }) {
|
||||
const supabase = useMemo(() => createClient(), []);
|
||||
const dict = useDict();
|
||||
const locale = useLocale();
|
||||
const friday = addDays(monday, 4);
|
||||
const days = WEEKDAYS.map((weekday, i) => ({
|
||||
const days = dict.weekdays.long.map((weekday, i) => ({
|
||||
weekday,
|
||||
iso: addDays(monday, i),
|
||||
}));
|
||||
@@ -42,7 +46,7 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
supabase
|
||||
.from("menu_entries")
|
||||
.select(
|
||||
"id, menu_date, section_key, position, dish:dishes(id, name, price, allergens, additives)"
|
||||
"id, menu_date, section_key, position, dish:dishes(id, name, name_en, name_pl, price, allergens, additives)"
|
||||
)
|
||||
.gte("menu_date", monday)
|
||||
.lte("menu_date", friday)
|
||||
@@ -51,14 +55,14 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
]);
|
||||
const problem = se.error ?? di.error ?? en.error;
|
||||
if (problem) {
|
||||
setError(`Laden fehlgeschlagen: ${problem.message}`);
|
||||
setError(formatMessage(dict.weekEditor.loadError, { message: problem.message }));
|
||||
} else {
|
||||
setSections(se.data as Section[]);
|
||||
setDishes(di.data as Dish[]);
|
||||
setEntries(en.data as unknown as MenuEntry[]);
|
||||
}
|
||||
setLoading(false);
|
||||
}, [supabase, monday, friday]);
|
||||
}, [supabase, monday, friday, dict]);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
@@ -75,8 +79,8 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
if (error) {
|
||||
setError(
|
||||
/duplicate key/i.test(error.message)
|
||||
? "Dieses Gericht ist an diesem Tag bereits zugewiesen."
|
||||
: `Speichern fehlgeschlagen: ${error.message}`
|
||||
? dict.weekEditor.duplicateEntry
|
||||
: formatMessage(dict.weekEditor.saveError, { message: error.message })
|
||||
);
|
||||
}
|
||||
setAdding(null);
|
||||
@@ -86,27 +90,30 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
|
||||
async function removeEntry(id: string) {
|
||||
const { error } = await supabase.from("menu_entries").delete().eq("id", id);
|
||||
if (error) setError(`Löschen fehlgeschlagen: ${error.message}`);
|
||||
if (error)
|
||||
setError(formatMessage(dict.weekEditor.deleteError, { message: error.message }));
|
||||
await load();
|
||||
}
|
||||
|
||||
const [hintBefore, hintAfter] = dict.weekEditor.newDishesHint.split("{link}");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-wrap items-center gap-3 mb-4">
|
||||
<h1 className="text-xl font-bold text-[var(--c-primary)] mr-auto">
|
||||
Wochenplan
|
||||
{dict.weekEditor.title}
|
||||
</h1>
|
||||
<Link href={`/intern?woche=${addDays(monday, -7)}`} className="btn btn-add">
|
||||
← Vorherige Woche
|
||||
{dict.publicMenu.prevWeek}
|
||||
</Link>
|
||||
<Link
|
||||
href={`/intern?woche=${mondayOf(todayISO())}`}
|
||||
className="btn btn-primary"
|
||||
>
|
||||
Heute
|
||||
{dict.publicMenu.today}
|
||||
</Link>
|
||||
<Link href={`/intern?woche=${addDays(monday, 7)}`} className="btn btn-add">
|
||||
Nächste Woche →
|
||||
{dict.publicMenu.nextWeek}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -116,7 +123,7 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
</p>
|
||||
)}
|
||||
{loading ? (
|
||||
<p className="text-gray-500">Lade Wochenplan…</p>
|
||||
<p className="text-gray-500">{dict.weekEditor.loading}</p>
|
||||
) : (
|
||||
<table className="menu-table">
|
||||
<thead>
|
||||
@@ -126,7 +133,7 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
<th key={d.iso} className="day-head">
|
||||
<span className="font-bold">{d.weekday}</span>
|
||||
<br />
|
||||
{formatDateLong(d.iso)}
|
||||
{formatDateLong(d.iso, dict.dateLocale)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
@@ -135,7 +142,7 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
{sections.map((s) => (
|
||||
<tr key={s.key}>
|
||||
<th className="section-head">
|
||||
<div>{s.label}</div>
|
||||
<div>{localized(s, locale)}</div>
|
||||
<div className="time">{s.time_range}</div>
|
||||
</th>
|
||||
{days.map((d) => {
|
||||
@@ -146,21 +153,23 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
{entriesFor(entries, d.iso, s.key).map((e) => {
|
||||
const codes = dishCodes(e.dish);
|
||||
return (
|
||||
<div key={e.id} className="dish">
|
||||
<span className="font-bold">{e.dish.name}</span>
|
||||
{codes && <sup className="dish-codes">{codes}</sup>}
|
||||
{" – "}
|
||||
{formatPrice(e.dish.price)}
|
||||
<div className="mt-1">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-del"
|
||||
onClick={() => removeEntry(e.id)}
|
||||
>
|
||||
Löschen
|
||||
</button>
|
||||
<div key={e.id} className="dish">
|
||||
<span className="font-bold">
|
||||
{localized(e.dish, locale)}
|
||||
</span>
|
||||
{codes && <sup className="dish-codes">{codes}</sup>}
|
||||
{" – "}
|
||||
{formatPrice(e.dish.price)}
|
||||
<div className="mt-1">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-del"
|
||||
onClick={() => removeEntry(e.id)}
|
||||
>
|
||||
{dict.common.delete}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{isAdding ? (
|
||||
@@ -170,10 +179,10 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
value={selectedDish}
|
||||
onChange={(e) => setSelectedDish(e.target.value)}
|
||||
>
|
||||
<option value="">– Gericht wählen –</option>
|
||||
<option value="">{dict.weekEditor.selectDish}</option>
|
||||
{dishes.map((dish) => (
|
||||
<option key={dish.id} value={dish.id}>
|
||||
{dish.name} ({formatPrice(dish.price)})
|
||||
{localized(dish, locale)} ({formatPrice(dish.price)})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -184,7 +193,7 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
onClick={addEntry}
|
||||
disabled={!selectedDish}
|
||||
>
|
||||
Hinzufügen
|
||||
{dict.common.add}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -194,7 +203,7 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
setSelectedDish("");
|
||||
}}
|
||||
>
|
||||
Abbrechen
|
||||
{dict.common.cancel}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,7 +216,7 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
setSelectedDish("");
|
||||
}}
|
||||
>
|
||||
Mahlzeit hinzufügen
|
||||
{dict.weekEditor.addMeal}
|
||||
</button>
|
||||
)}
|
||||
</td>
|
||||
@@ -219,11 +228,11 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
</table>
|
||||
)}
|
||||
<p className="mt-4 text-sm text-gray-500">
|
||||
Neue Gerichte legst du unter{" "}
|
||||
{hintBefore}
|
||||
<Link href="/intern/gerichte" className="underline">
|
||||
Gerichte
|
||||
</Link>{" "}
|
||||
an.
|
||||
{dict.weekEditor.newDishesLinkLabel}
|
||||
</Link>
|
||||
{hintAfter}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user