200 lines
5.2 KiB
TypeScript
200 lines
5.2 KiB
TypeScript
import { useMemo, useState } from 'react';
|
|
import { Redirect, router } from 'expo-router';
|
|
import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native';
|
|
|
|
import { useAuth, useTokenStorageInfo } from '@/src/providers/auth-provider';
|
|
|
|
const PRIMARY = '#69c350';
|
|
|
|
export default function TenantSelectScreen() {
|
|
const { token, tenants, activeTenantId, requiresTenantSelection, switchTenant, user, logout } = useAuth();
|
|
const storageInfo = useTokenStorageInfo();
|
|
|
|
const [switchingTenantId, setSwitchingTenantId] = useState<number | null>(null);
|
|
const [search, setSearch] = useState('');
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const filteredTenants = useMemo(() => {
|
|
const terms = search.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
if (terms.length === 0) return tenants;
|
|
|
|
return tenants.filter((tenant) => {
|
|
const haystack = `${String(tenant.name || '').toLowerCase()} ${String(tenant.id || '').toLowerCase()} ${
|
|
String(tenant.short || '').toLowerCase()
|
|
}`;
|
|
return terms.every((term) => haystack.includes(term));
|
|
});
|
|
}, [search, tenants]);
|
|
|
|
if (!token) {
|
|
return <Redirect href="/login" />;
|
|
}
|
|
|
|
if (!requiresTenantSelection && activeTenantId) {
|
|
return <Redirect href="/(tabs)" />;
|
|
}
|
|
|
|
async function onSelectTenant(tenantId: number) {
|
|
setSwitchingTenantId(tenantId);
|
|
setError(null);
|
|
|
|
try {
|
|
await switchTenant(tenantId);
|
|
router.replace('/(tabs)');
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Tenant konnte nicht gewechselt werden.');
|
|
} finally {
|
|
setSwitchingTenantId(null);
|
|
}
|
|
}
|
|
|
|
async function onLogout() {
|
|
setSwitchingTenantId(null);
|
|
await logout();
|
|
router.replace('/login');
|
|
}
|
|
|
|
return (
|
|
<ScrollView contentContainerStyle={styles.container}>
|
|
<View style={styles.headerCard}>
|
|
<Text style={styles.title}>Tenant auswählen</Text>
|
|
<Text style={styles.subtitle}>Wähle den Mandanten für diese Session.</Text>
|
|
<Text style={styles.meta}>User: {String(user?.email || user?.id || 'unbekannt')}</Text>
|
|
<Text style={styles.meta}>Storage: {storageInfo.mode}</Text>
|
|
</View>
|
|
|
|
{error ? <Text style={styles.error}>{error}</Text> : null}
|
|
|
|
<TextInput
|
|
value={search}
|
|
onChangeText={setSearch}
|
|
placeholder="Tenant suchen"
|
|
placeholderTextColor="#9ca3af"
|
|
style={styles.searchInput}
|
|
/>
|
|
|
|
{filteredTenants.length === 0 ? <Text style={styles.empty}>Keine passenden Tenants gefunden.</Text> : null}
|
|
|
|
{filteredTenants.map((tenant) => {
|
|
const tenantId = Number(tenant.id);
|
|
const isBusy = switchingTenantId === tenantId;
|
|
|
|
return (
|
|
<Pressable
|
|
key={String(tenant.id)}
|
|
style={[styles.tenantButton, isBusy ? styles.tenantButtonDisabled : null]}
|
|
onPress={() => onSelectTenant(tenantId)}
|
|
disabled={switchingTenantId !== null}>
|
|
<View style={styles.tenantInfo}>
|
|
<Text style={styles.tenantName} numberOfLines={1}>
|
|
{tenant.name}
|
|
</Text>
|
|
<Text style={styles.tenantMeta}>ID: {tenantId}</Text>
|
|
</View>
|
|
{isBusy ? <ActivityIndicator color={PRIMARY} /> : <Text style={styles.tenantAction}>Auswählen</Text>}
|
|
</Pressable>
|
|
);
|
|
})}
|
|
|
|
<Pressable style={styles.logoutButton} onPress={onLogout} disabled={switchingTenantId !== null}>
|
|
<Text style={styles.logoutText}>Anderen Nutzer anmelden</Text>
|
|
</Pressable>
|
|
</ScrollView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flexGrow: 1,
|
|
padding: 16,
|
|
gap: 12,
|
|
backgroundColor: '#f9fafb',
|
|
},
|
|
headerCard: {
|
|
borderRadius: 12,
|
|
borderWidth: 1,
|
|
borderColor: '#e5e7eb',
|
|
backgroundColor: '#ffffff',
|
|
padding: 14,
|
|
gap: 4,
|
|
},
|
|
title: {
|
|
fontSize: 22,
|
|
fontWeight: '700',
|
|
color: '#111827',
|
|
},
|
|
subtitle: {
|
|
color: '#6b7280',
|
|
marginBottom: 2,
|
|
},
|
|
meta: {
|
|
color: '#6b7280',
|
|
fontSize: 12,
|
|
},
|
|
error: {
|
|
color: '#dc2626',
|
|
fontSize: 13,
|
|
},
|
|
searchInput: {
|
|
borderWidth: 1,
|
|
borderColor: '#d1d5db',
|
|
borderRadius: 10,
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 10,
|
|
fontSize: 15,
|
|
color: '#111827',
|
|
backgroundColor: '#ffffff',
|
|
},
|
|
tenantButton: {
|
|
borderRadius: 12,
|
|
padding: 14,
|
|
borderWidth: 1,
|
|
borderColor: '#d1d5db',
|
|
backgroundColor: '#ffffff',
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
tenantButtonDisabled: {
|
|
opacity: 0.6,
|
|
},
|
|
tenantInfo: {
|
|
flex: 1,
|
|
minWidth: 0,
|
|
paddingRight: 10,
|
|
},
|
|
tenantName: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
color: '#111827',
|
|
},
|
|
tenantMeta: {
|
|
color: '#6b7280',
|
|
marginTop: 4,
|
|
},
|
|
tenantAction: {
|
|
color: PRIMARY,
|
|
fontWeight: '600',
|
|
},
|
|
empty: {
|
|
color: '#6b7280',
|
|
fontSize: 13,
|
|
textAlign: 'center',
|
|
paddingVertical: 8,
|
|
},
|
|
logoutButton: {
|
|
marginTop: 4,
|
|
minHeight: 42,
|
|
borderRadius: 10,
|
|
borderWidth: 1,
|
|
borderColor: '#d1d5db',
|
|
backgroundColor: '#ffffff',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
logoutText: {
|
|
color: '#374151',
|
|
fontWeight: '600',
|
|
},
|
|
});
|