first commit
This commit is contained in:
325
components/PricingSection.tsx
Normal file
325
components/PricingSection.tsx
Normal file
@@ -0,0 +1,325 @@
|
||||
|
||||
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">​</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;
|
||||
Reference in New Issue
Block a user