145 lines
5.4 KiB
TypeScript
145 lines
5.4 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 { Pressable } from '@tamagui/react-native-web-lite';
|
|
import { MobileShell } from './components/MobileShell';
|
|
import { MobileCard, CTAButton } from './components/Primitives';
|
|
import { useAuth } from '../auth/context';
|
|
import { fetchTenantProfile } from '../api';
|
|
import { adminPath } from '../constants';
|
|
import i18n from '../i18n';
|
|
|
|
export default function MobileProfilePage() {
|
|
const { user, logout } = useAuth();
|
|
const navigate = useNavigate();
|
|
const { t } = useTranslation('management');
|
|
|
|
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 (
|
|
<MobileShell
|
|
activeTab="profile"
|
|
title={t('profile.title', 'Profile')}
|
|
onBack={() => navigate(-1)}
|
|
>
|
|
<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('/mobile/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>
|
|
<Pressable onPress={() => navigate(adminPath('/mobile/billing#packages'))}>
|
|
<XStack alignItems="center" justifyContent="space-between" paddingVertical="$2" borderBottomWidth={1} borderColor="#e5e7eb">
|
|
<Text fontSize="$sm" color="#111827">
|
|
{t('billing.sections.packages.title', 'Packages & Billing')}
|
|
</Text>
|
|
<Settings size={18} color="#9ca3af" />
|
|
</XStack>
|
|
</Pressable>
|
|
<Pressable onPress={() => navigate(adminPath('/mobile/billing#invoices'))}>
|
|
<XStack alignItems="center" justifyContent="space-between" paddingVertical="$2">
|
|
<Text fontSize="$sm" color="#111827">
|
|
{t('billing.sections.invoices.title', 'Invoices & Payments')}
|
|
</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'));
|
|
}}
|
|
/>
|
|
</MobileShell>
|
|
);
|
|
}
|