import React from 'react'; import { Loader2, ShieldCheck, ShieldX, Mail, User as UserIcon, Globe, Lock } from 'lucide-react'; import toast from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import { AdminLayout } from '../components/AdminLayout'; import { useAuth } from '../auth/context'; import { fetchTenantProfile, updateTenantProfile, type TenantAccountProfile, type UpdateTenantProfilePayload, } from '../api'; import { getApiErrorMessage, isApiError } from '../lib/apiError'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; type FieldErrors = Record; function extractFieldErrors(error: unknown): FieldErrors { if (isApiError(error) && error.meta && typeof error.meta.errors === 'object') { const entries = error.meta.errors as Record; const mapped: FieldErrors = {}; Object.entries(entries).forEach(([key, value]) => { if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'string') { mapped[key] = value[0]; } }); return mapped; } return {}; } const DEFAULT_LOCALES = ['de', 'en']; const AUTO_LOCALE_OPTION = '__auto__'; export default function ProfilePage() { const { t } = useTranslation(['settings', 'common']); const { refreshProfile } = useAuth(); const [profile, setProfile] = React.useState(null); const [loading, setLoading] = React.useState(true); const [infoForm, setInfoForm] = React.useState({ name: '', email: '', preferred_locale: '', }); const [passwordForm, setPasswordForm] = React.useState({ current_password: '', password: '', password_confirmation: '', }); const [infoErrors, setInfoErrors] = React.useState({}); const [passwordErrors, setPasswordErrors] = React.useState({}); const [savingInfo, setSavingInfo] = React.useState(false); const [savingPassword, setSavingPassword] = React.useState(false); const availableLocales = React.useMemo(() => { const candidates = new Set(DEFAULT_LOCALES); if (typeof document !== 'undefined') { const lang = document.documentElement.lang; if (lang) { const short = lang.toLowerCase().split('-')[0]; candidates.add(short); } } if (profile?.preferred_locale) { candidates.add(profile.preferred_locale.toLowerCase()); } return Array.from(candidates).sort(); }, [profile?.preferred_locale]); const selectedLocale = infoForm.preferred_locale && infoForm.preferred_locale !== '' ? infoForm.preferred_locale : AUTO_LOCALE_OPTION; React.useEffect(() => { let cancelled = false; async function loadProfile(): Promise { setLoading(true); try { const data = await fetchTenantProfile(); if (cancelled) { return; } setProfile(data); setInfoForm({ name: data.name ?? '', email: data.email ?? '', preferred_locale: data.preferred_locale ?? '', }); } catch (error) { if (!cancelled) { toast.error(getApiErrorMessage(error, t('settings:profile.errors.load', 'Profil konnte nicht geladen werden.'))); } } finally { if (!cancelled) { setLoading(false); } } } void loadProfile(); return () => { cancelled = true; }; }, [t]); const handleInfoSubmit = React.useCallback( async (event: React.FormEvent) => { event.preventDefault(); setInfoErrors({}); setSavingInfo(true); const payload: UpdateTenantProfilePayload = { name: infoForm.name, email: infoForm.email, preferred_locale: infoForm.preferred_locale || null, }; try { const updated = await updateTenantProfile(payload); setProfile(updated); toast.success(t('settings:profile.toasts.updated', 'Profil wurde aktualisiert.')); setInfoForm({ name: updated.name ?? '', email: updated.email ?? '', preferred_locale: updated.preferred_locale ?? '', }); setPasswordForm((prev) => ({ ...prev, current_password: '' })); await refreshProfile(); } catch (error) { const message = getApiErrorMessage(error, t('settings:profile.errors.update', 'Profil konnte nicht aktualisiert werden.')); toast.error(message); const fieldErrors = extractFieldErrors(error); if (Object.keys(fieldErrors).length > 0) { setInfoErrors(fieldErrors); } } finally { setSavingInfo(false); } }, [infoForm.email, infoForm.name, infoForm.preferred_locale, refreshProfile, t] ); const handlePasswordSubmit = React.useCallback( async (event: React.FormEvent) => { event.preventDefault(); setPasswordErrors({}); setSavingPassword(true); const payload: UpdateTenantProfilePayload = { name: infoForm.name, email: infoForm.email, preferred_locale: infoForm.preferred_locale || null, current_password: passwordForm.current_password || undefined, password: passwordForm.password || undefined, password_confirmation: passwordForm.password_confirmation || undefined, }; try { const updated = await updateTenantProfile(payload); setProfile(updated); toast.success(t('settings:profile.toasts.passwordChanged', 'Passwort wurde aktualisiert.')); setPasswordForm({ current_password: '', password: '', password_confirmation: '' }); await refreshProfile(); } catch (error) { const message = getApiErrorMessage(error, t('settings:profile.errors.update', 'Profil konnte nicht aktualisiert werden.')); toast.error(message); const fieldErrors = extractFieldErrors(error); if (Object.keys(fieldErrors).length > 0) { setPasswordErrors(fieldErrors); } } finally { setSavingPassword(false); } }, [infoForm.email, infoForm.name, infoForm.preferred_locale, passwordForm, refreshProfile, t] ); if (loading) { return (
{t('common:loading', 'Wird geladen …')}
); } if (!profile) { return (
{t('settings:profile.errors.load', 'Profil konnte nicht geladen werden.')}
); } return ( {t('settings:profile.sections.account.heading', 'Account-Informationen')} {t('settings:profile.sections.account.description', 'Passe Name, E-Mail und Sprache deiner Admin-Oberfläche an.')}
setInfoForm((prev) => ({ ...prev, name: event.target.value }))} placeholder={t('settings:profile.placeholders.name', 'z. B. Hochzeitsplanung Schmidt')} /> {infoErrors.name &&

{infoErrors.name}

}
setInfoForm((prev) => ({ ...prev, email: event.target.value }))} placeholder="admin@example.com" /> {infoErrors.email &&

{infoErrors.email}

}
{infoErrors.preferred_locale &&

{infoErrors.preferred_locale}

}
{profile.email_verified ? t('settings:profile.status.verifiedHint', 'Bestätigt am {{date}}.', { date: profile.email_verified_at ? new Date(profile.email_verified_at).toLocaleString() : '', }) : t('settings:profile.status.unverifiedHint', 'Wir senden dir eine neue Bestätigung, sobald du die E-Mail änderst.')}
{t('settings:profile.sections.password.heading', 'Passwort ändern')} {t('settings:profile.sections.password.description', 'Wähle ein sicheres Passwort, um dein Admin-Konto zu schützen.')}
setPasswordForm((prev) => ({ ...prev, current_password: event.target.value }))} /> {passwordErrors.current_password &&

{passwordErrors.current_password}

}
setPasswordForm((prev) => ({ ...prev, password: event.target.value }))} /> {passwordErrors.password &&

{passwordErrors.password}

}
setPasswordForm((prev) => ({ ...prev, password_confirmation: event.target.value }))} /> {passwordErrors.password_confirmation &&

{passwordErrors.password_confirmation}

}
{t('settings:profile.sections.password.hint', 'Dein Passwort muss mindestens 8 Zeichen lang sein und eine Mischung aus Buchstaben und Zahlen enthalten.')}
); }