import React from 'react'; import { useTranslation } from 'react-i18next'; import { CheckCircle2, Lock, MailWarning, User } from 'lucide-react'; import { YStack, XStack } from '@tamagui/stacks'; import { SizableText as Text } from '@tamagui/text'; import toast from 'react-hot-toast'; import { MobileShell } from './components/MobileShell'; import { MobileCard, CTAButton, PillBadge } from './components/Primitives'; import { MobileField, MobileInput, MobileSelect } from './components/FormControls'; import { fetchTenantProfile, updateTenantProfile, type TenantAccountProfile } from '../api'; import { getApiErrorMessage, getApiValidationMessage } from '../lib/apiError'; import { ADMIN_PROFILE_PATH } from '../constants'; import { useBackNavigation } from './hooks/useBackNavigation'; import { useAdminTheme } from './theme'; import i18n from '../i18n'; type ProfileFormState = { name: string; email: string; preferredLocale: string; currentPassword: string; password: string; passwordConfirmation: string; }; const LOCALE_OPTIONS = [ { value: '', labelKey: 'profile.locale.auto', fallback: 'Automatisch' }, { value: 'de', label: 'Deutsch' }, { value: 'en', label: 'English' }, ]; export default function MobileProfileAccountPage() { const { t } = useTranslation('settings'); const { text, muted, danger, subtle, primary, accentSoft } = useAdminTheme(); const back = useBackNavigation(ADMIN_PROFILE_PATH); const [profile, setProfile] = React.useState(null); const [form, setForm] = React.useState({ name: '', email: '', preferredLocale: '', currentPassword: '', password: '', passwordConfirmation: '', }); const [loading, setLoading] = React.useState(true); const [savingAccount, setSavingAccount] = React.useState(false); const [savingPassword, setSavingPassword] = React.useState(false); const [error, setError] = React.useState(null); const loadErrorMessage = t('profile.errors.load', 'Profil konnte nicht geladen werden.'); const dateFormatter = React.useMemo( () => new Intl.DateTimeFormat(i18n.language || 'de', { day: '2-digit', month: 'long', year: 'numeric', }), [i18n.language], ); React.useEffect(() => { (async () => { setLoading(true); try { const data = await fetchTenantProfile(); setProfile(data); setForm((prev) => ({ ...prev, name: data.name ?? '', email: data.email ?? '', preferredLocale: data.preferred_locale ?? '', })); setError(null); } catch (err) { setError(getApiErrorMessage(err, loadErrorMessage)); } finally { setLoading(false); } })(); }, []); const verifiedAt = profile?.email_verified_at ? new Date(profile.email_verified_at) : null; const verifiedDate = verifiedAt ? dateFormatter.format(verifiedAt) : null; const emailStatusLabel = profile?.email_verified ? t('profile.status.emailVerified', 'E-Mail bestätigt') : t('profile.status.emailNotVerified', 'Bestätigung erforderlich'); const emailHint = profile?.email_verified ? t('profile.status.verifiedHint', 'Bestätigt am {{date}}.', { date: verifiedDate ?? '' }) : t('profile.status.unverifiedHint', 'Bei Änderung der E-Mail senden wir dir automatisch eine neue Bestätigung.'); const buildPayload = (includePassword: boolean) => ({ name: form.name.trim(), email: form.email.trim(), preferred_locale: form.preferredLocale ? form.preferredLocale : null, ...(includePassword ? { current_password: form.currentPassword, password: form.password, password_confirmation: form.passwordConfirmation, } : {}), }); const handleAccountSave = async () => { setSavingAccount(true); try { const updated = await updateTenantProfile(buildPayload(false)); setProfile(updated); setError(null); toast.success(t('profile.toasts.updated', 'Profil wurde aktualisiert.')); } catch (err) { const message = getApiValidationMessage(err, t('profile.errors.update', 'Profil konnte nicht aktualisiert werden.')); setError(message); toast.error(message); } finally { setSavingAccount(false); } }; const handlePasswordSave = async () => { setSavingPassword(true); try { const updated = await updateTenantProfile(buildPayload(true)); setProfile(updated); setError(null); setForm((prev) => ({ ...prev, currentPassword: '', password: '', passwordConfirmation: '', })); toast.success(t('profile.toasts.passwordChanged', 'Passwort wurde aktualisiert.')); } catch (err) { const message = getApiValidationMessage(err, t('profile.errors.update', 'Profil konnte nicht aktualisiert werden.')); setError(message); toast.error(message); } finally { setSavingPassword(false); } }; const passwordReady = form.currentPassword.trim().length > 0 && form.password.trim().length > 0 && form.passwordConfirmation.trim().length > 0; return ( {error ? ( {error} ) : null} {form.name || profile?.email || t('profile.title', 'Profil')} {form.email || profile?.email || '—'} {profile?.email_verified ? ( ) : ( )} {emailStatusLabel} {emailHint} {t('profile.sections.account.heading', 'Account-Informationen')} {t('profile.sections.account.description', 'Passe Anzeigename, E-Mail-Adresse und Sprache der Admin-Oberfläche an.')} {loading ? ( {t('profile.loading', 'Lädt ...')} ) : ( setForm((prev) => ({ ...prev, name: event.target.value }))} placeholder={t('profile.placeholders.name', 'z. B. Hochzeitsplanung Schmidt')} hasError={false} /> setForm((prev) => ({ ...prev, email: event.target.value }))} placeholder="mail@beispiel.de" type="email" hasError={false} /> setForm((prev) => ({ ...prev, preferredLocale: event.target.value }))} > {LOCALE_OPTIONS.map((option) => ( ))} )} {t('profile.sections.password.heading', 'Passwort ändern')} {t('profile.sections.password.description', 'Wähle ein sicheres Passwort, um deinen Admin-Zugang zu schützen.')} setForm((prev) => ({ ...prev, currentPassword: event.target.value }))} placeholder="••••••••" type="password" hasError={false} /> setForm((prev) => ({ ...prev, password: event.target.value }))} placeholder="••••••••" type="password" hasError={false} /> setForm((prev) => ({ ...prev, passwordConfirmation: event.target.value }))} placeholder="••••••••" type="password" hasError={false} /> ); }