fix(ui): tarifs publics unifiés et prix annuel lisible
La page tarifs reprend la barre du site public. En annuel, Pro affiche 8,25 € par mois (99 € l’année), plus le 99 € comme gros chiffre. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -11,6 +11,12 @@ import { format } from 'date-fns';
|
||||
import { motion } from 'motion/react';
|
||||
import { BillingHistory } from './billing-history';
|
||||
import { SUBSCRIPTION_TRIAL_DAYS } from '@/lib/billing/trial-constants';
|
||||
import {
|
||||
DEFAULT_PRICES,
|
||||
annualDiscountPercent,
|
||||
formatBillingAmount,
|
||||
yearToMonthlyEquivalent,
|
||||
} from '@/lib/billing/price-catalog';
|
||||
|
||||
type Tier = 'PRO' | 'BUSINESS';
|
||||
type Interval = 'month' | 'year';
|
||||
@@ -285,12 +291,36 @@ export function BillingPlans() {
|
||||
const trialCta = (fallback: string) =>
|
||||
trialEligible ? t('billing.startTrialCta', { days: trialDays }) : fallback;
|
||||
|
||||
const proMonth = status?.prices?.PRO?.month ?? DEFAULT_PRICES.PRO.month;
|
||||
const proYear = status?.prices?.PRO?.year ?? DEFAULT_PRICES.PRO.year;
|
||||
const businessMonth = status?.prices?.BUSINESS?.month ?? DEFAULT_PRICES.BUSINESS.month;
|
||||
const businessYear = status?.prices?.BUSINESS?.year ?? DEFAULT_PRICES.BUSINESS.year;
|
||||
const savePercent = annualDiscountPercent(proMonth.amount, proYear.amount);
|
||||
|
||||
const billed = (month: typeof proMonth, year: typeof proYear) => {
|
||||
if (interval === 'month') {
|
||||
return {
|
||||
price: month.display,
|
||||
period: t('billing.perMonth'),
|
||||
yearHint: null as string | null,
|
||||
};
|
||||
}
|
||||
return {
|
||||
price: formatBillingAmount(yearToMonthlyEquivalent(year.amount), year.currency),
|
||||
period: t('landing.pricing.perMonthAnnual'),
|
||||
yearHint: t('billing.billedYearTotal', { price: year.display }),
|
||||
};
|
||||
};
|
||||
const proBilled = billed(proMonth, proYear);
|
||||
const businessBilled = billed(businessMonth, businessYear);
|
||||
|
||||
const plans = [
|
||||
{
|
||||
id: 'free',
|
||||
name: t('billing.freePlan'),
|
||||
price: t('billing.freePrice') || 'Gratuit',
|
||||
period: '',
|
||||
yearHint: null as string | null,
|
||||
description: t('billing.freeDescription') || 'Pour découvrir Memento.',
|
||||
features: [
|
||||
t('billing.freeF1'),
|
||||
@@ -313,9 +343,9 @@ export function BillingPlans() {
|
||||
{
|
||||
id: 'pro',
|
||||
name: t('billing.proPlan'),
|
||||
price: status?.prices?.PRO?.[interval]?.display ??
|
||||
(interval === 'month' ? (t('billing.proPrice') || '9,90€') : (t('billing.proAnnualPrice') || '99€')),
|
||||
period: interval === 'month' ? t('billing.perMonth') : t('billing.perYear'),
|
||||
price: proBilled.price,
|
||||
period: proBilled.period,
|
||||
yearHint: proBilled.yearHint,
|
||||
description: t('billing.proDescription') || 'Pour les consultants et créateurs exigeants.',
|
||||
features: [
|
||||
...(trialEligible ? [t('billing.trialFeature', { days: trialDays })] : []),
|
||||
@@ -337,9 +367,9 @@ export function BillingPlans() {
|
||||
{
|
||||
id: 'business',
|
||||
name: t('billing.businessPlan'),
|
||||
price: status?.prices?.BUSINESS?.[interval]?.display ??
|
||||
(interval === 'month' ? (t('billing.businessPrice') || '29,90€') : (t('billing.businessAnnualPrice') || '299€')),
|
||||
period: interval === 'month' ? t('billing.perMonth') : t('billing.perYear'),
|
||||
price: businessBilled.price,
|
||||
period: businessBilled.period,
|
||||
yearHint: businessBilled.yearHint,
|
||||
features: [
|
||||
...(trialEligible ? [t('billing.trialFeature', { days: trialDays })] : []),
|
||||
t('billing.businessFeature1'),
|
||||
@@ -361,6 +391,7 @@ export function BillingPlans() {
|
||||
name: t('billing.enterpriseTitle') || 'Enterprise',
|
||||
price: t('billing.contactSales') || 'Sur devis',
|
||||
period: '',
|
||||
yearHint: null as string | null,
|
||||
description: t('billing.enterpriseDescription') || 'Crédits illimités ou pool dédié, connexion unique pour l’équipe, support prioritaire.',
|
||||
features: [
|
||||
t('billing.enterpriseFeature1'),
|
||||
@@ -765,7 +796,11 @@ export function BillingPlans() {
|
||||
)}
|
||||
>
|
||||
{t('billing.annual')}
|
||||
<span className="ms-1 text-primary/80 dark:text-primary">{t('billing.savePercent')}</span>
|
||||
{savePercent > 0 && (
|
||||
<span className="ms-1 text-primary/80 dark:text-primary">
|
||||
{t('billing.savePercent', { percent: savePercent })}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
@@ -800,6 +835,9 @@ export function BillingPlans() {
|
||||
<span className="text-4xl font-serif font-bold text-ink">{plan.price}</span>
|
||||
<span className="text-concrete text-xs font-light italic">{plan.period}</span>
|
||||
</div>
|
||||
{plan.yearHint && (
|
||||
<p className="text-xs text-concrete font-light">{plan.yearHint}</p>
|
||||
)}
|
||||
<p className="text-xs text-concrete font-light leading-relaxed pe-4">{plan.description}</p>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user