feat: coin packs
This commit is contained in:
@@ -35,7 +35,7 @@ const formatCurrency = (amount, currency = 'eur') => {
|
|||||||
currency: upperCurrency,
|
currency: upperCurrency,
|
||||||
minimumFractionDigits: 2,
|
minimumFractionDigits: 2,
|
||||||
}).format(normalized)
|
}).format(normalized)
|
||||||
} catch (_error) {}
|
} catch (_error) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${normalized.toFixed(2)} ${upperCurrency}`
|
return `${normalized.toFixed(2)} ${upperCurrency}`
|
||||||
@@ -45,6 +45,33 @@ const FALLBACK_BASE_PRICE_PER_COIN = 12 // ~0,12€ par jeton (valeur indicatif
|
|||||||
const FALLBACK_DISCOUNT_STEPS = [0, 12, 18, 26, 32]
|
const FALLBACK_DISCOUNT_STEPS = [0, 12, 18, 26, 32]
|
||||||
const STATIC_DISCOUNTS = [0, 30, 50]
|
const STATIC_DISCOUNTS = [0, 30, 50]
|
||||||
|
|
||||||
|
const COIN_PACK_DETAILS = {
|
||||||
|
starter: {
|
||||||
|
label: 'Pack starter',
|
||||||
|
creditsDisplay: '50 Crédits',
|
||||||
|
disclaimer: '*Non-Eligible au concours Chanson/Vidéo',
|
||||||
|
},
|
||||||
|
medium: {
|
||||||
|
label: 'Pack Medium',
|
||||||
|
creditsDisplay: '100 Crédits + 50 offerts',
|
||||||
|
disclaimer: '*Non-Eligible au concours Chanson/Vidéo',
|
||||||
|
},
|
||||||
|
premium: {
|
||||||
|
label: 'Pack Premium',
|
||||||
|
creditsDisplay: '150 Crédits + 100 offerts',
|
||||||
|
disclaimer: '*Non-Eligible au concours Chanson/Vidéo',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCoinPackKey = (pack) => {
|
||||||
|
const name = (pack?.name || '').toLowerCase()
|
||||||
|
if (name.includes('starter')) return 'starter'
|
||||||
|
if (name.includes('medium')) return 'medium'
|
||||||
|
if (name.includes('premium') || name.includes('prenium') || name.includes('creator'))
|
||||||
|
return 'premium'
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const computeCoinPackPricing = (packs = []) => {
|
const computeCoinPackPricing = (packs = []) => {
|
||||||
if (!Array.isArray(packs) || !packs.length) {
|
if (!Array.isArray(packs) || !packs.length) {
|
||||||
return {}
|
return {}
|
||||||
@@ -160,6 +187,11 @@ function CoinPackCard({ pack, selected, pricingDetail, onSelect, staticDiscountP
|
|||||||
? `-${discountPercent}% vs pack de base`
|
? `-${discountPercent}% vs pack de base`
|
||||||
: null
|
: null
|
||||||
|
|
||||||
|
const packKey = getCoinPackKey(pack)
|
||||||
|
const details = packKey ? COIN_PACK_DETAILS[packKey] : null
|
||||||
|
const displayName = details?.label || pack.name
|
||||||
|
const creditsText = details?.creditsDisplay
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={handleSelect}
|
onPress={handleSelect}
|
||||||
@@ -172,18 +204,18 @@ function CoinPackCard({ pack, selected, pricingDetail, onSelect, staticDiscountP
|
|||||||
accessibilityState={{ selected }}
|
accessibilityState={{ selected }}
|
||||||
>
|
>
|
||||||
<BlurView intensity={20} tint="dark" style={styles.cardBlur}>
|
<BlurView intensity={20} tint="dark" style={styles.cardBlur}>
|
||||||
{/*{pricingDetail?.isBestValue ? (*/}
|
|
||||||
{/* <View style={styles.tagBestValue}>*/}
|
|
||||||
{/* <Text style={styles.tagBestValueText}>Meilleure offre</Text>*/}
|
|
||||||
{/* </View>*/}
|
|
||||||
{/*) : null}*/}
|
|
||||||
<View style={styles.cardHeader}>
|
<View style={styles.cardHeader}>
|
||||||
|
{creditsText ? (
|
||||||
|
<Text style={styles.coinAmountCustom}>{creditsText}</Text>
|
||||||
|
) : (
|
||||||
<CreditAmount
|
<CreditAmount
|
||||||
value={pack?.coinAmount}
|
value={pack?.coinAmount}
|
||||||
style={styles.coinRow}
|
style={styles.coinRow}
|
||||||
textStyle={styles.coinAmount}
|
textStyle={styles.coinAmount}
|
||||||
iconSize={26}
|
iconSize={26}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{discountLabel ? (
|
{discountLabel ? (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
@@ -197,7 +229,10 @@ function CoinPackCard({ pack, selected, pricingDetail, onSelect, staticDiscountP
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
{pack?.name ? <Text style={styles.packName}>{pack.name}</Text> : null}
|
<Text style={styles.packName}>{displayName}</Text>
|
||||||
|
{details?.disclaimer ? (
|
||||||
|
<Text style={styles.packDisclaimer}>{details.disclaimer}</Text>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.priceBlock}>
|
<View style={styles.priceBlock}>
|
||||||
{formattedPrice ? <Text style={styles.packPrice}>{formattedPrice}</Text> : null}
|
{formattedPrice ? <Text style={styles.packPrice}>{formattedPrice}</Text> : null}
|
||||||
@@ -486,6 +521,20 @@ const styles = StyleSheet.create({
|
|||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: Palette.white,
|
color: Palette.white,
|
||||||
},
|
},
|
||||||
|
coinAmountCustom: {
|
||||||
|
fontFamily: FONT_FAMILY.InterBold,
|
||||||
|
fontSize: 20,
|
||||||
|
color: Palette.white,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
packDisclaimer: {
|
||||||
|
fontFamily: FONT_FAMILY.InterRegular,
|
||||||
|
fontSize: 10,
|
||||||
|
color: 'rgba(255, 255, 255, 0.6)',
|
||||||
|
fontStyle: 'italic',
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: 4,
|
||||||
|
},
|
||||||
packName: {
|
packName: {
|
||||||
fontFamily: FONT_FAMILY.InterMedium,
|
fontFamily: FONT_FAMILY.InterMedium,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
|||||||
@@ -240,6 +240,33 @@ const PLAN_DISPLAY_NAME_BY_KEY = {
|
|||||||
premium: 'Play Backer Gold',
|
premium: 'Play Backer Gold',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const COIN_PACK_DETAILS = {
|
||||||
|
starter: {
|
||||||
|
label: 'Pack starter',
|
||||||
|
creditsDisplay: '50 Crédits',
|
||||||
|
disclaimer: '*Non-Eligible au concours Chanson/Vidéo',
|
||||||
|
},
|
||||||
|
medium: {
|
||||||
|
label: 'Pack Medium',
|
||||||
|
creditsDisplay: '100 Crédits + 50 offerts',
|
||||||
|
disclaimer: '*Non-Eligible au concours Chanson/Vidéo',
|
||||||
|
},
|
||||||
|
premium: {
|
||||||
|
label: 'Pack Premium',
|
||||||
|
creditsDisplay: '150 Crédits + 100 offerts',
|
||||||
|
disclaimer: '*Non-Eligible au concours Chanson/Vidéo',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCoinPackKey = (pack) => {
|
||||||
|
const name = (pack?.name || '').toLowerCase()
|
||||||
|
if (name.includes('starter')) return 'starter'
|
||||||
|
if (name.includes('medium')) return 'medium'
|
||||||
|
if (name.includes('premium') || name.includes('prenium') || name.includes('creator'))
|
||||||
|
return 'premium'
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const PLAN_DETAILS = {
|
const PLAN_DETAILS = {
|
||||||
starter: {
|
starter: {
|
||||||
creditsLabel: '50 Crédits / Mois',
|
creditsLabel: '50 Crédits / Mois',
|
||||||
@@ -746,15 +773,28 @@ export default function Subscriptions() {
|
|||||||
const price = formatCurrency(rawAmount, pack.currency)
|
const price = formatCurrency(rawAmount, pack.currency)
|
||||||
// Assuming pack structure: { id, name, unitAmount/amount, currency, metadata: { coins: 100 } }
|
// Assuming pack structure: { id, name, unitAmount/amount, currency, metadata: { coins: 100 } }
|
||||||
const coins = Number(pack.metadata?.coins || 0)
|
const coins = Number(pack.metadata?.coins || 0)
|
||||||
|
|
||||||
|
const packKey = getCoinPackKey(pack)
|
||||||
|
const details = packKey ? COIN_PACK_DETAILS[packKey] : null
|
||||||
|
const displayName = details?.label || pack.name
|
||||||
|
const creditsText = details?.creditsDisplay
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View key={pack.productId} style={styles.creditCard}>
|
<View key={pack.productId} style={styles.creditCard}>
|
||||||
<View>
|
<View style={styles.creditInfoContainer}>
|
||||||
<Text style={styles.creditName}>{pack.name}</Text>
|
<Text style={styles.creditName}>{displayName}</Text>
|
||||||
|
{creditsText ? (
|
||||||
|
<Text style={styles.creditAmountCustom}>{creditsText}</Text>
|
||||||
|
) : (
|
||||||
<CreditAmount
|
<CreditAmount
|
||||||
value={coins}
|
value={coins}
|
||||||
iconSize={24}
|
iconSize={24}
|
||||||
textStyle={styles.creditAmountText}
|
textStyle={styles.creditAmountText}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
{details?.disclaimer ? (
|
||||||
|
<Text style={styles.packDisclaimer}>{details.disclaimer}</Text>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
<GradientButton
|
<GradientButton
|
||||||
title={
|
title={
|
||||||
@@ -974,6 +1014,24 @@ const styles = StyleSheet.create({
|
|||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
},
|
},
|
||||||
|
creditInfoContainer: {
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 4,
|
||||||
|
},
|
||||||
|
creditAmountCustom: {
|
||||||
|
fontFamily: FONT_FAMILY.InterBold,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Palette.white,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
packDisclaimer: {
|
||||||
|
fontFamily: FONT_FAMILY.InterRegular,
|
||||||
|
fontSize: 10,
|
||||||
|
color: 'rgba(255, 255, 255, 0.6)',
|
||||||
|
fontStyle: 'italic',
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: 4,
|
||||||
|
},
|
||||||
creditAmountText: {
|
creditAmountText: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user