Files
afrov2/components/PricingSection.tsx
2026-02-22 20:25:47 +01:00

326 lines
20 KiB
TypeScript

import React, { useState } from 'react';
import { Check, X, Smartphone, CreditCard, Loader, ShieldCheck } from 'lucide-react';
interface Plan {
id: string;
name: string;
priceXOF: string;
priceEUR: string;
description: string;
features: string[];
recommended?: boolean;
color: string;
}
const PLANS: Plan[] = [
{
id: 'starter',
name: 'Starter',
priceXOF: 'Gratuit',
priceEUR: '0€',
description: 'Pour démarrer votre présence en ligne.',
features: [
'Fiche entreprise basique',
'Visible dans la recherche',
'1 Offre produit/service',
'Support par email'
],
color: 'gray'
},
{
id: 'booster',
name: 'Booster',
priceXOF: '5.000 FCFA',
priceEUR: '8€',
description: 'L\'indispensable pour les entreprises en croissance.',
recommended: true,
features: [
'Tout du plan Starter',
'Badge "Vérifié" ✅',
'Jusqu\'à 10 Offres produits',
'Lien vers réseaux sociaux & Site Web',
'Statistiques de base (Vues)'
],
color: 'brand'
},
{
id: 'empire',
name: 'Empire',
priceXOF: '15.000 FCFA',
priceEUR: '23€',
description: 'Dominez votre marché avec une visibilité maximale.',
features: [
'Tout du plan Booster',
'Badge "Recommandé" 🏆',
'Offres illimitées',
'Intégration vidéo Youtube',
'Interview écrite sur le Blog',
'Support prioritaire WhatsApp'
],
color: 'gray'
}
];
const PricingSection = () => {
const [selectedPlan, setSelectedPlan] = useState<Plan | null>(null);
const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>('monthly');
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
// Payment State
const [paymentStep, setPaymentStep] = useState<'method' | 'processing' | 'success'>('method');
const [paymentMethod, setPaymentMethod] = useState<'mobile_money' | 'card' | null>(null);
const handleSelectPlan = (plan: Plan) => {
if (plan.id === 'starter') return; // Free plan, no payment
setSelectedPlan(plan);
setPaymentStep('method');
setIsPaymentModalOpen(true);
};
const handlePayment = () => {
setPaymentStep('processing');
// Fake API delay
setTimeout(() => {
setPaymentStep('success');
}, 2500);
};
const closePayment = () => {
setIsPaymentModalOpen(false);
setPaymentStep('method');
setSelectedPlan(null);
setPaymentMethod(null);
};
return (
<div className="py-12 bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="text-base font-semibold text-brand-600 tracking-wide uppercase">Tarifs</h2>
<p className="mt-1 text-4xl font-extrabold text-gray-900 sm:text-5xl sm:tracking-tight lg:text-6xl font-serif">
Investissez dans votre croissance
</p>
<p className="max-w-xl mt-5 mx-auto text-xl text-gray-500">
Choisissez le plan adapté à vos ambitions. Changez ou annulez à tout moment.
</p>
</div>
{/* Billing Toggle */}
<div className="mt-12 flex justify-center">
<div className="relative bg-white rounded-lg p-0.5 flex sm:mt-0 shadow-sm border border-gray-200">
<button
type="button"
onClick={() => setBillingCycle('monthly')}
className={`${billingCycle === 'monthly' ? 'bg-gray-100 border-gray-200 shadow-sm text-gray-900' : 'border-transparent text-gray-500 hover:text-gray-700'} relative w-1/2 whitespace-nowrap py-2 px-6 rounded-md text-sm font-medium focus:outline-none transition-all sm:w-auto sm:px-8`}
>
Mensuel
</button>
<button
type="button"
onClick={() => setBillingCycle('yearly')}
className={`${billingCycle === 'yearly' ? 'bg-gray-100 border-gray-200 shadow-sm text-gray-900' : 'border-transparent text-gray-500 hover:text-gray-700'} relative w-1/2 whitespace-nowrap py-2 px-6 rounded-md text-sm font-medium focus:outline-none transition-all sm:w-auto sm:px-8`}
>
Annuel <span className="text-brand-600 text-xs ml-1 font-bold">-20%</span>
</button>
</div>
</div>
{/* Plans Grid */}
<div className="mt-12 space-y-4 sm:mt-16 sm:space-y-0 sm:grid sm:grid-cols-2 sm:gap-6 lg:max-w-4xl lg:mx-auto xl:max-w-none xl:mx-0 xl:grid-cols-3">
{PLANS.map((plan) => (
<div key={plan.id} className={`rounded-2xl shadow-xl bg-white border-2 flex flex-col ${plan.recommended ? 'border-brand-500 ring-4 ring-brand-50 relative transform scale-105 z-10' : 'border-gray-100'}`}>
{plan.recommended && (
<div className="absolute top-0 inset-x-0 -mt-4 flex justify-center">
<span className="bg-brand-500 text-white px-4 py-1 rounded-full text-xs font-bold uppercase tracking-wider shadow-sm">
Populaire
</span>
</div>
)}
<div className="p-6 md:p-8 flex-1">
<h3 className="text-2xl font-bold text-gray-900 font-serif">{plan.name}</h3>
<p className="mt-4 text-sm text-gray-500">{plan.description}</p>
<div className="mt-8 flex items-baseline">
<span className="text-4xl font-extrabold text-gray-900 tracking-tight">
{plan.priceXOF === 'Gratuit' ? 'Gratuit' : (billingCycle === 'yearly' && plan.id !== 'starter' ? 'Sur Devis' : plan.priceXOF)}
</span>
{plan.priceXOF !== 'Gratuit' && <span className="ml-1 text-xl font-medium text-gray-500">/mois</span>}
</div>
{plan.priceXOF !== 'Gratuit' && (
<p className="text-xs text-gray-400 mt-1">soit env. {plan.priceEUR} /mois</p>
)}
<ul className="mt-8 space-y-4">
{plan.features.map((feature) => (
<li key={feature} className="flex items-start">
<div className="flex-shrink-0">
<Check className="h-5 w-5 text-green-500" />
</div>
<p className="ml-3 text-sm text-gray-700">{feature}</p>
</li>
))}
</ul>
</div>
<div className="p-6 bg-gray-50 rounded-b-2xl">
<button
onClick={() => handleSelectPlan(plan)}
className={`w-full block text-center rounded-lg border border-transparent px-6 py-3 text-base font-medium transition-colors ${
plan.id === 'starter'
? 'text-brand-700 bg-brand-100 hover:bg-brand-200'
: 'text-white bg-brand-600 hover:bg-brand-700 shadow-md hover:shadow-lg'
}`}
>
{plan.id === 'starter' ? 'Commencer Gratuitement' : `Choisir ${plan.name}`}
</button>
</div>
</div>
))}
</div>
</div>
{/* FAKE PAYMENT MODAL */}
{isPaymentModalOpen && selectedPlan && (
<div className="fixed inset-0 z-50 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div className="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
{/* Overlay */}
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" onClick={closePayment}></div>
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg w-full">
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
{/* Header */}
<div className="flex justify-between items-center mb-5">
<h3 className="text-lg leading-6 font-medium text-gray-900 font-serif" id="modal-title">
Paiement Sécurisé
</h3>
<button onClick={closePayment} className="text-gray-400 hover:text-gray-500">
<X className="h-6 w-6" />
</button>
</div>
{/* Content based on step */}
{paymentStep === 'method' && (
<>
<div className="bg-brand-50 p-4 rounded-md mb-6 border border-brand-100">
<p className="text-sm text-brand-800 font-medium">Vous avez choisi le plan <span className="font-bold">{selectedPlan.name}</span></p>
<p className="text-2xl font-bold text-brand-900 mt-1">{selectedPlan.priceXOF}</p>
</div>
<p className="text-sm font-medium text-gray-700 mb-3">Moyen de paiement</p>
<div className="grid grid-cols-1 gap-3">
<button
onClick={() => setPaymentMethod('mobile_money')}
className={`flex items-center p-4 border rounded-lg hover:bg-gray-50 transition-all ${paymentMethod === 'mobile_money' ? 'border-brand-500 ring-1 ring-brand-500 bg-brand-50' : 'border-gray-300'}`}
>
<div className="h-10 w-10 bg-orange-100 rounded-full flex items-center justify-center text-orange-600 mr-4">
<Smartphone className="h-6 w-6" />
</div>
<div className="text-left flex-1">
<p className="font-bold text-gray-900">Mobile Money</p>
<p className="text-xs text-gray-500">Orange Money, MTN, Wave, Moov</p>
</div>
<div className="h-4 w-4 rounded-full border border-gray-300 flex items-center justify-center">
{paymentMethod === 'mobile_money' && <div className="h-2 w-2 rounded-full bg-brand-600"></div>}
</div>
</button>
<button
onClick={() => setPaymentMethod('card')}
className={`flex items-center p-4 border rounded-lg hover:bg-gray-50 transition-all ${paymentMethod === 'card' ? 'border-brand-500 ring-1 ring-brand-500 bg-brand-50' : 'border-gray-300'}`}
>
<div className="h-10 w-10 bg-blue-100 rounded-full flex items-center justify-center text-blue-600 mr-4">
<CreditCard className="h-6 w-6" />
</div>
<div className="text-left flex-1">
<p className="font-bold text-gray-900">Carte Bancaire</p>
<p className="text-xs text-gray-500">Visa, Mastercard</p>
</div>
<div className="h-4 w-4 rounded-full border border-gray-300 flex items-center justify-center">
{paymentMethod === 'card' && <div className="h-2 w-2 rounded-full bg-brand-600"></div>}
</div>
</button>
</div>
{paymentMethod === 'mobile_money' && (
<div className="mt-4 animate-in fade-in slide-in-from-top-2">
<label className="block text-sm font-medium text-gray-700">Numéro de téléphone</label>
<input type="text" placeholder="+225 07..." className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:ring-brand-500 focus:border-brand-500 sm:text-sm" />
</div>
)}
{paymentMethod === 'card' && (
<div className="mt-4 animate-in fade-in slide-in-from-top-2 space-y-3">
<div>
<label className="block text-sm font-medium text-gray-700">Numéro de carte</label>
<input type="text" placeholder="0000 0000 0000 0000" className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:ring-brand-500 focus:border-brand-500 sm:text-sm" />
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700">Expiration</label>
<input type="text" placeholder="MM/AA" className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:ring-brand-500 focus:border-brand-500 sm:text-sm" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700">CVC</label>
<input type="text" placeholder="123" className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:ring-brand-500 focus:border-brand-500 sm:text-sm" />
</div>
</div>
</div>
)}
</>
)}
{paymentStep === 'processing' && (
<div className="py-10 text-center">
<Loader className="h-12 w-12 text-brand-600 animate-spin mx-auto mb-4" />
<p className="text-lg font-medium text-gray-900">Traitement en cours...</p>
<p className="text-sm text-gray-500 mt-2">Veuillez valider la transaction sur votre mobile si nécessaire.</p>
</div>
)}
{paymentStep === 'success' && (
<div className="py-6 text-center animate-in zoom-in duration-300">
<div className="mx-auto flex items-center justify-center h-16 w-16 rounded-full bg-green-100 mb-4">
<ShieldCheck className="h-10 w-10 text-green-600" />
</div>
<h3 className="text-xl font-bold text-gray-900">Paiement Réussi !</h3>
<p className="text-gray-500 mt-2 mb-6">Votre abonnement {selectedPlan.name} est maintenant actif.</p>
<button
onClick={closePayment}
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-brand-600 text-base font-medium text-white hover:bg-brand-700 focus:outline-none sm:text-sm"
>
Accéder à mon espace
</button>
</div>
)}
</div>
{paymentStep === 'method' && (
<div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
type="button"
disabled={!paymentMethod}
onClick={handlePayment}
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-brand-600 text-base font-medium text-white hover:bg-brand-700 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed sm:ml-3 sm:w-auto sm:text-sm"
>
Payer {selectedPlan.priceXOF}
</button>
<button
type="button"
onClick={closePayment}
className="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
>
Annuler
</button>
</div>
)}
</div>
</div>
</div>
)}
</div>
);
};
export default PricingSection;