Files
fotospiel-app/resources/js/admin/mobile/ProfilePage.tsx
2025-12-10 15:49:08 +01:00

133 lines
4.7 KiB
TypeScript

import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { LogOut, User, Settings, Shield, Globe, Moon } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { MobileScaffold } from './components/Scaffold';
import { MobileCard, CTAButton } from './components/Primitives';
import { BottomNav } from './components/BottomNav';
import { useAuth } from '../auth/context';
import { fetchTenantProfile } from '../api';
import { useMobileNav } from './hooks/useMobileNav';
import { adminPath } from '../constants';
import i18n from '../i18n';
export default function MobileProfilePage() {
const { user, logout } = useAuth();
const navigate = useNavigate();
const { t } = useTranslation('management');
const { go } = useMobileNav();
const [name, setName] = React.useState(user?.name ?? 'Guest');
const [email, setEmail] = React.useState(user?.email ?? '');
const [role, setRole] = React.useState<string>(user?.role ?? '');
const [theme, setTheme] = React.useState<'light' | 'dark'>('light');
const [language, setLanguage] = React.useState<string>(i18n.language || 'de');
React.useEffect(() => {
(async () => {
try {
const profile = await fetchTenantProfile();
setName(profile.name ?? name);
setEmail(profile.email ?? email);
setRole((profile as any)?.role ?? role);
} catch {
// non-fatal for mobile profile view
}
})();
}, [email, name, role]);
return (
<MobileScaffold
title={t('profile.title', 'Profile')}
onBack={() => navigate(-1)}
footer={
<BottomNav active="profile" onNavigate={go} />
}
>
<MobileCard space="$3" alignItems="center">
<XStack
width={64}
height={64}
borderRadius={20}
alignItems="center"
justifyContent="center"
backgroundColor="#e0f2fe"
>
<User size={28} color="#2563eb" />
</XStack>
<Text fontSize="$md" fontWeight="800" color="#111827">
{name}
</Text>
<Text fontSize="$sm" color="#4b5563">
{email}
</Text>
{role ? (
<Text fontSize="$xs" color="#6b7280">
{role}
</Text>
) : null}
</MobileCard>
<MobileCard space="$3">
<Text fontSize="$md" fontWeight="800" color="#111827">
{t('profile.settings', 'Settings')}
</Text>
<Pressable onPress={() => navigate(adminPath('/settings'))}>
<XStack alignItems="center" justifyContent="space-between" paddingVertical="$2" borderBottomWidth={1} borderColor="#e5e7eb">
<Text fontSize="$sm" color="#111827">
{t('profile.account', 'Account & Security')}
</Text>
<Settings size={18} color="#9ca3af" />
</XStack>
</Pressable>
<XStack alignItems="center" justifyContent="space-between" paddingVertical="$2" borderBottomWidth={1} borderColor="#e5e7eb">
<XStack space="$2" alignItems="center">
<Globe size={16} color="#6b7280" />
<Text fontSize="$sm" color="#111827">
{t('profile.language', 'Language')}
</Text>
</XStack>
<select
value={language}
onChange={(e) => {
const lng = e.target.value;
setLanguage(lng);
void i18n.changeLanguage(lng);
}}
style={{ border: '1px solid #e5e7eb', borderRadius: 10, padding: '6px 10px', background: 'white', fontSize: 13 }}
>
<option value="de">Deutsch</option>
<option value="en">English</option>
</select>
</XStack>
<XStack alignItems="center" justifyContent="space-between" paddingVertical="$2">
<XStack space="$2" alignItems="center">
<Moon size={16} color="#6b7280" />
<Text fontSize="$sm" color="#111827">
{t('profile.theme', 'Theme')}
</Text>
</XStack>
<select
value={theme}
onChange={(e) => setTheme(e.target.value as 'light' | 'dark')}
style={{ border: '1px solid #e5e7eb', borderRadius: 10, padding: '6px 10px', background: 'white', fontSize: 13 }}
>
<option value="light">{t('profile.themeLight', 'Light')}</option>
<option value="dark">{t('profile.themeDark', 'Dark')}</option>
</select>
</XStack>
</MobileCard>
<CTAButton
label={t('profile.logout', 'Log out')}
onPress={() => {
logout();
navigate(adminPath('/logout'));
}}
/>
</MobileScaffold>
);
}