import { useCallback, useState } from 'react'; import { Redirect, router, useFocusEffect } from 'expo-router'; import { ActivityIndicator, KeyboardAvoidingView, Modal, Platform, Pressable, StyleSheet, Text, TextInput, View, } from 'react-native'; import { getApiBaseUrlSync, hydrateApiBaseUrl, isServerSetupDone, markServerSetupDone, setApiBaseUrl as persistApiBaseUrl, } from '@/src/lib/server-config'; import { useAuth, useTokenStorageInfo } from '@/src/providers/auth-provider'; const PRIMARY = '#69c350'; function isValidServerUrl(value: string): boolean { return /^https?:\/\/.+/i.test(value.trim()); } export default function LoginScreen() { const { token, requiresTenantSelection, login } = useAuth(); const storageInfo = useTokenStorageInfo(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [apiBaseUrl, setApiBaseUrl] = useState(getApiBaseUrlSync()); const [serverInput, setServerInput] = useState(getApiBaseUrlSync()); const [showServerModal, setShowServerModal] = useState(false); const [isServerSetupRequired, setIsServerSetupRequired] = useState(false); const [isServerSaving, setIsServerSaving] = useState(false); const [serverError, setServerError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); useFocusEffect( useCallback(() => { let active = true; async function refreshApiBase() { const current = await hydrateApiBaseUrl(); const setupDone = await isServerSetupDone(); if (!active) return; setApiBaseUrl(current); setServerInput(current); setIsServerSetupRequired(!setupDone); setShowServerModal(!setupDone); } void refreshApiBase(); return () => { active = false; }; }, []) ); if (token) { return ; } async function applyDefaultServer() { setIsServerSaving(true); setServerError(null); try { await markServerSetupDone(); setIsServerSetupRequired(false); setShowServerModal(false); setApiBaseUrl(getApiBaseUrlSync()); } catch (err) { setServerError(err instanceof Error ? err.message : 'Server-Einstellung konnte nicht gespeichert werden.'); } finally { setIsServerSaving(false); } } async function saveCustomServer() { setServerError(null); const value = serverInput.trim(); if (!isValidServerUrl(value)) { setServerError('Bitte eine gültige URL mit http:// oder https:// eingeben.'); return; } setIsServerSaving(true); try { const normalized = await persistApiBaseUrl(value); setApiBaseUrl(normalized); setServerInput(normalized); setIsServerSetupRequired(false); setShowServerModal(false); } catch (err) { setServerError(err instanceof Error ? err.message : 'Server-Einstellung konnte nicht gespeichert werden.'); } finally { setIsServerSaving(false); } } async function onSubmit() { setIsSubmitting(true); setError(null); try { await login(email.trim(), password); router.replace('/'); } catch (err) { setError(err instanceof Error ? err.message : 'Login fehlgeschlagen.'); } finally { setIsSubmitting(false); } } return ( FEDEO Mobile Login mit anschliessender Tenant-Auswahl E-Mail Passwort {error ? {error} : null} {isSubmitting ? : Anmelden} setShowServerModal(true)}> Eigenen Server festlegen API: {apiBaseUrl} Token Storage: {storageInfo.mode} {}}> Server-Instanz festlegen Vor dem ersten Login bitte Server wählen. Standard verwenden oder eigene Instanz hinterlegen. {serverError ? {serverError} : null} {isServerSaving ? 'Speichern...' : 'Eigene Instanz speichern'} Standardserver verwenden ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', padding: 20, backgroundColor: '#f3f4f6', }, card: { backgroundColor: '#ffffff', borderRadius: 16, padding: 20, gap: 10, shadowColor: '#111827', shadowOpacity: 0.08, shadowRadius: 16, shadowOffset: { width: 0, height: 8 }, elevation: 3, }, title: { fontSize: 24, fontWeight: '700', color: '#111827', }, subtitle: { color: '#6b7280', marginBottom: 8, }, label: { fontSize: 14, color: '#374151', fontWeight: '500', }, input: { borderWidth: 1, borderColor: '#d1d5db', borderRadius: 10, paddingHorizontal: 12, paddingVertical: 10, fontSize: 16, color: '#111827', }, button: { marginTop: 6, backgroundColor: PRIMARY, borderRadius: 10, minHeight: 44, alignItems: 'center', justifyContent: 'center', }, buttonDisabled: { backgroundColor: '#86efac', }, buttonText: { color: '#ffffff', fontSize: 16, fontWeight: '600', }, error: { color: '#dc2626', fontSize: 13, }, metaBox: { marginTop: 8, paddingTop: 8, borderTopWidth: 1, borderTopColor: '#e5e7eb', gap: 4, }, serverLink: { marginTop: 2, alignSelf: 'flex-start', paddingVertical: 4, }, serverLinkText: { color: PRIMARY, fontSize: 13, fontWeight: '600', }, metaText: { fontSize: 12, color: '#6b7280', }, modalBackdrop: { flex: 1, backgroundColor: 'rgba(17, 24, 39, 0.45)', justifyContent: 'center', padding: 20, }, modalCard: { backgroundColor: '#ffffff', borderRadius: 14, padding: 16, gap: 10, }, modalTitle: { color: '#111827', fontSize: 18, fontWeight: '700', }, modalText: { color: '#6b7280', fontSize: 13, lineHeight: 18, }, modalPrimaryButton: { minHeight: 42, borderRadius: 10, backgroundColor: PRIMARY, alignItems: 'center', justifyContent: 'center', }, modalPrimaryText: { color: '#ffffff', fontWeight: '700', fontSize: 14, }, modalSecondaryButton: { minHeight: 42, borderRadius: 10, borderWidth: 1, borderColor: '#d1d5db', alignItems: 'center', justifyContent: 'center', backgroundColor: '#ffffff', }, modalSecondaryText: { color: '#374151', fontWeight: '600', fontSize: 14, }, });