import React, { useState } from 'react'; import { Image, KeyboardAvoidingView, Platform, Pressable, ScrollView, StatusBar, StyleSheet, Text, useWindowDimensions, View, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { authApi } from '../api/auth'; import type { RootStackParamList } from '../navigation/types'; import { useAuth } from '../context/AuthContext'; import { colors, radii, spacing, typography } from '../theme'; import { PrimaryButton, TextField } from '../components/ui'; type Props = NativeStackScreenProps; const BRAND_ORANGE = '#F7941D'; const BRAND_ORANGE_DARK = '#E8780A'; const BUTTON_HEIGHT = 52; const BOTTOM_BAR_PAD_Y = 16; export function LoginScreen({ navigation }: Props) { const { refreshUser } = useAuth(); const { height: windowHeight } = useWindowDimensions(); const insets = useSafeAreaInsets(); const heroHeight = Math.round(windowHeight * 0.42); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [showPw, setShowPw] = useState(false); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const submit = async () => { setError(null); const em = email.trim(); if (!em || !password) { setError('Email and password are required'); return; } setLoading(true); let res; try { res = await authApi.login(em, password); } catch { setLoading(false); setError('Network error. Check API URL and that the backend is running.'); return; } setLoading(false); if (res.error) { setError(res.error); return; } const d = res.data; if (d && 'requiresTwoFactor' in d && d.requiresTwoFactor) { navigation.navigate('Login2FA', { twoFactorToken: d.twoFactorToken, email: d.email, }); return; } await refreshUser(); navigation.replace('Events'); }; return ( {/* Orange hero β€” fixed pixel height so it always renders */} Attendance Management Welcome back Sign in to your dashboard account {error ? ( {error} ) : null} βœ‰} /> πŸ”’} right={ setShowPw((v) => !v)} hitSlop={10}> {showPw ? 'πŸ™ˆ' : 'πŸ‘'} } /> ); } const styles = StyleSheet.create({ root: { flex: 1, backgroundColor: BRAND_ORANGE, }, hero: { backgroundColor: BRAND_ORANGE, alignItems: 'center', justifyContent: 'flex-end', paddingBottom: 36, overflow: 'hidden', }, heroGradientDark: { position: 'absolute', right: 0, bottom: 0, width: '70%', height: '80%', backgroundColor: BRAND_ORANGE_DARK, opacity: 0.45, borderTopLeftRadius: 200, }, heroCircleA: { position: 'absolute', width: 176, height: 176, borderRadius: 88, right: -44, top: -36, backgroundColor: 'rgba(255,255,255,0.12)', }, heroCircleB: { position: 'absolute', width: 128, height: 128, borderRadius: 64, left: -28, bottom: 8, backgroundColor: 'rgba(255,255,255,0.10)', }, logoCard: { backgroundColor: '#fff', borderRadius: 16, paddingHorizontal: 20, paddingVertical: 16, marginBottom: 14, zIndex: 2, shadowColor: '#000', shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.12, shadowRadius: 20, elevation: 8, }, logo: { width: 144, height: 52 }, heroSubtitle: { color: 'rgba(255,255,255,0.85)', fontSize: 14, letterSpacing: 0.4, zIndex: 2, }, formArea: { flex: 1, }, sheet: { flex: 1, marginTop: -28, backgroundColor: '#fff', borderTopLeftRadius: 28, borderTopRightRadius: 28, shadowColor: '#000', shadowOffset: { width: 0, height: -4 }, shadowOpacity: 0.08, shadowRadius: 24, elevation: 12, overflow: 'hidden', }, sheetScroll: { paddingHorizontal: spacing.xl, paddingTop: 32, paddingBottom: 16, }, title: { ...typography.title, color: colors.text, marginBottom: 6 }, sub: { ...typography.body, color: colors.muted, marginBottom: 24 }, fields: { gap: 16 }, fieldIcon: { fontSize: 16, color: '#9CA3AF' }, errorBox: { backgroundColor: 'rgba(220,38,38,0.08)', borderWidth: 1, borderColor: 'rgba(220,38,38,0.18)', borderRadius: radii.md, paddingHorizontal: 14, paddingVertical: 12, marginBottom: 14, }, errorText: { ...typography.body, color: colors.error }, signInBtn: { borderRadius: 14, shadowColor: BRAND_ORANGE, shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.35, shadowRadius: 16, elevation: 6, }, bottomBar: { position: 'absolute', left: 0, right: 0, bottom: 0, paddingHorizontal: spacing.xl, paddingTop: BOTTOM_BAR_PAD_Y, backgroundColor: '#fff', }, });