274 lines
7.4 KiB
JavaScript
274 lines
7.4 KiB
JavaScript
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'
|
|
import CreditAmount from '../../components/CreditAmount'
|
|
|
|
const ORDER_TYPE_LABELS = {
|
|
GIFT: 'Crédit offert',
|
|
COINS: 'Achat de coins',
|
|
SONG: 'Génération de musique',
|
|
SUBSCRIPTION: 'Abonnement',
|
|
}
|
|
|
|
const formatDateParts = (date) => {
|
|
if (!date) {
|
|
return {
|
|
dateLabel: 'En attente de confirmation',
|
|
timeLabel: '',
|
|
}
|
|
}
|
|
try {
|
|
const dateLabel = date.toLocaleDateString('fr-FR', {
|
|
day: '2-digit',
|
|
month: 'short',
|
|
year: 'numeric',
|
|
})
|
|
const timeLabel = date.toLocaleTimeString('fr-FR', {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
})
|
|
return { dateLabel, timeLabel }
|
|
} catch (_error) {
|
|
return {
|
|
dateLabel: date.toString(),
|
|
timeLabel: '',
|
|
}
|
|
}
|
|
}
|
|
|
|
const mapOrderType = (type) => ORDER_TYPE_LABELS[type] || 'Opération'
|
|
|
|
const shortenIdentifier = (value, visible = 6) => {
|
|
if (typeof value !== 'string') return null
|
|
if (value.length <= visible + 2) return value
|
|
return `${value.slice(0, visible)}…`
|
|
}
|
|
|
|
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 amountTextStyle = [
|
|
styles.orderAmount,
|
|
isPositive ? styles.amountPositive : styles.amountNegative,
|
|
]
|
|
|
|
const { dateLabel } = formatDateParts(item.createdAt)
|
|
|
|
let reasonLabel = mapOrderType(item.type)
|
|
|
|
if (item.type === 'GIFT') {
|
|
const reason = item.metadata?.reason
|
|
if (reason === 'WELCOME_BONUS') {
|
|
reasonLabel = 'Crédit de bienvenue'
|
|
} else if (typeof reason === 'string' && reason.trim()) {
|
|
reasonLabel = reason.trim()
|
|
}
|
|
} else if (item.type === 'SONG') {
|
|
reasonLabel = 'Génération de musique'
|
|
} else if (item.type === 'COINS') {
|
|
if (item.metadata?.coinPackKey) {
|
|
const shortenedPack = shortenIdentifier(item.metadata.coinPackKey, 10)
|
|
reasonLabel = `Pack ${shortenedPack || item.metadata.coinPackKey}`
|
|
} else if (typeof item.metadata?.source === 'string' && item.metadata.source.trim()) {
|
|
const source = item.metadata.source.trim()
|
|
reasonLabel = source === 'STRIPE_CHECKOUT' ? 'Recharge Stripe' : source
|
|
} else {
|
|
reasonLabel = 'Rechargement de coins'
|
|
}
|
|
} else if (item.type === 'SUBSCRIPTION') {
|
|
const rawPeriod =
|
|
typeof item.metadata?.billingPeriod === 'string'
|
|
? item.metadata.billingPeriod.toLowerCase()
|
|
: null
|
|
reasonLabel =
|
|
rawPeriod === 'annual'
|
|
? 'Abonnement annuel'
|
|
: rawPeriod === 'monthly'
|
|
? 'Abonnement mensuel'
|
|
: 'Abonnement'
|
|
}
|
|
|
|
return (
|
|
<View style={styles.orderCard}>
|
|
<CreditAmount value={amountValue} showPlus textStyle={amountTextStyle} iconSize={18} />
|
|
<Text style={styles.orderReason}>{reasonLabel}</Text>
|
|
<Text style={styles.orderTimestamp}>{dateLabel}</Text>
|
|
</View>
|
|
)
|
|
}, [])
|
|
|
|
const keyExtractor = useCallback((item) => item.id, [])
|
|
|
|
const listEmptyComponent = useMemo(() => {
|
|
if (loading) {
|
|
return null
|
|
}
|
|
if (!currentUID) {
|
|
return (
|
|
<Text style={styles.emptyText}>
|
|
Connecte-toi pour consulter l'historique de tes commandes.
|
|
</Text>
|
|
)
|
|
}
|
|
if (error) {
|
|
return <Text style={styles.errorText}>{error}</Text>
|
|
}
|
|
return <Text style={styles.emptyText}>Aucune opération de coins pour le moment.</Text>
|
|
}, [loading, currentUID, error])
|
|
|
|
return (
|
|
<Page
|
|
headerType="NAVIGATION"
|
|
title="Historique de commandes"
|
|
backgroundImg={background.profileBG}
|
|
contentContainerStyle={{ paddingBottom: gutters * 2 }}
|
|
containerStyle={{
|
|
backgroundColor: 'rgba(0,0,0,0.4)',
|
|
}}
|
|
>
|
|
<View style={styles.container}>
|
|
{loading ? (
|
|
<View style={styles.loaderContainer}>
|
|
<ActivityIndicator color={Palette.white} size="large" />
|
|
</View>
|
|
) : (
|
|
<FlatList
|
|
data={orders}
|
|
keyExtractor={keyExtractor}
|
|
renderItem={renderOrder}
|
|
contentContainerStyle={[
|
|
styles.listContent,
|
|
orders.length === 0 && styles.emptyListContent,
|
|
]}
|
|
ItemSeparatorComponent={() => <View style={{ height: 12 }} />}
|
|
ListEmptyComponent={listEmptyComponent}
|
|
/>
|
|
)}
|
|
</View>
|
|
</Page>
|
|
)
|
|
}
|
|
|
|
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)',
|
|
gap: 6,
|
|
},
|
|
orderAmount: {
|
|
fontSize: 16,
|
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
|
},
|
|
amountPositive: {
|
|
color: Palette.green,
|
|
},
|
|
amountNegative: {
|
|
color: Palette.red,
|
|
},
|
|
orderReason: {
|
|
fontSize: 14,
|
|
fontFamily: FONT_FAMILY.InterRegular,
|
|
color: Palette.white,
|
|
},
|
|
orderTimestamp: {
|
|
fontSize: 12,
|
|
color: Palette.grayMid,
|
|
fontFamily: FONT_FAMILY.InterMedium,
|
|
},
|
|
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
|