Files
omnex-demo-app/omnex-plateform-app/src/screens/auth/DeliveryLoginScreen.tsx
T
Xor290 877291e684
Omnex Plateform App - EAS Build / build (push) Failing after 8m4s
chore: v1.0.0
2026-08-04 18:47:47 +02:00

299 lines
11 KiB
TypeScript

import React, { useState, useRef, useMemo } from "react";
import {
View,
Text,
StyleSheet,
KeyboardAvoidingView,
ScrollView,
TouchableOpacity,
TextInput as RNTextInput,
Animated,
useWindowDimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useNavigation } from "@react-navigation/native";
import { spacing, fontSize, borderRadius } from "../../theme";
import { useTheme } from "../../context/ThemeContext";
import { useAuth } from "../../auth/AuthContext";
import { loginAdmin } from "../../api/api_admin";
import AlertModal from "../../components/ui/AlertModal";
import { useAlert } from "../../hooks/useAlert";
export default function DeliveryLoginScreen() {
const { colors } = useTheme();
const { width: screenWidth } = useWindowDimensions();
const ACCENT = colors.success;
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const { loginAdmin: authLogin } = useAuth();
const navigation = useNavigation();
const buttonScale = useRef(new Animated.Value(1)).current;
const { alert, showError, hideAlert } = useAlert();
const styles = useMemo(
() =>
StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bgPrimary },
accentBar: { height: 4, width: "100%" },
heroSection: {
alignItems: "center",
paddingTop: screenWidth < 380 ? 36 : 60,
paddingBottom: spacing.l,
},
heroBg: {
width: screenWidth < 380 ? 96 : 120,
height: screenWidth < 380 ? 96 : 120,
borderRadius: screenWidth < 380 ? 48 : 60,
justifyContent: "center",
alignItems: "center",
},
heroInner: {
width: screenWidth < 380 ? 68 : 88,
height: screenWidth < 380 ? 68 : 88,
borderRadius: screenWidth < 380 ? 34 : 44,
justifyContent: "center",
alignItems: "center",
},
heroTitle: {
fontSize: screenWidth < 380 ? fontSize.xl : fontSize.xxl,
fontWeight: "800",
color: colors.textPrimary,
marginTop: spacing.m,
letterSpacing: 0.5,
},
heroSubtitle: {
fontSize: screenWidth < 380 ? fontSize.sm : fontSize.md,
color: colors.textMuted,
marginTop: spacing.xs,
},
formSection: { paddingHorizontal: screenWidth < 380 ? spacing.l : spacing.xl, flex: 1 },
inputContainer: {
flexDirection: "row",
alignItems: "center",
backgroundColor: colors.bgSecondary,
borderRadius: borderRadius.md,
borderWidth: 1,
borderColor: colors.border,
marginBottom: spacing.m,
overflow: "hidden",
},
inputIconBox: {
width: 52,
height: 52,
justifyContent: "center",
alignItems: "center",
},
input: {
flex: 1,
color: colors.textPrimary,
fontSize: fontSize.md,
paddingVertical: 15,
paddingHorizontal: spacing.m,
},
eyeBtn: { padding: spacing.m },
loginBtn: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
height: 52,
borderRadius: borderRadius.md,
marginTop: spacing.m,
},
loginBtnText: {
color: colors.white,
fontSize: fontSize.lg,
fontWeight: "700",
},
backLink: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
marginTop: spacing.xl,
paddingVertical: spacing.m,
},
backText: {
color: colors.textMuted,
fontSize: fontSize.sm,
marginLeft: spacing.xs,
},
}),
[colors, screenWidth],
);
const handleLogin = async () => {
if (!username.trim() || !password.trim()) {
showError("Erreur", "Veuillez remplir tous les champs");
return;
}
setLoading(true);
try {
const result = await loginAdmin(username.trim(), password);
if (result.success && result.access_token) {
await authLogin(result.access_token, "livreur");
} else {
showError("Erreur", result.message || "Connexion échouée");
}
} catch {
showError("Erreur", "Erreur de connexion");
} finally {
setLoading(false);
}
};
const onPressIn = () =>
Animated.spring(buttonScale, {
toValue: 0.96,
useNativeDriver: true,
}).start();
const onPressOut = () =>
Animated.spring(buttonScale, {
toValue: 1,
useNativeDriver: true,
}).start();
return (
<KeyboardAvoidingView
style={styles.container}
behavior="height"
>
<ScrollView
contentContainerStyle={{ flexGrow: 1 }}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
>
<View style={[styles.accentBar, { backgroundColor: ACCENT }]} />
<View style={styles.heroSection}>
<View
style={[styles.heroBg, { backgroundColor: ACCENT + "12" }]}
>
<View
style={[
styles.heroInner,
{ backgroundColor: ACCENT + "20" },
]}
>
<Ionicons name="bicycle" size={48} color={ACCENT} />
</View>
</View>
<Text style={styles.heroTitle}>Livreur</Text>
<Text style={styles.heroSubtitle}>
Gestion de vos livraisons
</Text>
</View>
<View style={styles.formSection}>
<View style={styles.inputContainer}>
<View
style={[
styles.inputIconBox,
{ backgroundColor: ACCENT + "15" },
]}
>
<Ionicons
name="person-outline"
size={20}
color={ACCENT}
/>
</View>
<RNTextInput
style={styles.input}
placeholder="Nom d'utilisateur"
placeholderTextColor={colors.textMuted}
value={username}
onChangeText={setUsername}
autoCapitalize="none"
autoCorrect={false}
/>
</View>
<View style={styles.inputContainer}>
<View
style={[
styles.inputIconBox,
{ backgroundColor: ACCENT + "15" },
]}
>
<Ionicons
name="lock-closed-outline"
size={20}
color={ACCENT}
/>
</View>
<RNTextInput
style={styles.input}
placeholder="Mot de passe"
placeholderTextColor={colors.textMuted}
value={password}
onChangeText={setPassword}
secureTextEntry={!showPassword}
/>
<TouchableOpacity
onPress={() => setShowPassword(!showPassword)}
style={styles.eyeBtn}
>
<Ionicons
name={
showPassword ? "eye-off-outline" : "eye-outline"
}
size={20}
color={colors.textMuted}
/>
</TouchableOpacity>
</View>
<Animated.View style={{ transform: [{ scale: buttonScale }] }}>
<TouchableOpacity
style={[styles.loginBtn, { backgroundColor: ACCENT }]}
onPress={handleLogin}
onPressIn={onPressIn}
onPressOut={onPressOut}
disabled={loading}
activeOpacity={0.9}
>
{loading ? (
<Text style={styles.loginBtnText}>
Connexion...
</Text>
) : (
<>
<Text style={styles.loginBtnText}>
Se connecter
</Text>
<Ionicons
name="arrow-forward"
size={20}
color={colors.white}
style={{ marginLeft: spacing.s }}
/>
</>
)}
</TouchableOpacity>
</Animated.View>
<TouchableOpacity
onPress={() => navigation.goBack()}
style={styles.backLink}
>
<Ionicons
name="chevron-back"
size={18}
color={colors.textMuted}
/>
<Text style={styles.backText}>Choisir un autre rôle</Text>
</TouchableOpacity>
</View>
<AlertModal
visible={alert.visible}
type={alert.type}
title={alert.title}
message={alert.message}
onClose={hideAlert}
/>
</ScrollView>
</KeyboardAvoidingView>
);
}