Files
projet_gestion_commande/mobile/src/screens/client/CheckoutScreen.tsx
T
2026-03-28 17:39:45 +01:00

743 lines
36 KiB
TypeScript

import React, { useState, useEffect, useRef, useMemo } from "react";
import {
View,
Text,
ScrollView,
StyleSheet,
KeyboardAvoidingView,
Platform,
Switch,
TouchableOpacity,
Clipboard,
Alert,
} from "react-native";
import { useNavigation } from "@react-navigation/native";
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { Ionicons } from "@expo/vector-icons";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { useCart } from "../../context/CartContext";
import {
checkoutCart,
getReferralBalance,
getPublicSettings,
getCryptoPaymentStatus,
} from "../../api/api";
import type { CryptoPaymentStatus } from "../../api/api";
import type { ClientStackParamList } from "../../navigation/types";
import TextInput from "../../components/ui/TextInput";
import Button from "../../components/ui/Button";
import Modal from "../../components/ui/Modal";
import { useTheme } from "../../context/ThemeContext";
import { spacing, borderRadius, fontSize, fontWeight } from "../../theme";
type Nav = NativeStackNavigationProp<ClientStackParamList>;
const CRYPTO_COLOR = "#f7931a";
export default function CheckoutScreen() {
const navigation = useNavigation<Nav>();
const { colors } = useTheme();
const { cartItems, cartTotal, clearCart, refreshCart } = useCart();
const [nom, setNom] = useState("");
const [prenom, setPrenom] = useState("");
const [telephone, setTelephone] = useState("");
const [address, setAddress] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [showConfirmation, setShowConfirmation] = useState(false);
const [confirmationData, setConfirmationData] = useState<any>(null);
const [invalidAddressModal, setInvalidAddressModal] = useState(false);
const [suggestedAddress, setSuggestedAddress] = useState("");
// Parrainage
const [referralBalance, setReferralBalance] = useState(0);
const [useReferral, setUseReferral] = useState(false);
// Crypto
const [cryptoEnabled, setCryptoEnabled] = useState(false);
const [cryptoOnly, setCryptoOnly] = useState(false);
const [cryptoCurrencies, setCryptoCurrencies] = useState<string[]>([]);
const [paymentMethod, setPaymentMethod] = useState<"especes" | "crypto">("especes");
const [payCurrency, setPayCurrency] = useState("");
const [cryptoData, setCryptoData] = useState<CryptoPaymentStatus | null>(null);
const [showCryptoModal, setShowCryptoModal] = useState(false);
const [cryptoPolling, setCryptoPolling] = useState(false);
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
useEffect(() => {
getReferralBalance().then((res) => {
if (res.success && res.balance > 0) setReferralBalance(res.balance);
});
getPublicSettings().then((settings) => {
if (settings.crypto_payment_enabled && settings.nowpayments_currencies.length > 0) {
setCryptoEnabled(true);
setCryptoCurrencies(settings.nowpayments_currencies);
setPayCurrency(settings.nowpayments_currencies[0]);
if (settings.crypto_only) {
setCryptoOnly(true);
setPaymentMethod("crypto");
}
}
});
AsyncStorage.multiGet([
"profile_default_address",
"profile_default_phone",
]).then((pairs) => {
const savedAddress = pairs[0][1];
const savedPhone = pairs[1][1];
if (savedAddress) setAddress(savedAddress);
if (savedPhone) setTelephone(savedPhone);
});
return () => {
if (pollRef.current) clearInterval(pollRef.current);
};
}, []);
const startPolling = (commandId: number) => {
setCryptoPolling(true);
pollRef.current = setInterval(async () => {
const status = await getCryptoPaymentStatus(commandId);
if (!status) return;
setCryptoData(status);
if (
status.payment_status === "finished" ||
status.payment_status === "confirmed"
) {
stopPolling();
await refreshCart();
} else if (
status.payment_status === "failed" ||
status.payment_status === "expired"
) {
stopPolling();
}
}, 10000);
};
const stopPolling = () => {
setCryptoPolling(false);
if (pollRef.current) {
clearInterval(pollRef.current);
pollRef.current = null;
}
};
const handleCheckout = async () => {
if (!nom.trim()) { setError("Veuillez saisir votre nom"); return; }
if (!prenom.trim()) { setError("Veuillez saisir votre prenom"); return; }
if (!telephone.trim()) { setError("Veuillez saisir votre numero de telephone"); return; }
if (!address.trim()) { setError("Veuillez saisir une adresse de livraison"); return; }
if (cartItems.length === 0) { setError("Votre panier est vide"); return; }
if (paymentMethod === "crypto" && !payCurrency) {
setError("Veuillez sélectionner une cryptomonnaie");
return;
}
setError(null);
setLoading(true);
try {
const res = await checkoutCart(
address.trim(),
nom.trim(),
prenom.trim(),
telephone.trim(),
useReferral && referralBalance > 0,
paymentMethod === "crypto" ? "crypto" : undefined,
paymentMethod === "crypto" ? payCurrency : undefined,
);
if (res.success && res.payment_method === "crypto") {
setCryptoData({
command_id: res.command_id!,
client_order_number: (res as any).client_order_number,
payment_status: res.payment_status!,
pay_address: res.pay_address!,
pay_amount: res.pay_amount!,
pay_currency: res.pay_currency!,
price_amount: res.price_amount!,
price_currency: res.price_currency!,
});
setShowCryptoModal(true);
startPolling(res.command_id!);
} else if (res.success) {
setConfirmationData(res);
setShowConfirmation(true);
await refreshCart();
} else if (res.invalid_address && res.suggested_address) {
setSuggestedAddress(res.suggested_address);
setInvalidAddressModal(true);
} else {
setError(res.message || "Erreur lors de la commande");
}
} catch {
setError("Erreur de connexion au serveur");
} finally {
setLoading(false);
}
};
const handleConfirmClose = () => {
setShowConfirmation(false);
navigation.navigate("ClientTabs");
};
const handleCryptoClose = () => {
stopPolling();
setShowCryptoModal(false);
navigation.navigate("ClientTabs");
};
const copyAddress = () => {
if (!cryptoData) return;
Clipboard.setString(cryptoData.pay_address);
Alert.alert("Copié", "Adresse copiée dans le presse-papier");
};
const cryptoStatusLabel = (status: string) => {
switch (status) {
case "waiting": return "⏳ En attente de paiement";
case "confirming": return "🔄 Confirmation en cours...";
case "confirmed": return "✅ Confirmé";
case "finished": return "✅ Paiement reçu !";
case "failed": return "❌ Paiement échoué";
case "expired": return "⌛ Expiré";
default: return status;
}
};
const cryptoStatusColor = (status: string) => {
if (status === "finished" || status === "confirmed") return colors.success;
if (status === "failed" || status === "expired") return colors.danger;
if (status === "confirming") return colors.info;
return colors.warning;
};
const styles = useMemo(
() =>
StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bgPrimary },
content: { padding: spacing.xl, paddingBottom: spacing.xxxl },
section: { marginBottom: spacing.xl },
sectionTitle: {
color: colors.textSecondary,
fontSize: fontSize.sm,
fontWeight: fontWeight.medium,
textTransform: "uppercase",
letterSpacing: 1,
marginBottom: spacing.m,
},
fieldGroup: { gap: spacing.m },
row: { flexDirection: "row", gap: spacing.m },
halfField: { flex: 1 },
summaryCard: {
backgroundColor: colors.bgCard,
borderRadius: borderRadius.md,
padding: spacing.l,
borderWidth: 1,
borderColor: colors.borderLight,
},
summaryItem: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: spacing.s,
borderBottomWidth: 1,
borderBottomColor: colors.borderLight,
},
summaryItemLeft: { flex: 1, marginRight: spacing.m },
summaryItemName: { color: colors.textPrimary, fontSize: fontSize.md },
summaryItemQty: { color: colors.textMuted, fontSize: fontSize.sm },
summaryItemPrice: {
color: colors.textPrimary,
fontSize: fontSize.md,
fontWeight: fontWeight.semibold,
},
summaryTotal: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingTop: spacing.m,
marginTop: spacing.s,
},
summaryTotalLabel: {
color: colors.textSecondary,
fontSize: fontSize.lg,
fontWeight: fontWeight.semibold,
},
summaryTotalValue: {
color: colors.success,
fontSize: fontSize.xl,
fontWeight: fontWeight.bold,
},
errorText: {
color: colors.danger,
fontSize: fontSize.sm,
textAlign: "center",
},
referralCard: {
backgroundColor: colors.bgCard,
borderRadius: borderRadius.md,
borderWidth: 1,
borderColor: colors.accent + "44",
padding: spacing.l,
marginBottom: spacing.xl,
},
referralRow: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
referralLeft: { flexDirection: "row", alignItems: "center", gap: spacing.s, flex: 1 },
referralTitle: {
color: colors.textPrimary,
fontSize: fontSize.md,
fontWeight: fontWeight.semibold,
},
referralAmount: { color: colors.accent, fontSize: fontSize.sm, marginTop: 2 },
referralHint: {
color: colors.textMuted,
fontSize: fontSize.xs,
marginTop: spacing.s,
lineHeight: 16,
},
prefillHint: { color: "#6ee7b7", fontSize: fontSize.xs, marginTop: 4, opacity: 0.85 },
invalidAddrContent: { gap: spacing.m },
invalidAddrIconContainer: { alignItems: "center" },
invalidAddrIconCircle: {
width: 64, height: 64, borderRadius: 32,
backgroundColor: "rgba(251,191,36,0.12)",
justifyContent: "center", alignItems: "center",
},
invalidAddrLabel: {
color: colors.textSecondary, fontSize: fontSize.sm, textAlign: "center",
},
invalidAddrSuggestion: {
backgroundColor: colors.bgInput, borderRadius: 10,
padding: spacing.m, borderWidth: 1, borderColor: colors.borderLight,
},
invalidAddrSuggestionText: {
color: colors.textPrimary, fontSize: fontSize.md,
fontWeight: fontWeight.semibold, textAlign: "center",
},
invalidAddrActions: { flexDirection: "row", gap: spacing.m, marginTop: spacing.s },
confirmContent: { gap: spacing.m },
confirmIconContainer: { alignItems: "center" },
confirmIconCircle: {
width: 72, height: 72, borderRadius: 36,
backgroundColor: "rgba(74,222,128,0.1)",
justifyContent: "center", alignItems: "center",
},
confirmText: {
color: colors.textPrimary, fontSize: fontSize.md,
textAlign: "center", lineHeight: 22,
},
confirmInfo: {
flexDirection: "row", alignItems: "center", gap: spacing.s,
backgroundColor: colors.bgInput, padding: spacing.m,
borderRadius: 12, borderWidth: 1, borderColor: colors.borderLight,
},
confirmInfoText: { color: colors.textSecondary, fontSize: fontSize.sm, flex: 1 },
// Méthode de paiement
paymentMethodRow: { flexDirection: "row", gap: spacing.m, marginBottom: spacing.m },
paymentMethodBtn: {
flex: 1, padding: spacing.m, borderRadius: borderRadius.md,
borderWidth: 2, borderColor: colors.borderLight,
backgroundColor: colors.bgCard,
alignItems: "center", justifyContent: "center", gap: spacing.xs,
},
paymentMethodBtnActive: { borderColor: CRYPTO_COLOR },
paymentMethodBtnText: { color: colors.textMuted, fontSize: fontSize.sm, fontWeight: fontWeight.medium },
paymentMethodBtnTextActive: { color: CRYPTO_COLOR },
// Sélection devise crypto
currencyGrid: { flexDirection: "row", flexWrap: "wrap", gap: spacing.s, marginTop: spacing.s },
currencyBtn: {
paddingHorizontal: spacing.m, paddingVertical: spacing.s,
borderRadius: borderRadius.sm, borderWidth: 2,
borderColor: colors.borderLight, backgroundColor: colors.bgCard,
},
currencyBtnActive: { borderColor: CRYPTO_COLOR, backgroundColor: "rgba(247,147,26,0.08)" },
currencyBtnText: { color: colors.textMuted, fontSize: fontSize.sm, fontWeight: fontWeight.semibold },
currencyBtnTextActive: { color: CRYPTO_COLOR },
cryptoHint: { color: colors.textMuted, fontSize: fontSize.xs, marginTop: spacing.s },
// Modal crypto
cryptoContent: { gap: spacing.m },
cryptoRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "center" },
cryptoLabel: { color: colors.textSecondary, fontSize: fontSize.sm },
cryptoValue: { color: colors.textPrimary, fontSize: fontSize.md, fontWeight: fontWeight.semibold },
cryptoAddressBox: {
backgroundColor: colors.bgInput, borderRadius: borderRadius.md,
padding: spacing.m, borderWidth: 1, borderColor: colors.borderLight,
gap: spacing.s,
},
cryptoAddressText: {
color: colors.textPrimary, fontSize: fontSize.xs,
fontFamily: Platform.OS === "ios" ? "Courier" : "monospace",
},
cryptoCopyBtn: {
flexDirection: "row", alignItems: "center", justifyContent: "center",
gap: spacing.xs, padding: spacing.s, borderRadius: borderRadius.sm,
backgroundColor: colors.bgCard, borderWidth: 1, borderColor: colors.borderLight,
},
cryptoCopyText: { color: colors.textSecondary, fontSize: fontSize.sm },
cryptoPollingText: {
color: colors.textMuted, fontSize: fontSize.xs, textAlign: "center",
},
cryptoSuccessText: { color: colors.success, fontSize: fontSize.sm, textAlign: "center" },
// Crypto only badge
cryptoOnlyBadge: {
flexDirection: "row", alignItems: "center", gap: spacing.s,
backgroundColor: "rgba(247,147,26,0.10)", borderRadius: borderRadius.md,
borderWidth: 1, borderColor: CRYPTO_COLOR + "55",
padding: spacing.m, marginBottom: spacing.s,
},
cryptoOnlyText: { color: CRYPTO_COLOR, fontSize: fontSize.sm, fontWeight: fontWeight.medium, flex: 1 },
}),
[colors],
);
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === "ios" ? "padding" : undefined}
>
<ScrollView contentContainerStyle={styles.content}>
{/* Résumé commande */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Resume de la commande</Text>
<View style={styles.summaryCard}>
{cartItems.map((item) => (
<View key={item.id} style={styles.summaryItem}>
<View style={styles.summaryItemLeft}>
<Text style={styles.summaryItemName} numberOfLines={1}>
{item.name_product}
</Text>
<Text style={styles.summaryItemQty}>{item.quantity}g</Text>
</View>
<Text style={styles.summaryItemPrice}>{item.price.toFixed(2)} </Text>
</View>
))}
<View style={styles.summaryTotal}>
<Text style={styles.summaryTotalLabel}>Total</Text>
<Text style={styles.summaryTotalValue}>{cartTotal.toFixed(2)} </Text>
</View>
</View>
</View>
{/* Infos personnelles */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Informations personnelles</Text>
<View style={styles.fieldGroup}>
<View style={styles.row}>
<View style={styles.halfField}>
<TextInput
placeholder="Nom"
value={nom}
onChangeText={(t) => { setNom(t); setError(null); }}
icon={<Ionicons name="person-outline" size={20} color={colors.textMuted} />}
error={error && !nom.trim() ? error : undefined}
/>
</View>
<View style={styles.halfField}>
<TextInput
placeholder="Prenom"
value={prenom}
onChangeText={(t) => { setPrenom(t); setError(null); }}
icon={<Ionicons name="person-outline" size={20} color={colors.textMuted} />}
error={error && nom.trim() && !prenom.trim() ? error : undefined}
/>
</View>
</View>
<TextInput
placeholder="Numero de telephone"
value={telephone}
onChangeText={(t) => { setTelephone(t); setError(null); }}
icon={<Ionicons name="call-outline" size={20} color={colors.textMuted} />}
keyboardType="phone-pad"
error={error && nom.trim() && prenom.trim() && !telephone.trim() ? error : undefined}
/>
{!!telephone && <Text style={styles.prefillHint}>Pre-rempli depuis votre profil</Text>}
</View>
</View>
{/* Adresse */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Adresse de livraison</Text>
<TextInput
placeholder="Entrez votre adresse complete"
value={address}
onChangeText={(t) => { setAddress(t); setError(null); }}
icon={<Ionicons name="location-outline" size={20} color={colors.textMuted} />}
error={error && nom.trim() && prenom.trim() && telephone.trim() && !address.trim() ? error : undefined}
multiline
numberOfLines={3}
/>
{!!address && (
<Text style={styles.prefillHint}>
Pre-rempli depuis votre profil modifiez si vous etes ailleurs
</Text>
)}
</View>
{/* Méthode de paiement */}
{cryptoEnabled && (
<View style={styles.section}>
{cryptoOnly ? (
<>
<Text style={styles.sectionTitle}>Cryptomonnaie</Text>
<View style={styles.cryptoOnlyBadge}>
<Ionicons name="logo-bitcoin" size={18} color={CRYPTO_COLOR} />
<Text style={styles.cryptoOnlyText}>
Paiement uniquement en cryptomonnaie
</Text>
</View>
<View style={styles.currencyGrid}>
{cryptoCurrencies.map((c) => (
<TouchableOpacity
key={c}
style={[styles.currencyBtn, payCurrency === c && styles.currencyBtnActive]}
onPress={() => setPayCurrency(c)}
>
<Text style={[styles.currencyBtnText, payCurrency === c && styles.currencyBtnTextActive]}>
{c.toUpperCase()}
</Text>
</TouchableOpacity>
))}
</View>
<Text style={styles.cryptoHint}>
Choisissez la devise avec laquelle vous souhaitez payer
</Text>
</>
) : (
<>
<Text style={styles.sectionTitle}>Méthode de paiement</Text>
<View style={styles.paymentMethodRow}>
<TouchableOpacity
style={[styles.paymentMethodBtn, paymentMethod === "especes" && styles.paymentMethodBtnActive]}
onPress={() => setPaymentMethod("especes")}
>
<Ionicons name="cash-outline" size={22} color={paymentMethod === "especes" ? CRYPTO_COLOR : colors.textMuted} />
<Text style={[styles.paymentMethodBtnText, paymentMethod === "especes" && styles.paymentMethodBtnTextActive]}>
Espèces
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.paymentMethodBtn, paymentMethod === "crypto" && styles.paymentMethodBtnActive]}
onPress={() => setPaymentMethod("crypto")}
>
<Ionicons name="logo-bitcoin" size={22} color={paymentMethod === "crypto" ? CRYPTO_COLOR : colors.textMuted} />
<Text style={[styles.paymentMethodBtnText, paymentMethod === "crypto" && styles.paymentMethodBtnTextActive]}>
Crypto
</Text>
</TouchableOpacity>
</View>
{paymentMethod === "crypto" && (
<>
<Text style={styles.sectionTitle}>Cryptomonnaie</Text>
<View style={styles.currencyGrid}>
{cryptoCurrencies.map((c) => (
<TouchableOpacity
key={c}
style={[styles.currencyBtn, payCurrency === c && styles.currencyBtnActive]}
onPress={() => setPayCurrency(c)}
>
<Text style={[styles.currencyBtnText, payCurrency === c && styles.currencyBtnTextActive]}>
{c.toUpperCase()}
</Text>
</TouchableOpacity>
))}
</View>
<Text style={styles.cryptoHint}>
Vous recevrez l'adresse wallet après validation
</Text>
</>
)}
</>
)}
</View>
)}
{/* Parrainage */}
{referralBalance > 0 && (
<View style={styles.referralCard}>
<View style={styles.referralRow}>
<View style={styles.referralLeft}>
<Ionicons name="gift-outline" size={22} color={colors.accent} />
<View>
<Text style={styles.referralTitle}>Credit parrainage</Text>
<Text style={styles.referralAmount}>{referralBalance.toFixed(2)} € disponibles</Text>
</View>
</View>
<Switch
value={useReferral}
onValueChange={setUseReferral}
trackColor={{ false: colors.border, true: colors.accent + "66" }}
thumbColor={useReferral ? colors.accent : colors.textMuted}
/>
</View>
{useReferral && (
<Text style={styles.referralHint}>
Le credit sera deduit de ta commande. Tu dois quand meme atteindre le minimum de ta zone + le credit utilise.
</Text>
)}
</View>
)}
{error && nom.trim() && prenom.trim() && telephone.trim() && address.trim() && (
<Text style={styles.errorText}>{error}</Text>
)}
<Button
title="Valider la commande"
onPress={handleCheckout}
loading={loading}
disabled={loading || cartItems.length === 0}
variant="success"
size="lg"
fullWidth
style={{ marginTop: spacing.l }}
/>
</ScrollView>
{/* Modal adresse invalide */}
<Modal
visible={invalidAddressModal}
onClose={() => setInvalidAddressModal(false)}
title="Adresse invalide"
icon="warning-outline"
iconColor={colors.warning}
>
<View style={styles.invalidAddrContent}>
<View style={styles.invalidAddrIconContainer}>
<View style={styles.invalidAddrIconCircle}>
<Ionicons name="location-outline" size={32} color={colors.warning} />
</View>
</View>
<Text style={styles.invalidAddrLabel}>
L'adresse saisie n'est pas reconnue. Voulez-vous utiliser l'adresse correcte suggérée ?
</Text>
<View style={styles.invalidAddrSuggestion}>
<Text style={styles.invalidAddrSuggestionText}>{suggestedAddress}</Text>
</View>
<View style={styles.invalidAddrActions}>
<Button title="Modifier" variant="outline" size="md" onPress={() => setInvalidAddressModal(false)} style={{ flex: 1 }} />
<Button title="Utiliser" variant="success" size="md" onPress={() => { setAddress(suggestedAddress); setInvalidAddressModal(false); }} style={{ flex: 1 }} />
</View>
</View>
</Modal>
{/* Modal confirmation (paiement espèces) */}
<Modal
visible={showConfirmation}
onClose={handleConfirmClose}
title="Commande confirmee !"
icon="checkmark-circle"
iconColor={colors.success}
>
<View style={styles.confirmContent}>
<View style={styles.confirmIconContainer}>
<View style={styles.confirmIconCircle}>
<Ionicons name="checkmark-circle" size={48} color={colors.success} />
</View>
</View>
<Text style={styles.confirmText}>
Votre commande #{confirmationData?.client_order_number ?? confirmationData?.command_id} a ete creee avec succes.
</Text>
{confirmationData?.referral_used > 0 && (
<View style={styles.confirmInfo}>
<Ionicons name="gift-outline" size={16} color={colors.accent} />
<Text style={styles.confirmInfoText}>
{confirmationData.referral_used.toFixed(2)} de credit parrainage utilises
{confirmationData.referral_balance !== undefined
? ` — Solde restant : ${confirmationData.referral_balance.toFixed(2)} €`
: ""}
</Text>
</View>
)}
<View style={styles.confirmInfo}>
<Ionicons name="location-outline" size={16} color={colors.info} />
<Text style={styles.confirmInfoText}>
Suivez votre livraison en temps réel depuis la page Suivi
</Text>
</View>
<Button title="Voir mes commandes" onPress={handleConfirmClose} variant="primary" size="md" fullWidth style={{ marginTop: spacing.l }} />
</View>
</Modal>
{/* Modal paiement crypto */}
<Modal
visible={showCryptoModal}
onClose={handleCryptoClose}
title="Paiement Crypto"
icon="logo-bitcoin"
iconColor={CRYPTO_COLOR}
>
{cryptoData && (
<View style={styles.cryptoContent}>
{/* Commande */}
<View style={styles.cryptoRow}>
<Text style={styles.cryptoLabel}>Commande</Text>
<Text style={styles.cryptoValue}>#{cryptoData.client_order_number ?? cryptoData.command_id}</Text>
</View>
{/* Statut */}
<View style={styles.cryptoRow}>
<Text style={styles.cryptoLabel}>Statut</Text>
<Text style={[styles.cryptoValue, { color: cryptoStatusColor(cryptoData.payment_status) }]}>
{cryptoStatusLabel(cryptoData.payment_status)}
</Text>
</View>
{/* Montant */}
<View style={styles.cryptoRow}>
<Text style={styles.cryptoLabel}>Montant</Text>
<Text style={styles.cryptoValue}>
{cryptoData.pay_amount} {cryptoData.pay_currency.toUpperCase()}
{"\n"}
<Text style={{ color: colors.textMuted, fontSize: fontSize.xs }}>
{cryptoData.price_amount.toFixed(2)} {cryptoData.price_currency.toUpperCase()}
</Text>
</Text>
</View>
{/* Adresse wallet */}
<Text style={styles.cryptoLabel}>Adresse wallet</Text>
<View style={styles.cryptoAddressBox}>
<Text style={styles.cryptoAddressText} selectable>
{cryptoData.pay_address}
</Text>
<TouchableOpacity style={styles.cryptoCopyBtn} onPress={copyAddress}>
<Ionicons name="copy-outline" size={16} color={colors.textSecondary} />
<Text style={styles.cryptoCopyText}>Copier l'adresse</Text>
</TouchableOpacity>
</View>
{/* Polling */}
{cryptoPolling && (
<Text style={styles.cryptoPollingText}>
Vérification automatique toutes les 10 secondes...
</Text>
)}
{(cryptoData.payment_status === "finished" || cryptoData.payment_status === "confirmed") && (
<Text style={styles.cryptoSuccessText}>
Paiement confirmé ! Votre commande est en cours de traitement.
</Text>
)}
<Button
title="Suivre ma commande"
onPress={handleCryptoClose}
variant="primary"
size="md"
fullWidth
style={{ marginTop: spacing.s }}
/>
</View>
)}
</Modal>
</KeyboardAvoidingView>
);
}