299 lines
11 KiB
TypeScript
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>
|
|
);
|
|
}
|