continue subscriptions and coins

This commit is contained in:
Thomas Demirdjian
2025-11-05 15:39:15 +01:00
parent a58eeac079
commit 5f777f3b6d
25 changed files with 1994 additions and 222 deletions
+74 -110
View File
@@ -18,12 +18,7 @@ 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",
SUBSCRIPTION: "Abonnement",
};
const formatCoins = (amount) => {
@@ -39,26 +34,39 @@ const formatCoins = (amount) => {
}
};
const formatDate = (date) => {
const formatDateParts = (date) => {
if (!date) {
return "En attente de confirmation";
return {
dateLabel: "En attente de confirmation",
timeLabel: "",
};
}
try {
return date.toLocaleString("fr-FR", {
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 date.toString();
return {
dateLabel: date.toString(),
timeLabel: "",
};
}
};
const mapOrderType = (type) => ORDER_TYPE_LABELS[type] || "Opération";
const mapStatus = (status) => STATUS_LABELS[status] || "Inconnue";
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() || {};
@@ -126,64 +134,64 @@ const OrderHistory = () => {
: 0;
const isPositive = amountValue > 0;
const amountLabel = `${isPositive ? "+" : ""}${formatCoins(amountValue)} ${
amountValue === 1 ? "coin" : "coins"
Math.abs(amountValue) === 1 ? "coin" : "coins"
}`;
const status = item.status || "PENDING";
const { dateLabel } = formatDateParts(item.createdAt);
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;
}
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();
}
if (item.type === "SONG" && item.songId) {
return `Musique ${item.songId}`;
} 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";
}
if (item.type === "COINS" && item.metadata?.paymentId) {
return `Paiement ${item.metadata.paymentId}`;
}
return null;
})();
} 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}>
<View style={styles.orderHeader}>
<Text style={styles.orderTitle}>{mapOrderType(item.type)}</Text>
<Text
style={[
styles.orderAmount,
isPositive ? styles.amountPositive : styles.amountNegative,
]}
>
{amountLabel}
</Text>
</View>
<View style={styles.orderMetaRow}>
<Text style={styles.orderDate}>{formatDate(item.createdAt)}</Text>
<View
style={[
styles.statusBadge,
status === "APPLIED"
? styles.statusApplied
: status === "REJECTED"
? styles.statusRejected
: styles.statusPending,
]}
>
<Text style={styles.statusText}>{mapStatus(status)}</Text>
</View>
</View>
{details ? <Text style={styles.orderDetails}>{details}</Text> : null}
{typeof item.balanceAfter === "number" &&
Number.isFinite(item.balanceAfter) ? (
<Text style={styles.orderBalance}>
Solde après opération: {formatCoins(item.balanceAfter)} coins
</Text>
) : null}
<Text
style={[
styles.orderAmount,
isPositive ? styles.amountPositive : styles.amountNegative,
]}
>
{amountLabel}
</Text>
<Text style={styles.orderReason}>{reasonLabel}</Text>
<Text style={styles.orderTimestamp}>{dateLabel}</Text>
</View>
);
}, []);
@@ -268,19 +276,7 @@ const styles = StyleSheet.create({
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,
gap: 6,
},
orderAmount: {
fontSize: 16,
@@ -292,47 +288,15 @@ const styles = StyleSheet.create({
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,
orderReason: {
fontSize: 14,
fontFamily: FONT_FAMILY.InterRegular,
color: Palette.white,
},
orderBalance: {
fontSize: 13,
fontFamily: FONT_FAMILY.InterRegular,
orderTimestamp: {
fontSize: 12,
color: Palette.grayMid,
fontFamily: FONT_FAMILY.InterMedium,
},
emptyText: {
textAlign: "center",