feat(ui): refine club, packs, and download flows
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
export const COIN_PACK_SECTION_TITLE = 'Achat de crédits'
|
||||
|
||||
export const COIN_PACK_DETAILS = {
|
||||
starter: {
|
||||
label: 'Amateur',
|
||||
creditsDisplay: '30 crédits',
|
||||
priceDisplay: '4,99€',
|
||||
disclaimer: 'Téléchargement gratuit de tes créations',
|
||||
},
|
||||
pro: {
|
||||
label: 'Pro',
|
||||
creditsDisplay: '100 crédits',
|
||||
priceDisplay: '9,99€',
|
||||
popular: true,
|
||||
disclaimer: 'Téléchargement gratuit de tes créations',
|
||||
},
|
||||
premium: {
|
||||
label: 'Engagé',
|
||||
creditsDisplay: '150 crédits',
|
||||
priceDisplay: '14,99€',
|
||||
disclaimer: 'Téléchargement gratuit de tes créations',
|
||||
},
|
||||
}
|
||||
|
||||
export const getCoinPackKey = (pack) => {
|
||||
const directKey = (pack?.coinPackKey || '').toString().trim().toLowerCase()
|
||||
const name = (pack?.name || '').toString().trim().toLowerCase()
|
||||
|
||||
if (directKey === 'starter') {
|
||||
return 'starter'
|
||||
}
|
||||
|
||||
if (directKey === 'pro' || directKey === 'medium') {
|
||||
return 'pro'
|
||||
}
|
||||
|
||||
if (directKey === 'premium' || directKey === 'creator') {
|
||||
return 'premium'
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (name.includes('starter') || name.includes('amateur')) {
|
||||
return 'starter'
|
||||
}
|
||||
|
||||
if (name.includes('medium') || name.includes('pro')) {
|
||||
return 'pro'
|
||||
}
|
||||
|
||||
if (
|
||||
name.includes('premium') ||
|
||||
name.includes('prenium') ||
|
||||
name.includes('creator') ||
|
||||
name.includes('engage')
|
||||
) {
|
||||
return 'premium'
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
+14
-10
@@ -1,21 +1,25 @@
|
||||
const listeners = new Set()
|
||||
const listeners = new Map()
|
||||
let nextListenerId = 0
|
||||
|
||||
export const subscribeCoinPackModal = (listener) => {
|
||||
if (typeof listener !== 'function') {
|
||||
return () => {}
|
||||
}
|
||||
listeners.add(listener)
|
||||
const listenerId = ++nextListenerId
|
||||
listeners.set(listenerId, listener)
|
||||
return () => {
|
||||
listeners.delete(listener)
|
||||
listeners.delete(listenerId)
|
||||
}
|
||||
}
|
||||
|
||||
export const openCoinPackModal = () => {
|
||||
listeners.forEach((listener) => {
|
||||
try {
|
||||
listener()
|
||||
} catch (error) {
|
||||
console.error('[coinPackModal] open listener error', error)
|
||||
}
|
||||
})
|
||||
const activeListener = Array.from(listeners.values()).pop()
|
||||
if (!activeListener) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
activeListener()
|
||||
} catch (error) {
|
||||
console.error('[coinPackModal] open listener error', error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
export const SUBSCRIPTION_PRICE_ID_BY_PERIOD = {
|
||||
monthly: {
|
||||
starter: 'price_1SPgitCzf2o5bDRdbnhLFx6f',
|
||||
pro: 'price_1SPgjCCzf2o5bDRdr08Xzp8u',
|
||||
premium: 'price_1SPgjaCzf2o5bDRdd9Xo2u26',
|
||||
},
|
||||
annual: {
|
||||
starter: 'price_1SPgkDCzf2o5bDRdNGLVNeQ3',
|
||||
pro: 'price_1SPgkXCzf2o5bDRdejBVxEBY',
|
||||
premium: 'price_1SPgkqCzf2o5bDRdIcUwTDrm',
|
||||
},
|
||||
}
|
||||
|
||||
export const SUBSCRIPTION_PRICE_ID_TO_PLAN_KEY = Object.values(
|
||||
SUBSCRIPTION_PRICE_ID_BY_PERIOD
|
||||
).reduce((acc, mapping) => {
|
||||
Object.entries(mapping).forEach(([planKey, priceId]) => {
|
||||
if (priceId) {
|
||||
acc[priceId] = planKey
|
||||
}
|
||||
})
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
export const SUBSCRIPTION_PLAN_SYNONYMS = {
|
||||
starter: ['starter', 'blue'],
|
||||
pro: ['pro', 'silver'],
|
||||
premium: ['premium', 'gold'],
|
||||
}
|
||||
|
||||
export const SUBSCRIPTION_PLAN_ORDER = ['starter', 'pro', 'premium']
|
||||
|
||||
export const SUBSCRIPTION_PLAN_DISPLAY_NAME_BY_KEY = {
|
||||
starter: 'Pack Play Backer Blue',
|
||||
pro: 'Pack Play Backer Silver',
|
||||
premium: 'Pack Play Backer Gold',
|
||||
}
|
||||
|
||||
export const SUBSCRIPTION_PLAN_DETAILS = {
|
||||
starter: {
|
||||
monthlyPriceDisplay: '4,99 € / Mois',
|
||||
annualPriceDisplay: '49,99 € / An (2 mois offerts)',
|
||||
monthlyCreditsLabel: '50 Crédits / Mois',
|
||||
annualCreditsLabel: 'Annuel: 600 crédits',
|
||||
playlistLabel: 'Accès à la création de Playlist personnalisées',
|
||||
downloadLabel: 'Téléchargement gratuit de tes créations',
|
||||
fullAccessLabel: 'Accès au téléchargement de toutes les créations de Musicland',
|
||||
popular: false,
|
||||
},
|
||||
pro: {
|
||||
monthlyPriceDisplay: '9,99 € / Mois',
|
||||
annualPriceDisplay: '99,99 € / An (2 mois offerts)',
|
||||
monthlyCreditsLabel: '110 crédits/Mois',
|
||||
annualCreditsLabel: 'Annuel: 1320 crédits',
|
||||
playlistLabel: 'Accès à la création de Playlist personnalisées',
|
||||
downloadLabel: 'Téléchargement gratuit de tes créations',
|
||||
fullAccessLabel: 'Accès au téléchargement de toutes les créations de Musicland',
|
||||
popular: true,
|
||||
},
|
||||
premium: {
|
||||
monthlyPriceDisplay: '14,99 € / Mois',
|
||||
annualPriceDisplay: '149,99 € / An (2 mois offerts)',
|
||||
monthlyCreditsLabel: '160 Crédits/mois',
|
||||
annualCreditsLabel: 'Annuel: 1920 Crédits',
|
||||
playlistLabel: 'Accès à la création de Playlist personnalisées',
|
||||
downloadLabel: 'Téléchargement gratuit de tes créations',
|
||||
fullAccessLabel: 'Accès au téléchargement de toutes les créations de Musicland',
|
||||
popular: false,
|
||||
},
|
||||
}
|
||||
|
||||
const formatFallbackPrimaryPrice = (formattedPrice, intervalLabel) => {
|
||||
if (!formattedPrice) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!intervalLabel) {
|
||||
return formattedPrice
|
||||
}
|
||||
|
||||
return `${formattedPrice} ${intervalLabel}`
|
||||
}
|
||||
|
||||
export const getSubscriptionPlanKey = (plan) => {
|
||||
if (!plan || typeof plan !== 'object') {
|
||||
return null
|
||||
}
|
||||
|
||||
const priceId = typeof plan?.priceId === 'string' ? plan.priceId : plan?.id || null
|
||||
if (priceId && SUBSCRIPTION_PRICE_ID_TO_PLAN_KEY[priceId]) {
|
||||
return SUBSCRIPTION_PRICE_ID_TO_PLAN_KEY[priceId]
|
||||
}
|
||||
|
||||
const label = (plan?.product?.name || plan?.nickname || '').toString().toLowerCase()
|
||||
if (!label) {
|
||||
return null
|
||||
}
|
||||
|
||||
const match = Object.entries(SUBSCRIPTION_PLAN_SYNONYMS).find(([, variants]) =>
|
||||
variants.some((variant) => label.includes(variant))
|
||||
)
|
||||
|
||||
return match ? match[0] : null
|
||||
}
|
||||
|
||||
export const getSubscriptionCardDisplay = ({
|
||||
planKey,
|
||||
isAnnual = false,
|
||||
formattedPrice = null,
|
||||
intervalLabel = null,
|
||||
coinsPerMonth = null,
|
||||
fallbackTitle = 'Abonnement',
|
||||
}) => {
|
||||
const details = planKey ? SUBSCRIPTION_PLAN_DETAILS[planKey] : null
|
||||
|
||||
return {
|
||||
title:
|
||||
(planKey && SUBSCRIPTION_PLAN_DISPLAY_NAME_BY_KEY[planKey]) || fallbackTitle,
|
||||
primaryPrice:
|
||||
(details && (isAnnual ? details.annualPriceDisplay : details.monthlyPriceDisplay)) ||
|
||||
formatFallbackPrimaryPrice(formattedPrice, intervalLabel),
|
||||
secondaryPrice: details && !isAnnual ? details.annualPriceDisplay : null,
|
||||
creditsLabel:
|
||||
(details && (isAnnual ? details.annualCreditsLabel : details.monthlyCreditsLabel)) ||
|
||||
(typeof coinsPerMonth === 'number' ? `${Math.round(coinsPerMonth)} crédits/mois` : null),
|
||||
downloadLabel: details?.downloadLabel || null,
|
||||
benefitItems: isAnnual
|
||||
? [
|
||||
details?.playlistLabel ? { type: 'text', text: details.playlistLabel } : null,
|
||||
details?.downloadLabel ? { type: 'text', text: details.downloadLabel } : null,
|
||||
{ type: 'plus', text: '+' },
|
||||
details?.fullAccessLabel ? { type: 'text', text: details.fullAccessLabel } : null,
|
||||
].filter(Boolean)
|
||||
: details?.downloadLabel
|
||||
? [{ type: 'text', text: details.downloadLabel }]
|
||||
: [],
|
||||
popular: details?.popular === true,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user