This commit is contained in:
@@ -0,0 +1,298 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user