chore: fix UI bug
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
|||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
FlatList,
|
FlatList,
|
||||||
|
ScrollView,
|
||||||
Image,
|
Image,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
@@ -58,6 +59,7 @@ export default function CartScreen() {
|
|||||||
const [loadingMedia, setLoadingMedia] = useState(false);
|
const [loadingMedia, setLoadingMedia] = useState(false);
|
||||||
const [removeId, setRemoveId] = useState<number | null>(null);
|
const [removeId, setRemoveId] = useState<number | null>(null);
|
||||||
const [showClearModal, setShowClearModal] = useState(false);
|
const [showClearModal, setShowClearModal] = useState(false);
|
||||||
|
const [showRecapModal, setShowRecapModal] = useState(false);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
@@ -249,6 +251,94 @@ export default function CartScreen() {
|
|||||||
borderTopWidth: 1,
|
borderTopWidth: 1,
|
||||||
borderTopColor: colors.border,
|
borderTopColor: colors.border,
|
||||||
},
|
},
|
||||||
|
recapScroll: {
|
||||||
|
maxHeight: 280,
|
||||||
|
},
|
||||||
|
recapItem: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
paddingVertical: spacing.m,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: colors.borderLight,
|
||||||
|
},
|
||||||
|
recapItemImage: {
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
borderRadius: borderRadius.sm,
|
||||||
|
},
|
||||||
|
recapItemImagePlaceholder: {
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
borderRadius: borderRadius.sm,
|
||||||
|
backgroundColor: colors.bgInput,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
recapItemInfo: {
|
||||||
|
flex: 1,
|
||||||
|
marginLeft: spacing.m,
|
||||||
|
},
|
||||||
|
recapItemName: {
|
||||||
|
color: colors.textWhite,
|
||||||
|
fontSize: fontSize.sm,
|
||||||
|
fontWeight: fontWeight.semibold,
|
||||||
|
},
|
||||||
|
recapItemQty: {
|
||||||
|
color: colors.textMuted,
|
||||||
|
fontSize: fontSize.xs,
|
||||||
|
marginTop: 2,
|
||||||
|
},
|
||||||
|
recapItemPrice: {
|
||||||
|
color: colors.success,
|
||||||
|
fontSize: fontSize.sm,
|
||||||
|
fontWeight: fontWeight.bold,
|
||||||
|
},
|
||||||
|
recapDivider: {
|
||||||
|
height: 1,
|
||||||
|
backgroundColor: colors.border,
|
||||||
|
marginVertical: spacing.l,
|
||||||
|
},
|
||||||
|
recapTotalRow: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: spacing.xs,
|
||||||
|
},
|
||||||
|
recapTotalLabel: {
|
||||||
|
color: colors.textSecondary,
|
||||||
|
fontSize: fontSize.md,
|
||||||
|
fontWeight: fontWeight.medium,
|
||||||
|
},
|
||||||
|
recapTotalValue: {
|
||||||
|
color: colors.success,
|
||||||
|
fontSize: fontSize.xxl,
|
||||||
|
fontWeight: fontWeight.bold,
|
||||||
|
},
|
||||||
|
recapArticlesCount: {
|
||||||
|
color: colors.textMuted,
|
||||||
|
fontSize: fontSize.xs,
|
||||||
|
},
|
||||||
|
recapNote: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: spacing.s,
|
||||||
|
backgroundColor: colors.accent + "18",
|
||||||
|
borderRadius: borderRadius.sm,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: colors.accent + "44",
|
||||||
|
padding: spacing.m,
|
||||||
|
marginBottom: spacing.l,
|
||||||
|
},
|
||||||
|
recapNoteText: {
|
||||||
|
color: colors.accentLight,
|
||||||
|
fontSize: fontSize.xs,
|
||||||
|
flex: 1,
|
||||||
|
lineHeight: 16,
|
||||||
|
},
|
||||||
|
recapActions: {
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: spacing.m,
|
||||||
|
},
|
||||||
totalRow: {
|
totalRow: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
@@ -375,8 +465,8 @@ export default function CartScreen() {
|
|||||||
</View>
|
</View>
|
||||||
<Button
|
<Button
|
||||||
title="Commander"
|
title="Commander"
|
||||||
onPress={() => navigation.navigate("Checkout")}
|
onPress={() => setShowRecapModal(true)}
|
||||||
variant="success"
|
variant="primary"
|
||||||
size="lg"
|
size="lg"
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
@@ -423,6 +513,77 @@ export default function CartScreen() {
|
|||||||
</View>
|
</View>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
visible={showRecapModal}
|
||||||
|
onClose={() => setShowRecapModal(false)}
|
||||||
|
title="Récapitulatif"
|
||||||
|
icon="receipt-outline"
|
||||||
|
iconColor={colors.accent}
|
||||||
|
>
|
||||||
|
<ScrollView style={styles.recapScroll} showsVerticalScrollIndicator={false}>
|
||||||
|
{(enrichedItems.length > 0 ? enrichedItems : cartItems).map((item, idx) => (
|
||||||
|
<View key={item.id} style={[styles.recapItem, idx === 0 && { borderTopWidth: 1, borderTopColor: colors.borderLight }]}>
|
||||||
|
{(item as EnrichedItem).imageUri ? (
|
||||||
|
<Image
|
||||||
|
source={{ uri: (item as EnrichedItem).imageUri }}
|
||||||
|
style={styles.recapItemImage}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<View style={styles.recapItemImagePlaceholder}>
|
||||||
|
<Ionicons name="leaf-outline" size={20} color={colors.textMuted} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<View style={styles.recapItemInfo}>
|
||||||
|
<Text style={styles.recapItemName} numberOfLines={1}>
|
||||||
|
{item.name_product}
|
||||||
|
</Text>
|
||||||
|
<Text style={styles.recapItemQty}>{item.quantity}g</Text>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.recapItemPrice}>{item.price.toFixed(2)} €</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<View style={styles.recapDivider} />
|
||||||
|
|
||||||
|
<View style={styles.recapTotalRow}>
|
||||||
|
<Text style={styles.recapTotalLabel}>Total</Text>
|
||||||
|
<Text style={styles.recapTotalValue}>{cartTotal.toFixed(2)} €</Text>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.recapArticlesCount}>
|
||||||
|
{cartCount} article{cartCount > 1 ? "s" : ""}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View style={[styles.recapDivider, { marginTop: spacing.l }]} />
|
||||||
|
|
||||||
|
<View style={styles.recapNote}>
|
||||||
|
<Ionicons name="information-circle-outline" size={16} color={colors.accentLight} />
|
||||||
|
<Text style={styles.recapNoteText}>
|
||||||
|
Vous renseignerez votre adresse et vos coordonnées à l'étape suivante.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.recapActions}>
|
||||||
|
<Button
|
||||||
|
title="Modifier"
|
||||||
|
onPress={() => setShowRecapModal(false)}
|
||||||
|
variant="outline"
|
||||||
|
size="md"
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="Confirmer"
|
||||||
|
onPress={() => {
|
||||||
|
setShowRecapModal(false);
|
||||||
|
navigation.navigate("Checkout");
|
||||||
|
}}
|
||||||
|
variant="primary"
|
||||||
|
size="md"
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
visible={showClearModal}
|
visible={showClearModal}
|
||||||
onClose={() => setShowClearModal(false)}
|
onClose={() => setShowClearModal(false)}
|
||||||
|
|||||||
@@ -209,6 +209,23 @@ export default function OrderHistoryScreen() {
|
|||||||
fontSize: fontSize.lg,
|
fontSize: fontSize.lg,
|
||||||
fontWeight: fontWeight.bold,
|
fontWeight: fontWeight.bold,
|
||||||
},
|
},
|
||||||
|
deliveredBadge: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
paddingHorizontal: spacing.m,
|
||||||
|
paddingVertical: spacing.xs,
|
||||||
|
borderRadius: borderRadius.xl,
|
||||||
|
borderWidth: 1.5,
|
||||||
|
borderColor: "#7c3aed",
|
||||||
|
},
|
||||||
|
deliveredBadgeText: {
|
||||||
|
color: "#a78bfa",
|
||||||
|
fontSize: fontSize.xs,
|
||||||
|
fontWeight: fontWeight.bold,
|
||||||
|
textTransform: "uppercase",
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
marginLeft: 4,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
[colors],
|
[colors],
|
||||||
);
|
);
|
||||||
@@ -372,7 +389,14 @@ export default function OrderHistoryScreen() {
|
|||||||
<Text style={styles.orderIdText}>
|
<Text style={styles.orderIdText}>
|
||||||
Commande #{order.client_order_number}
|
Commande #{order.client_order_number}
|
||||||
</Text>
|
</Text>
|
||||||
<StatusBadge status={order.status} />
|
{order.status === "cancelled" ? (
|
||||||
|
<StatusBadge status={order.status} />
|
||||||
|
) : (
|
||||||
|
<View style={styles.deliveredBadge}>
|
||||||
|
<Ionicons name="checkmark-circle" size={12} color="#a78bfa" />
|
||||||
|
<Text style={styles.deliveredBadgeText}>Livrée</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.orderRow}>
|
<View style={styles.orderRow}>
|
||||||
<Ionicons
|
<Ionicons
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export default function ProductsScreen() {
|
|||||||
const pulse = Animated.loop(
|
const pulse = Animated.loop(
|
||||||
Animated.sequence([
|
Animated.sequence([
|
||||||
Animated.timing(scaleAnim, {
|
Animated.timing(scaleAnim, {
|
||||||
toValue: 1.35,
|
toValue: 1.12,
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
useNativeDriver: true,
|
useNativeDriver: true,
|
||||||
}),
|
}),
|
||||||
@@ -178,14 +178,15 @@ export default function ProductsScreen() {
|
|||||||
gap: spacing.m,
|
gap: spacing.m,
|
||||||
},
|
},
|
||||||
comingSoonText: {
|
comingSoonText: {
|
||||||
fontSize: 36,
|
fontSize: Math.min(32, SCREEN_WIDTH * 0.07),
|
||||||
color: "#8E8FE8",
|
color: "#8E8FE8",
|
||||||
letterSpacing: 4,
|
letterSpacing: 2,
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
textTransform: "uppercase",
|
textTransform: "uppercase",
|
||||||
textShadowColor: "rgba(142, 143, 232, 0.85)",
|
textShadowColor: "rgba(142, 143, 232, 0.85)",
|
||||||
textShadowOffset: { width: 0, height: 0 },
|
textShadowOffset: { width: 0, height: 0 },
|
||||||
textShadowRadius: 18,
|
textShadowRadius: 18,
|
||||||
|
maxWidth: SCREEN_WIDTH * 0.82,
|
||||||
},
|
},
|
||||||
comingSoonDesc: {
|
comingSoonDesc: {
|
||||||
fontSize: fontSize.md,
|
fontSize: fontSize.md,
|
||||||
|
|||||||
@@ -2,19 +2,22 @@ import React, { useState, useCallback, useMemo } from "react";
|
|||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
|
TextInput as RNTextInput,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Alert,
|
Alert,
|
||||||
Modal,
|
Modal,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
|
ActivityIndicator,
|
||||||
Platform,
|
Platform,
|
||||||
Linking,
|
Linking,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
|
import { Feather } from "@expo/vector-icons";
|
||||||
import { useFocusEffect } from "@react-navigation/native";
|
import { useFocusEffect } from "@react-navigation/native";
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { getMyProfile, updateMyProfile, getTelegramStatus, generateTelegramLinkToken, unlinkTelegram } from "../../api/api";
|
import { getMyProfile, updateMyProfile, getTelegramStatus, generateTelegramLinkToken, unlinkTelegram, changePassword } from "../../api/api";
|
||||||
import TextInput from "../../components/ui/TextInput";
|
import TextInput from "../../components/ui/TextInput";
|
||||||
import Button from "../../components/ui/Button";
|
import Button from "../../components/ui/Button";
|
||||||
import { useTheme } from "../../context/ThemeContext";
|
import { useTheme } from "../../context/ThemeContext";
|
||||||
@@ -49,6 +52,16 @@ export default function ProfileScreen() {
|
|||||||
// Modal confirmation infos par défaut
|
// Modal confirmation infos par défaut
|
||||||
const [showSaveModal, setShowSaveModal] = useState(false);
|
const [showSaveModal, setShowSaveModal] = useState(false);
|
||||||
|
|
||||||
|
// Modal changement de mot de passe
|
||||||
|
const [showPasswordModal, setShowPasswordModal] = useState(false);
|
||||||
|
const [currentPwd, setCurrentPwd] = useState("");
|
||||||
|
const [newPwd, setNewPwd] = useState("");
|
||||||
|
const [confirmPwd, setConfirmPwd] = useState("");
|
||||||
|
const [showCurrentPwd, setShowCurrentPwd] = useState(false);
|
||||||
|
const [showNewPwd, setShowNewPwd] = useState(false);
|
||||||
|
const [showConfirmPwd, setShowConfirmPwd] = useState(false);
|
||||||
|
const [savingPassword, setSavingPassword] = useState(false);
|
||||||
|
|
||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
setLoadingProfile(true);
|
setLoadingProfile(true);
|
||||||
const [savedAddress, savedPhone, savedSignal, profileRes, tgStatus] = await Promise.all([
|
const [savedAddress, savedPhone, savedSignal, profileRes, tgStatus] = await Promise.all([
|
||||||
@@ -129,6 +142,34 @@ export default function ProfileScreen() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChangePassword = async () => {
|
||||||
|
if (!currentPwd || !newPwd || !confirmPwd) {
|
||||||
|
Alert.alert("Erreur", "Veuillez remplir tous les champs");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newPwd.length < 8) {
|
||||||
|
Alert.alert("Erreur", "Le nouveau mot de passe doit contenir au moins 8 caractères");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newPwd !== confirmPwd) {
|
||||||
|
Alert.alert("Erreur", "Les nouveaux mots de passe ne correspondent pas");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSavingPassword(true);
|
||||||
|
try {
|
||||||
|
const result = await changePassword(currentPwd, newPwd);
|
||||||
|
if (result.success) {
|
||||||
|
setShowPasswordModal(false);
|
||||||
|
setCurrentPwd(""); setNewPwd(""); setConfirmPwd("");
|
||||||
|
Alert.alert("Succès", "Mot de passe modifié avec succès");
|
||||||
|
} else {
|
||||||
|
Alert.alert("Erreur", result.message || "Erreur inattendue");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setSavingPassword(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const saveContact = async () => {
|
const saveContact = async () => {
|
||||||
setSavingContact(true);
|
setSavingContact(true);
|
||||||
const res = await updateMyProfile({ nom: nom.trim(), prenom: prenom.trim(), telephone: telephone.trim() });
|
const res = await updateMyProfile({ nom: nom.trim(), prenom: prenom.trim(), telephone: telephone.trim() });
|
||||||
@@ -274,6 +315,18 @@ export default function ProfileScreen() {
|
|||||||
},
|
},
|
||||||
modalBtnCancelText: { color: colors.textMuted, fontSize: fontSize.sm, fontWeight: fontWeight.semibold },
|
modalBtnCancelText: { color: colors.textMuted, fontSize: fontSize.sm, fontWeight: fontWeight.semibold },
|
||||||
modalBtnConfirmText: { color: colors.accent, fontSize: fontSize.sm, fontWeight: fontWeight.semibold },
|
modalBtnConfirmText: { color: colors.accent, fontSize: fontSize.sm, fontWeight: fontWeight.semibold },
|
||||||
|
pwdInputWrapper: {
|
||||||
|
flexDirection: "row" as const,
|
||||||
|
alignItems: "center" as const,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: borderRadius.sm,
|
||||||
|
paddingHorizontal: spacing.m,
|
||||||
|
height: 46,
|
||||||
|
marginBottom: spacing.m,
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
pwdInputIcon: { marginRight: spacing.s },
|
||||||
|
pwdInput: { flex: 1, fontSize: fontSize.sm },
|
||||||
}), [colors]);
|
}), [colors]);
|
||||||
|
|
||||||
if (loadingProfile) {
|
if (loadingProfile) {
|
||||||
@@ -398,6 +451,21 @@ export default function ProfileScreen() {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* Carte Changer le mot de passe */}
|
||||||
|
<View style={styles.card}>
|
||||||
|
<View style={styles.cardTitle}>
|
||||||
|
<Ionicons name="lock-closed-outline" size={18} color="#f59e0b" />
|
||||||
|
<Text style={styles.cardTitleText}>Sécurité</Text>
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.saveBtn, { backgroundColor: "#f59e0b22", borderWidth: 1, borderColor: "#f59e0b66" }]}
|
||||||
|
onPress={() => setShowPasswordModal(true)}
|
||||||
|
>
|
||||||
|
<Ionicons name="lock-closed-outline" size={16} color="#f59e0b" />
|
||||||
|
<Text style={[styles.saveBtnText, { color: "#f59e0b" }]}>Changer le mot de passe</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* Carte Telegram */}
|
{/* Carte Telegram */}
|
||||||
{telegramEnabled && (
|
{telegramEnabled && (
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
@@ -478,6 +546,103 @@ export default function ProfileScreen() {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
{/* Modal changement de mot de passe */}
|
||||||
|
<Modal
|
||||||
|
visible={showPasswordModal}
|
||||||
|
transparent
|
||||||
|
animationType="slide"
|
||||||
|
onRequestClose={() => setShowPasswordModal(false)}
|
||||||
|
>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.modalOverlay}
|
||||||
|
activeOpacity={1}
|
||||||
|
onPress={() => setShowPasswordModal(false)}
|
||||||
|
>
|
||||||
|
<TouchableOpacity activeOpacity={1} onPress={() => {}}>
|
||||||
|
<View style={[styles.modalBox, { paddingBottom: spacing.xl }]}>
|
||||||
|
<View style={styles.modalIconWrap}>
|
||||||
|
<Ionicons name="lock-closed-outline" size={24} color="#f59e0b" />
|
||||||
|
</View>
|
||||||
|
<Text style={styles.modalTitle}>Changer le mot de passe</Text>
|
||||||
|
|
||||||
|
{/* Champ mot de passe actuel */}
|
||||||
|
<View style={[styles.pwdInputWrapper, { backgroundColor: colors.bgSecondary, borderColor: colors.border }]}>
|
||||||
|
<Feather name="lock" size={16} color={colors.textMuted} style={styles.pwdInputIcon} />
|
||||||
|
<RNTextInput
|
||||||
|
style={[styles.pwdInput, { color: colors.textPrimary }]}
|
||||||
|
placeholder="Mot de passe actuel"
|
||||||
|
placeholderTextColor={colors.textMuted}
|
||||||
|
secureTextEntry={!showCurrentPwd}
|
||||||
|
value={currentPwd}
|
||||||
|
onChangeText={setCurrentPwd}
|
||||||
|
autoCapitalize="none"
|
||||||
|
autoCorrect={false}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity onPress={() => setShowCurrentPwd(!showCurrentPwd)}>
|
||||||
|
<Feather name={showCurrentPwd ? "eye-off" : "eye"} size={16} color={colors.textMuted} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Nouveau mot de passe */}
|
||||||
|
<View style={[styles.pwdInputWrapper, { backgroundColor: colors.bgSecondary, borderColor: colors.border }]}>
|
||||||
|
<Feather name="key" size={16} color={colors.textMuted} style={styles.pwdInputIcon} />
|
||||||
|
<RNTextInput
|
||||||
|
style={[styles.pwdInput, { color: colors.textPrimary }]}
|
||||||
|
placeholder="Nouveau mot de passe (min. 8 car.)"
|
||||||
|
placeholderTextColor={colors.textMuted}
|
||||||
|
secureTextEntry={!showNewPwd}
|
||||||
|
value={newPwd}
|
||||||
|
onChangeText={setNewPwd}
|
||||||
|
autoCapitalize="none"
|
||||||
|
autoCorrect={false}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity onPress={() => setShowNewPwd(!showNewPwd)}>
|
||||||
|
<Feather name={showNewPwd ? "eye-off" : "eye"} size={16} color={colors.textMuted} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Confirmer nouveau mot de passe */}
|
||||||
|
<View style={[styles.pwdInputWrapper, { backgroundColor: colors.bgSecondary, borderColor: colors.border }]}>
|
||||||
|
<Feather name="check-circle" size={16} color={colors.textMuted} style={styles.pwdInputIcon} />
|
||||||
|
<RNTextInput
|
||||||
|
style={[styles.pwdInput, { color: colors.textPrimary }]}
|
||||||
|
placeholder="Confirmer le nouveau mot de passe"
|
||||||
|
placeholderTextColor={colors.textMuted}
|
||||||
|
secureTextEntry={!showConfirmPwd}
|
||||||
|
value={confirmPwd}
|
||||||
|
onChangeText={setConfirmPwd}
|
||||||
|
autoCapitalize="none"
|
||||||
|
autoCorrect={false}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity onPress={() => setShowConfirmPwd(!showConfirmPwd)}>
|
||||||
|
<Feather name={showConfirmPwd ? "eye-off" : "eye"} size={16} color={colors.textMuted} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.modalActions}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.modalBtnCancel}
|
||||||
|
onPress={() => setShowPasswordModal(false)}
|
||||||
|
disabled={savingPassword}
|
||||||
|
>
|
||||||
|
<Text style={styles.modalBtnCancelText}>Annuler</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.modalBtnConfirm, { borderColor: "#f59e0b66" }]}
|
||||||
|
onPress={handleChangePassword}
|
||||||
|
disabled={savingPassword}
|
||||||
|
>
|
||||||
|
{savingPassword
|
||||||
|
? <ActivityIndicator size="small" color="#f59e0b" />
|
||||||
|
: <Text style={[styles.modalBtnConfirmText, { color: "#f59e0b" }]}>Confirmer</Text>
|
||||||
|
}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user