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:
+28
-25
@@ -2,7 +2,9 @@
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { createClient } from "@/lib/supabase/client";
|
||||
import { formatMessage } from "@/lib/i18n/shared";
|
||||
import type { Profile, Role } from "@/lib/types";
|
||||
import { useDict } from "@/components/i18n/LocaleProvider";
|
||||
|
||||
interface UserForm {
|
||||
id: string | null; // null = neuer Benutzer
|
||||
@@ -22,13 +24,13 @@ const EMPTY_FORM: UserForm = {
|
||||
passwordRepeat: "",
|
||||
};
|
||||
|
||||
const ROLE_LABELS: Record<Role, string> = {
|
||||
admin: "Administrator",
|
||||
staff: "Kantinenmitarbeiter",
|
||||
};
|
||||
|
||||
export default function UserManager() {
|
||||
const supabase = useMemo(() => createClient(), []);
|
||||
const dict = useDict();
|
||||
const roleLabels: Record<Role, string> = {
|
||||
admin: dict.common.roleAdmin,
|
||||
staff: dict.common.roleStaff,
|
||||
};
|
||||
const [profiles, setProfiles] = useState<Profile[]>([]);
|
||||
const [me, setMe] = useState<string | null>(null);
|
||||
const [form, setForm] = useState<UserForm | null>(null);
|
||||
@@ -41,10 +43,11 @@ export default function UserManager() {
|
||||
supabase.from("profiles").select("*").order("created_at"),
|
||||
supabase.auth.getUser(),
|
||||
]);
|
||||
if (profileError) setError(`Laden fehlgeschlagen: ${profileError.message}`);
|
||||
if (profileError)
|
||||
setError(formatMessage(dict.userManager.loadError, { message: profileError.message }));
|
||||
else setProfiles(profileData as Profile[]);
|
||||
setMe(userData.user?.id ?? null);
|
||||
}, [supabase]);
|
||||
}, [supabase, dict]);
|
||||
|
||||
useEffect(() => {
|
||||
load();
|
||||
@@ -77,7 +80,7 @@ export default function UserManager() {
|
||||
e.preventDefault();
|
||||
if (!form) return;
|
||||
if (form.password && form.password !== form.passwordRepeat) {
|
||||
setError("Die Passwörter stimmen nicht überein.");
|
||||
setError(dict.userManager.passwordMismatch);
|
||||
return;
|
||||
}
|
||||
const ok = form.id
|
||||
@@ -102,7 +105,7 @@ export default function UserManager() {
|
||||
async function remove(profile: Profile) {
|
||||
if (
|
||||
!window.confirm(
|
||||
`Benutzer „${profile.email}“ wirklich löschen? Der Zugang wird dauerhaft entfernt.`
|
||||
formatMessage(dict.userManager.confirmDelete, { email: profile.email })
|
||||
)
|
||||
)
|
||||
return;
|
||||
@@ -113,14 +116,14 @@ export default function UserManager() {
|
||||
<div className="max-w-3xl">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<h1 className="text-xl font-bold text-[var(--c-primary)] mr-auto">
|
||||
Benutzer
|
||||
{dict.userManager.title}
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-add"
|
||||
onClick={() => setForm(EMPTY_FORM)}
|
||||
>
|
||||
Neuer Benutzer
|
||||
{dict.userManager.newUser}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -133,12 +136,12 @@ export default function UserManager() {
|
||||
{form && (
|
||||
<form onSubmit={save} className="card mb-4 space-y-3">
|
||||
<h2 className="font-semibold">
|
||||
{form.id ? "Benutzer bearbeiten" : "Neuer Benutzer"}
|
||||
{form.id ? dict.userManager.editUser : dict.userManager.newUser}
|
||||
</h2>
|
||||
{!form.id && (
|
||||
<div>
|
||||
<label className="label" htmlFor="user-email">
|
||||
E-Mail-Adresse
|
||||
{dict.common.email}
|
||||
</label>
|
||||
<input
|
||||
id="user-email"
|
||||
@@ -152,7 +155,7 @@ export default function UserManager() {
|
||||
)}
|
||||
<div>
|
||||
<label className="label" htmlFor="user-name">
|
||||
Name
|
||||
{dict.common.name}
|
||||
</label>
|
||||
<input
|
||||
id="user-name"
|
||||
@@ -163,7 +166,7 @@ export default function UserManager() {
|
||||
</div>
|
||||
<div className="max-w-60">
|
||||
<label className="label" htmlFor="user-role">
|
||||
Rolle
|
||||
{dict.common.role}
|
||||
</label>
|
||||
<select
|
||||
id="user-role"
|
||||
@@ -173,13 +176,13 @@ export default function UserManager() {
|
||||
setForm({ ...form, role: e.target.value as Role })
|
||||
}
|
||||
>
|
||||
<option value="staff">Kantinenmitarbeiter</option>
|
||||
<option value="admin">Administrator</option>
|
||||
<option value="staff">{dict.common.roleStaff}</option>
|
||||
<option value="admin">{dict.common.roleAdmin}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="max-w-60">
|
||||
<label className="label" htmlFor="user-password">
|
||||
{form.id ? "Neues Passwort (optional)" : "Passwort"}
|
||||
{form.id ? dict.userManager.newPasswordLabel : dict.common.password}
|
||||
</label>
|
||||
<input
|
||||
id="user-password"
|
||||
@@ -194,7 +197,7 @@ export default function UserManager() {
|
||||
</div>
|
||||
<div className="max-w-60">
|
||||
<label className="label" htmlFor="user-password-repeat">
|
||||
Passwort wiederholen
|
||||
{dict.userManager.repeatPasswordLabel}
|
||||
</label>
|
||||
<input
|
||||
id="user-password-repeat"
|
||||
@@ -211,14 +214,14 @@ export default function UserManager() {
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button type="submit" className="btn btn-add" disabled={busy}>
|
||||
Speichern
|
||||
{dict.common.save}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-neutral"
|
||||
onClick={() => setForm(null)}
|
||||
>
|
||||
Abbrechen
|
||||
{dict.common.cancel}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -230,9 +233,9 @@ export default function UserManager() {
|
||||
<span className="mr-auto">
|
||||
<span className="font-semibold">{p.full_name || p.email}</span>{" "}
|
||||
<span className="text-gray-500">
|
||||
– {p.email} – {ROLE_LABELS[p.role]}
|
||||
– {p.email} – {roleLabels[p.role]}
|
||||
</span>
|
||||
{p.id === me && <span className="text-gray-400"> (du)</span>}
|
||||
{p.id === me && <span className="text-gray-400"> {dict.common.you}</span>}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
@@ -248,7 +251,7 @@ export default function UserManager() {
|
||||
})
|
||||
}
|
||||
>
|
||||
Bearbeiten
|
||||
{dict.common.edit}
|
||||
</button>
|
||||
{p.id !== me && (
|
||||
<button
|
||||
@@ -256,7 +259,7 @@ export default function UserManager() {
|
||||
className="btn btn-del"
|
||||
onClick={() => remove(p)}
|
||||
>
|
||||
Löschen
|
||||
{dict.common.delete}
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user