Files
DailyMeals/meal-plan-frontend-mobile/app/(auth)/login.tsx
Piotr Kus f15bb7a916 * Extended functionalities
* Added different UIs
2026-06-24 21:43:40 +02:00

118 lines
3.4 KiB
TypeScript

import { Link, router } from 'expo-router';
import { useState } from 'react';
import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, Text, View } from 'react-native';
import { useTranslation } from 'react-i18next';
import { Button } from '@/src/components/Button';
import { Input } from '@/src/components/Input';
import { Screen } from '@/src/components/Screen';
import { useAuth } from '@/src/context/AuthContext';
import { useAppearance } from '@/src/context/AppearanceContext';
import { extractApiError } from '@/src/utils/apiError';
export default function LoginScreen() {
const { t } = useTranslation();
const { login } = useAuth();
const { colors } = useAppearance();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit() {
setError(null);
if (!email.trim()) {
setError(t('validation.emailRequired'));
return;
}
if (!password) {
setError(t('validation.passwordRequired'));
return;
}
setLoading(true);
try {
await login({ email: email.trim(), password });
router.replace('/(main)/(tabs)');
} catch (err) {
setError(extractApiError(err, t('errors.signInFailed')));
} finally {
setLoading(false);
}
}
return (
<Screen>
<KeyboardAvoidingView
style={styles.flex}
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
>
<ScrollView contentContainerStyle={styles.scroll} keyboardShouldPersistTaps="handled">
<Text style={[styles.brand, { color: colors.brand }]}>{t('app.name')}</Text>
<Text style={[styles.subtitle, { color: colors.textMuted }]}>{t('auth.signInSubtitle')}</Text>
<View style={styles.form}>
<Input
label={t('auth.email')}
autoCapitalize="none"
autoComplete="email"
keyboardType="email-address"
placeholder={t('auth.emailPlaceholder')}
value={email}
onChangeText={setEmail}
/>
<Input
label={t('auth.password')}
secureTextEntry
autoComplete="password"
value={password}
onChangeText={setPassword}
/>
{error ? <Text style={[styles.error, { color: colors.danger }]}>{error}</Text> : null}
<Button
title={loading ? t('auth.signingIn') : t('auth.signIn')}
loading={loading}
onPress={handleSubmit}
/>
</View>
<Text style={[styles.footer, { color: colors.textMuted }]}>
{t('auth.noAccount')}{' '}
<Link href="/(auth)/register" style={{ color: colors.brand, fontWeight: '600' }}>
{t('auth.createOne')}
</Link>
</Text>
</ScrollView>
</KeyboardAvoidingView>
</Screen>
);
}
const styles = StyleSheet.create({
flex: { flex: 1 },
scroll: {
flexGrow: 1,
justifyContent: 'center',
gap: 16,
paddingVertical: 24,
},
brand: {
fontSize: 34,
fontWeight: '800',
},
subtitle: {
fontSize: 16,
lineHeight: 24,
},
form: {
gap: 14,
marginTop: 8,
},
error: {
fontSize: 14,
},
footer: {
textAlign: 'center',
fontSize: 15,
},
});