Files
cesizen-individuel/mobile/app/auth/login.tsx
T
Azmog 2ec2a78469 fix: corrige les dépendances mobile et la qualité du code frontend
Mobile (Expo SDK 53) :
- supprime victory-native et react-native-svg (dépendances mortes, non
  importées) qui empêchaient l'installation (ETARGET sur svg@~16.0.0 +
  conflit de peer reanimated/skia)
- ajoute les peers manquants expo-constants et expo-linking (requis par
  expo-router)
- aligne expo-router, react-native et react-native-screens sur le SDK 53
- ajoute package-lock.json (installs reproductibles)
- expo-doctor 18/18, tsc sans erreur

Frontend (Next.js) :
- EmotionChart : remplace 4 `any` par les types Recharts (ValueType/NameType)
- EditSaisieModal : remplace le setState dans useEffect par l'ajustement
  d'état pendant le rendu (pattern recommandé par React)
- tsc, eslint et build de production sans erreur

Backend : 15 tests passent (Auth, Admin, Tracker, RGPD).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 17:19:07 +02:00

127 lines
4.4 KiB
TypeScript

import { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, Alert, KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
import { Link, useRouter } from 'expo-router';
import { useAuthStore } from '@/lib/auth-store';
import { Colors } from '@/lib/colors';
export default function LoginScreen() {
const router = useRouter();
const { login } = useAuthStore();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const handleLogin = async () => {
if (!email || !password) {
Alert.alert('Erreur', 'Veuillez remplir tous les champs');
return;
}
setLoading(true);
try {
await login(email, password);
router.replace('/(tabs)');
} catch (error: any) {
Alert.alert('Erreur', error.response?.data?.message || 'Identifiants incorrects');
} finally {
setLoading(false);
}
};
return (
<KeyboardAvoidingView style={styles.container} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<ScrollView contentContainerStyle={styles.scrollContent} keyboardShouldPersistTaps="handled">
<View style={styles.header}>
<Text style={styles.logo}>CESIZen</Text>
<Text style={styles.subtitle}>Votre compagnon de sante mentale</Text>
</View>
<View style={styles.form}>
<Text style={styles.title}>Connexion</Text>
<View style={styles.inputGroup}>
<Text style={styles.label}>Email</Text>
<TextInput
style={styles.input}
value={email}
onChangeText={setEmail}
placeholder="votre@email.com"
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Mot de passe</Text>
<TextInput
style={styles.input}
value={password}
onChangeText={setPassword}
placeholder="Votre mot de passe"
secureTextEntry
/>
</View>
<TouchableOpacity
style={[styles.button, loading && styles.buttonDisabled]}
onPress={handleLogin}
disabled={loading}
>
<Text style={styles.buttonText}>{loading ? 'Connexion...' : 'Se connecter'}</Text>
</TouchableOpacity>
<Link href="/auth/forgot-password" asChild>
<TouchableOpacity style={styles.forgotLink}>
<Text style={styles.link}>Mot de passe oublié ?</Text>
</TouchableOpacity>
</Link>
<View style={styles.footer}>
<Text style={styles.footerText}>Pas encore de compte ? </Text>
<Link href="/auth/register" asChild>
<TouchableOpacity>
<Text style={styles.link}>S'inscrire</Text>
</TouchableOpacity>
</Link>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: Colors.white },
scrollContent: { flexGrow: 1, justifyContent: 'center', padding: 24 },
header: { alignItems: 'center', marginBottom: 40 },
logo: { fontSize: 36, fontWeight: '800', color: Colors.black },
subtitle: { fontSize: 16, color: Colors.gray[500], marginTop: 8 },
form: { backgroundColor: Colors.gray[50], borderRadius: 24, padding: 24 },
title: { fontSize: 24, fontWeight: '700', color: Colors.black, marginBottom: 24 },
inputGroup: { marginBottom: 16 },
label: { fontSize: 14, fontWeight: '600', color: Colors.gray[700], marginBottom: 6 },
input: {
backgroundColor: Colors.white,
borderWidth: 1,
borderColor: Colors.gray[300],
borderRadius: 12,
paddingHorizontal: 16,
paddingVertical: 14,
fontSize: 16,
color: Colors.black,
},
button: {
backgroundColor: Colors.primary,
borderRadius: 12,
paddingVertical: 16,
alignItems: 'center',
marginTop: 8,
},
buttonDisabled: { opacity: 0.6 },
buttonText: { fontSize: 16, fontWeight: '700', color: Colors.black },
forgotLink: { alignItems: 'center', marginTop: 12 },
footer: { flexDirection: 'row', justifyContent: 'center', marginTop: 16 },
footerText: { color: Colors.gray[500], fontSize: 14 },
link: { color: Colors.secondary, fontSize: 14, fontWeight: '600' },
});