252 lines
11 KiB
TypeScript
252 lines
11 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import { Zap, Calendar, Newspaper, Layout, Check, Smartphone, CreditCard, Loader, ShieldCheck, X } from 'lucide-react';
|
|
import { useUser } from './UserProvider';
|
|
import { useRouter } from 'next/navigation';
|
|
import CreateEventModal from './dashboard/CreateEventModal';
|
|
import CreateNewsModal from './dashboard/CreateNewsModal';
|
|
import { toast } from 'react-hot-toast';
|
|
|
|
interface OneShotPlan {
|
|
id: string;
|
|
type: string;
|
|
name: string;
|
|
priceXOF: number;
|
|
priceEUR: number;
|
|
description: string | null;
|
|
}
|
|
|
|
const TYPE_ICONS = {
|
|
BUMP: Zap,
|
|
POST_EVENT: Calendar,
|
|
POST_NEWS: Newspaper,
|
|
AD_DIRECTORY: Layout,
|
|
};
|
|
|
|
const OneShotPricingSection = () => {
|
|
const [plans, setPlans] = useState<OneShotPlan[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [selectedPlan, setSelectedPlan] = useState<OneShotPlan | null>(null);
|
|
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
|
|
const [paymentStep, setPaymentStep] = useState<'method' | 'processing' | 'success'>('method');
|
|
const [paymentMethod, setPaymentMethod] = useState<'mobile_money' | 'card' | null>(null);
|
|
const [showEventModal, setShowEventModal] = useState(false);
|
|
const [showNewsModal, setShowNewsModal] = useState(false);
|
|
const { user } = useUser();
|
|
const router = useRouter();
|
|
|
|
useEffect(() => {
|
|
const fetchPlans = async () => {
|
|
try {
|
|
const res = await fetch('/api/one-shot-plans');
|
|
const data = await res.json();
|
|
if (!data.error) {
|
|
setPlans(data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching one-shot plans:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
fetchPlans();
|
|
}, []);
|
|
|
|
const handleSelectPlan = (plan: OneShotPlan) => {
|
|
if (!user) {
|
|
toast.error("Veuillez vous connecter pour commander un service");
|
|
router.push('/login?callback=/subscription');
|
|
return;
|
|
}
|
|
setSelectedPlan(plan);
|
|
setPaymentStep('method');
|
|
setIsPaymentModalOpen(true);
|
|
};
|
|
|
|
const handlePayment = () => {
|
|
setPaymentStep('processing');
|
|
setTimeout(() => {
|
|
setPaymentStep('success');
|
|
}, 2500);
|
|
};
|
|
|
|
const closePayment = () => {
|
|
setIsPaymentModalOpen(false);
|
|
setPaymentStep('method');
|
|
setSelectedPlan(null);
|
|
setPaymentMethod(null);
|
|
};
|
|
|
|
if (!loading && plans.length === 0) return null;
|
|
|
|
return (
|
|
<div className="py-16 bg-white">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className="text-center mb-12">
|
|
<h2 className="text-base font-semibold text-brand-600 tracking-wide uppercase">Services à la carte</h2>
|
|
<p className="mt-1 text-3xl font-extrabold text-gray-900 sm:text-4xl font-serif">
|
|
Boostez votre visibilité ponctuellement
|
|
</p>
|
|
<p className="max-w-xl mt-4 mx-auto text-lg text-gray-500">
|
|
Des options flexibles pour mettre en avant vos actualités et vos offres sans engagement.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
{loading ? (
|
|
[1, 2, 3, 4].map((i) => (
|
|
<div key={i} className="bg-gray-50 rounded-2xl p-6 h-64 animate-pulse border border-gray-100">
|
|
<div className="w-12 h-12 bg-gray-200 rounded-xl mb-4"></div>
|
|
<div className="h-6 bg-gray-200 rounded w-3/4 mb-4"></div>
|
|
<div className="h-4 bg-gray-200 rounded w-full mb-8"></div>
|
|
<div className="h-10 bg-gray-200 rounded w-full"></div>
|
|
</div>
|
|
))
|
|
) : (
|
|
plans.map((plan) => {
|
|
const Icon = TYPE_ICONS[plan.type as keyof typeof TYPE_ICONS] || Zap;
|
|
return (
|
|
<div key={plan.id} className="bg-gray-50 rounded-2xl p-8 border border-gray-100 hover:border-brand-200 hover:shadow-lg transition-all group flex flex-col">
|
|
<div className="w-14 h-14 bg-brand-50 rounded-2xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform">
|
|
<Icon className="w-8 h-8 text-brand-600" />
|
|
</div>
|
|
<h3 className="text-xl font-bold text-gray-900 font-serif mb-2">{plan.name}</h3>
|
|
<p className="text-sm text-gray-500 mb-6 flex-1">{plan.description}</p>
|
|
|
|
<div className="mb-6">
|
|
<span className="text-3xl font-black text-gray-900">{plan.priceXOF.toLocaleString()} F CFA</span>
|
|
<p className="text-xs text-gray-400 mt-1">env. {plan.priceEUR} €</p>
|
|
</div>
|
|
|
|
<button
|
|
onClick={() => handleSelectPlan(plan)}
|
|
className="w-full bg-white border-2 border-brand-600 text-brand-600 hover:bg-brand-600 hover:text-white font-bold py-3 rounded-xl transition-all shadow-sm"
|
|
>
|
|
Commander
|
|
</button>
|
|
</div>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* PAYMENT MODAL (Simplified copy from PricingSection) */}
|
|
{isPaymentModalOpen && selectedPlan && (
|
|
<div className="fixed inset-0 z-50 overflow-y-auto">
|
|
<div className="flex items-center justify-center min-h-screen px-4">
|
|
<div className="fixed inset-0 bg-gray-900/60 backdrop-blur-sm transition-opacity" onClick={closePayment}></div>
|
|
<div className="relative bg-white rounded-2xl text-left overflow-hidden shadow-2xl transform transition-all max-w-lg w-full">
|
|
<div className="px-6 py-8">
|
|
<div className="flex justify-between items-center mb-6">
|
|
<h3 className="text-xl font-bold text-gray-900 font-serif">Paiement Service Ponctuel</h3>
|
|
<button onClick={closePayment} className="text-gray-400 hover:text-gray-600"><X className="h-6 w-6" /></button>
|
|
</div>
|
|
|
|
{paymentStep === 'method' && (
|
|
<>
|
|
<div className="bg-brand-50 p-5 rounded-2xl mb-8 border border-brand-100">
|
|
<p className="text-sm text-brand-800 font-medium">Option : <span className="font-bold">{selectedPlan.name}</span></p>
|
|
<p className="text-3xl font-black text-brand-900 mt-1">{selectedPlan.priceXOF.toLocaleString()} F CFA</p>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
<button
|
|
onClick={() => setPaymentMethod('mobile_money')}
|
|
className={`w-full flex items-center p-4 border-2 rounded-xl transition-all ${paymentMethod === 'mobile_money' ? 'border-brand-500 bg-brand-50/50' : 'border-gray-100 hover:bg-gray-50'}`}
|
|
>
|
|
<div className="h-10 w-10 bg-orange-100 rounded-xl flex items-center justify-center text-orange-600 mr-4"><Smartphone /></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, MTN, Wave, Moov</p>
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => setPaymentMethod('card')}
|
|
className={`w-full flex items-center p-4 border-2 rounded-xl transition-all ${paymentMethod === 'card' ? 'border-brand-500 bg-brand-50/50' : 'border-gray-100 hover:bg-gray-50'}`}
|
|
>
|
|
<div className="h-10 w-10 bg-blue-100 rounded-xl flex items-center justify-center text-blue-600 mr-4"><CreditCard /></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>
|
|
</button>
|
|
</div>
|
|
|
|
<button
|
|
disabled={!paymentMethod}
|
|
onClick={handlePayment}
|
|
className="w-full mt-8 bg-brand-600 text-white font-bold py-4 rounded-xl shadow-lg hover:bg-brand-700 disabled:opacity-50 transition-all"
|
|
>
|
|
Payer maintenant
|
|
</button>
|
|
</>
|
|
)}
|
|
|
|
{paymentStep === 'processing' && (
|
|
<div className="py-12 text-center">
|
|
<Loader className="h-14 w-14 text-brand-600 animate-spin mx-auto mb-6" />
|
|
<p className="text-xl font-bold text-gray-900">Sécurisation du paiement...</p>
|
|
<p className="text-gray-500 mt-2">N'interrompez pas la transaction.</p>
|
|
</div>
|
|
)}
|
|
|
|
{paymentStep === 'success' && (
|
|
<div className="py-8 text-center animate-in zoom-in duration-300">
|
|
<div className="mx-auto flex items-center justify-center h-20 w-20 rounded-full bg-green-100 mb-6">
|
|
<ShieldCheck className="h-12 w-12 text-green-600" />
|
|
</div>
|
|
<h3 className="text-2xl font-bold text-gray-900">Paiement Réussi !</h3>
|
|
<p className="text-gray-500 mt-3 mb-8">
|
|
{selectedPlan.type === 'POST_EVENT' || selectedPlan.type === 'POST_NEWS'
|
|
? "Votre paiement a été validé. Vous pouvez maintenant remplir le formulaire de votre publication."
|
|
: `Votre service ${selectedPlan.name} a été activé avec succès.`
|
|
}
|
|
</p>
|
|
{selectedPlan.type === 'POST_EVENT' || selectedPlan.type === 'POST_NEWS' ? (
|
|
<button
|
|
onClick={() => {
|
|
setIsPaymentModalOpen(false);
|
|
if (selectedPlan.type === 'POST_EVENT') setShowEventModal(true);
|
|
if (selectedPlan.type === 'POST_NEWS') setShowNewsModal(true);
|
|
}}
|
|
className="w-full bg-brand-600 text-white font-bold py-4 rounded-xl shadow-lg hover:bg-brand-700 transition-all flex items-center justify-center gap-2"
|
|
>
|
|
{selectedPlan.type === 'POST_EVENT' ? <Calendar className="w-5 h-5" /> : <Newspaper className="w-5 h-5" />}
|
|
Remplir le formulaire
|
|
</button>
|
|
) : (
|
|
<button onClick={closePayment} className="w-full bg-brand-600 text-white font-bold py-4 rounded-xl shadow-lg hover:bg-brand-700 transition-all">Retourner au site</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* EVENT FORM MODAL */}
|
|
<CreateEventModal
|
|
isOpen={showEventModal}
|
|
onClose={() => {
|
|
setShowEventModal(false);
|
|
closePayment();
|
|
}}
|
|
/>
|
|
{/* NEWS FORM MODAL */}
|
|
<CreateNewsModal
|
|
isOpen={showNewsModal}
|
|
onClose={() => {
|
|
setShowNewsModal(false);
|
|
closePayment();
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default OneShotPricingSection;
|