import React, { useCallback, useEffect, useMemo, useState } from "react"; import { ActivityIndicator, FlatList, StyleSheet, Text, View, } from "react-native"; import firebase from "../../config/firebase"; import { background } from "../../assets"; import Page from "../../layouts/Page"; import { useUser } from "../../providers/UserDataProvider"; import { gutters, Palette } from "../../styles"; import { FONT_FAMILY } from "../../styles/Fonts"; const ORDER_TYPE_LABELS = { GIFT: "Crédit offert", COINS: "Achat de coins", SONG: "Génération de musique", }; const STATUS_LABELS = { PENDING: "En cours", APPLIED: "Confirmée", REJECTED: "Refusée", }; const formatCoins = (amount) => { if (typeof amount !== "number" || !Number.isFinite(amount)) { return "0"; } try { return new Intl.NumberFormat("fr-FR", { maximumFractionDigits: 0, }).format(amount); } catch (_error) { return `${amount}`; } }; const formatDate = (date) => { if (!date) { return "En attente de confirmation"; } try { return date.toLocaleString("fr-FR", { day: "2-digit", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit", }); } catch (_error) { return date.toString(); } }; const mapOrderType = (type) => ORDER_TYPE_LABELS[type] || "Opération"; const mapStatus = (status) => STATUS_LABELS[status] || "Inconnue"; const OrderHistory = () => { const { currentUID } = useUser() || {}; const [orders, setOrders] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { if (!currentUID) { setOrders([]); setLoading(false); return; } setLoading(true); setError(null); const unsubscribe = firebase .firestore() .collection("orders") .where("userId", "==", currentUID) .orderBy("createdAt", "desc") .onSnapshot( (snapshot) => { const nextOrders = snapshot.docs.map((doc) => { const rawData = doc.data() || {}; const createdAt = typeof rawData.createdAt?.toDate === "function" ? rawData.createdAt.toDate() : null; return { id: doc.id, ...rawData, createdAt, }; }); setOrders(nextOrders); setLoading(false); }, (firestoreError) => { console.warn( "[OrderHistory] Unable to load orders", firestoreError?.message || firestoreError, ); setError( firestoreError?.message || "Impossible de récupérer l'historique des commandes.", ); setLoading(false); }, ); return () => { if (typeof unsubscribe === "function") { unsubscribe(); } }; }, [currentUID]); const renderOrder = useCallback(({ item }) => { const amountValue = typeof item.amount === "number" && Number.isFinite(item.amount) ? item.amount : 0; const isPositive = amountValue > 0; const amountLabel = `${isPositive ? "+" : ""}${formatCoins(amountValue)} ${ amountValue === 1 ? "coin" : "coins" }`; const status = item.status || "PENDING"; const details = (() => { if (item.type === "GIFT" && item.metadata?.reason) { switch (item.metadata.reason) { case "WELCOME_BONUS": return "Crédit de bienvenue"; default: return item.metadata.reason; } } if (item.type === "SONG" && item.songId) { return `Musique ${item.songId}`; } if (item.type === "COINS" && item.metadata?.paymentId) { return `Paiement ${item.metadata.paymentId}`; } return null; })(); return ( {mapOrderType(item.type)} {amountLabel} {formatDate(item.createdAt)} {mapStatus(status)} {details ? {details} : null} {typeof item.balanceAfter === "number" && Number.isFinite(item.balanceAfter) ? ( Solde après opération: {formatCoins(item.balanceAfter)} coins ) : null} ); }, []); const keyExtractor = useCallback((item) => item.id, []); const listEmptyComponent = useMemo(() => { if (loading) { return null; } if (!currentUID) { return ( Connecte-toi pour consulter l'historique de tes commandes. ); } if (error) { return {error}; } return ( Aucune opération de coins pour le moment. ); }, [loading, currentUID, error]); return ( {loading ? ( ) : ( } ListEmptyComponent={listEmptyComponent} /> )} ); }; const styles = StyleSheet.create({ container: { flex: 1, paddingHorizontal: gutters, paddingTop: gutters, }, loaderContainer: { flex: 1, alignItems: "center", justifyContent: "center", }, listContent: { paddingBottom: gutters * 2, }, emptyListContent: { flexGrow: 1, justifyContent: "center", }, orderCard: { padding: 16, borderRadius: 16, backgroundColor: Palette.ultraLightWhite, borderWidth: 1, borderColor: "rgba(255, 255, 255, 0.08)", }, orderHeader: { flexDirection: "row", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 8, gap: 12, }, orderTitle: { flex: 1, fontSize: 16, fontFamily: FONT_FAMILY.InterSemiBold, color: Palette.white, }, orderAmount: { fontSize: 16, fontFamily: FONT_FAMILY.InterSemiBold, }, amountPositive: { color: Palette.green, }, amountNegative: { color: Palette.red, }, orderMetaRow: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 6, gap: 12, }, orderDate: { fontSize: 13, color: Palette.grayMid, fontFamily: FONT_FAMILY.InterRegular, }, statusBadge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 999, }, statusApplied: { backgroundColor: Palette.transparentGreen, }, statusPending: { backgroundColor: Palette.transparentOrange, }, statusRejected: { backgroundColor: Palette.transparentRed, }, statusText: { fontSize: 12, fontFamily: FONT_FAMILY.InterMedium, color: Palette.white, }, orderDetails: { marginBottom: 6, fontSize: 14, fontFamily: FONT_FAMILY.InterRegular, color: Palette.white, }, orderBalance: { fontSize: 13, fontFamily: FONT_FAMILY.InterRegular, color: Palette.grayMid, }, emptyText: { textAlign: "center", color: Palette.grayMid, fontSize: 15, fontFamily: FONT_FAMILY.InterRegular, }, errorText: { textAlign: "center", color: Palette.red, fontSize: 15, fontFamily: FONT_FAMILY.InterRegular, }, }); export default OrderHistory;