import React from 'react'; import { AlertTriangle, Loader2, Lock, LogOut, Mail, Moon, ShieldCheck, SunMedium, UserCog } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import AppearanceToggleDropdown from '@/components/appearance-dropdown'; import { Button } from '@/components/ui/button'; import { Switch } from '@/components/ui/switch'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { AdminLayout } from '../components/AdminLayout'; import { TenantHeroCard, FrostedSurface, tenantHeroPrimaryButtonClass, tenantHeroSecondaryButtonClass, SectionCard, SectionHeader, } from '../components/tenant'; import { useAuth } from '../auth/context'; import { ADMIN_EVENTS_PATH, ADMIN_LOGIN_PATH, ADMIN_PROFILE_PATH } from '../constants'; import { encodeReturnTo } from '../lib/returnTo'; import { getNotificationPreferences, updateNotificationPreferences, NotificationPreferences, } from '../api'; import { getApiErrorMessage } from '../lib/apiError'; import { useTranslation } from 'react-i18next'; export default function SettingsPage() { const navigate = useNavigate(); const { user, logout } = useAuth(); const { t } = useTranslation('management'); const [preferences, setPreferences] = React.useState(null); const [defaults, setDefaults] = React.useState({}); const [loadingNotifications, setLoadingNotifications] = React.useState(true); const [savingNotifications, setSavingNotifications] = React.useState(false); const [notificationError, setNotificationError] = React.useState(null); const heroDescription = t('settings.hero.description', { defaultValue: 'Gestalte das Erlebnis für dein Admin-Team – Darstellung, Benachrichtigungen und Session-Sicherheit.' }); const heroSupporting = [ t('settings.hero.summary.appearance', { defaultValue: 'Synchronisiere den Look & Feel mit dem Gästeportal oder schalte den Dark Mode frei.' }), t('settings.hero.summary.notifications', { defaultValue: 'Stimme Benachrichtigungen auf Aufgaben, Pakete und Live-Events ab.' }) ]; const accountName = user?.name ?? user?.email ?? 'Customer Admin'; const heroPrimaryAction = ( ); const heroSecondaryAction = ( ); const heroAside = (

{t('settings.hero.accountLabel', { defaultValue: 'Angemeldeter Account' })}

{accountName}

{user?.tenant_id ? (

Tenant #{user.tenant_id}

) : null}

{t('settings.hero.support', { defaultValue: 'Passe Einstellungen für dich und dein Team an – Änderungen wirken sofort im Admin.' })}

); const translateNotification = React.useCallback( (key: string, fallback?: string, options?: Record) => t(key, { defaultValue: fallback, ...(options ?? {}) }), [t], ); function handleLogout() { const target = new URL(ADMIN_LOGIN_PATH, window.location.origin); target.searchParams.set('reset-auth', '1'); target.searchParams.set('return_to', encodeReturnTo(ADMIN_EVENTS_PATH)); logout({ redirect: `${target.pathname}${target.search}` }); } React.useEffect(() => { (async () => { try { const result = await getNotificationPreferences(); setPreferences(result.preferences); setDefaults(result.defaults); } catch (error) { setNotificationError(getApiErrorMessage(error, t('settings.notifications.errorLoad', 'Benachrichtigungseinstellungen konnten nicht geladen werden.'))); } finally { setLoadingNotifications(false); } })(); }, [t]); return (

{t('settings.appearance.lightTitle', 'Heller Modus')}

{t('settings.appearance.lightCopy', 'Perfekt für Büros und klare Kontraste.')}

{t('settings.appearance.darkTitle', 'Dunkler Modus')}

{t('settings.appearance.darkCopy', 'Schonend für Nachtproduktionen oder OLED-Displays.')}

{t('settings.appearance.themeLabel', 'Theme wählen')}

{t('settings.appearance.themeHint', 'Nutze automatische Anpassung oder überschreibe das Theme manuell.')}

{user ? ( <> {t('settings.session.loggedInAs', 'Eingeloggt als')} {user.name ?? user.email ?? 'Customer Admin'} {user.tenant_id ? • Tenant #{user.tenant_id} : null} ) : ( t('settings.session.unknown', 'Aktuell kein Benutzer geladen.') )}

{t('settings.session.security', 'SSO & 2FA aktivierbar')} {t('settings.session.session', 'Session 12h gültig')}
{t('settings.session.hint', 'Bei Gerätewechsel solltest du dich kurz ab- und wieder anmelden, um Berechtigungen zu synchronisieren.')}
{notificationError ? ( {notificationError} ) : null} {loadingNotifications ? ( ) : preferences ? ( setPreferences(next)} onReset={() => setPreferences(defaults)} onSave={async () => { if (!preferences) { return; } try { setSavingNotifications(true); const updated = await updateNotificationPreferences(preferences); setPreferences(updated.preferences); if (updated.defaults && Object.keys(updated.defaults).length > 0) { setDefaults(updated.defaults); } setNotificationError(null); } catch (error) { setNotificationError( getApiErrorMessage(error, t('settings.notifications.errorSave', 'Speichern fehlgeschlagen. Bitte versuche es erneut.')), ); } finally { setSavingNotifications(false); } }} saving={savingNotifications} translate={translateNotification} /> ) : null}
); } function NotificationPreferencesForm({ preferences, defaults, onChange, onReset, onSave, saving, translate, }: { preferences: NotificationPreferences; defaults: NotificationPreferences; onChange: (next: NotificationPreferences) => void; onReset: () => void; onSave: () => Promise; saving: boolean; translate: (key: string, fallback?: string, options?: Record) => string; }) { const items = React.useMemo(() => buildPreferenceMeta(translate), [translate]); return (
{items.map((item) => { const checked = preferences[item.key] ?? defaults[item.key] ?? true; return (

{item.label}

{item.description}

onChange({ ...preferences, [item.key]: Boolean(value) })} />
); })}
{translate('settings.notifications.hint', 'Du kannst Benachrichtigungen jederzeit wieder aktivieren.')}
); } function buildPreferenceMeta( translate: (key: string, fallback?: string, options?: Record) => string ): Array<{ key: keyof NotificationPreferences; label: string; description: string }> { const map = [ { key: 'photo_thresholds', label: translate('settings.notifications.items.photoThresholds.label', 'Warnung bei Foto-Schwellen'), description: translate('settings.notifications.items.photoThresholds.description', 'Sende Warnungen bei 80 % und 95 % Foto-Auslastung.'), }, { key: 'photo_limits', label: translate('settings.notifications.items.photoLimits.label', 'Sperre bei Foto-Limit'), description: translate('settings.notifications.items.photoLimits.description', 'Informiere mich, sobald keine Foto-Uploads mehr möglich sind.'), }, { key: 'guest_thresholds', label: translate('settings.notifications.items.guestThresholds.label', 'Warnung bei Gästekontingent'), description: translate('settings.notifications.items.guestThresholds.description', 'Warnung kurz bevor alle Gästelinks vergeben sind.'), }, { key: 'guest_limits', label: translate('settings.notifications.items.guestLimits.label', 'Sperre bei Gästelimit'), description: translate('settings.notifications.items.guestLimits.description', 'Hinweis, wenn keine neuen Gästelinks mehr erzeugt werden können.'), }, { key: 'gallery_warnings', label: translate('settings.notifications.items.galleryWarnings.label', 'Galerie läuft bald ab'), description: translate('settings.notifications.items.galleryWarnings.description', 'Erhalte 7 und 1 Tag vor Ablauf eine Erinnerung.'), }, { key: 'gallery_expired', label: translate('settings.notifications.items.galleryExpired.label', 'Galerie ist abgelaufen'), description: translate('settings.notifications.items.galleryExpired.description', 'Informiere mich, sobald Gäste die Galerie nicht mehr sehen können.'), }, { key: 'event_thresholds', label: translate('settings.notifications.items.eventThresholds.label', 'Warnung bei Event-Kontingent'), description: translate('settings.notifications.items.eventThresholds.description', 'Hinweis, wenn das Reseller-Paket fast ausgeschöpft ist.'), }, { key: 'event_limits', label: translate('settings.notifications.items.eventLimits.label', 'Sperre bei Event-Kontingent'), description: translate('settings.notifications.items.eventLimits.description', 'Nachricht, sobald keine weiteren Events erstellt werden können.'), }, { key: 'package_expiring', label: translate('settings.notifications.items.packageExpiring.label', 'Paket läuft bald ab'), description: translate('settings.notifications.items.packageExpiring.description', 'Erinnerungen bei 30, 7 und 1 Tag vor Paketablauf.'), }, { key: 'package_expired', label: translate('settings.notifications.items.packageExpired.label', 'Paket ist abgelaufen'), description: translate('settings.notifications.items.packageExpired.description', 'Benachrichtige mich, wenn das Paket abgelaufen ist.'), }, ]; return map as Array<{ key: keyof NotificationPreferences; label: string; description: string }>; } function NotificationSkeleton() { return (
{Array.from({ length: 5 }).map((_, index) => ( ))}
); } function SupportCard() { const { t } = useTranslation('management'); return (

{t('settings.support.badge', 'Hilfe & Support')}

{t('settings.support.title', 'Team informieren')}

{t('settings.support.copy', 'Benötigst du sofortige Hilfe? Unser Support reagiert in der Regel innerhalb weniger Stunden.')}

); }