mirror of
http://88.130.71.182:3000/BlitTech/deals24togo_fe.git
synced 2026-06-12 23:33:21 +00:00
146 lines
5.7 KiB
TypeScript
146 lines
5.7 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
import { CreditCard, CheckCircle, Clock } from 'lucide-react';
|
|
import { api } from '../services/api';
|
|
import { Subscription } from '../types';
|
|
import { useI18n } from '../contexts/I18nContext';
|
|
import { usePageTitle } from '../hooks/usePageTitle';
|
|
import { useToast } from '../contexts/ToastContext';
|
|
import Button from '../components/common/Button';
|
|
import Card, { CardContent, CardHeader } from '../components/common/Card';
|
|
|
|
interface SubscriptionStatus {
|
|
has_active_subscription: boolean;
|
|
subscription: Subscription | null;
|
|
}
|
|
|
|
const SubscriptionPage: React.FC = () => {
|
|
const { t } = useI18n();
|
|
usePageTitle('Subscription');
|
|
const { showToast } = useToast();
|
|
const [status, setStatus] = useState<SubscriptionStatus | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [initiating, setInitiating] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
api.subscriptions.me()
|
|
.then((data: any) => setStatus(data))
|
|
.catch(() => {})
|
|
.finally(() => setLoading(false));
|
|
}, []);
|
|
|
|
const handleSubscribe = async (plan: 'monthly' | 'yearly') => {
|
|
setInitiating(plan);
|
|
try {
|
|
const result = await api.payments.initiate({ type: 'subscription', plan }) as any;
|
|
window.location.href = result.payment_url;
|
|
} catch (err: any) {
|
|
showToast(err.message || 'Failed to initiate payment', 'error');
|
|
setInitiating(null);
|
|
}
|
|
};
|
|
|
|
const formatter = new Intl.NumberFormat('fr-TG', {
|
|
style: 'currency',
|
|
currency: 'XOF',
|
|
minimumFractionDigits: 0,
|
|
});
|
|
|
|
return (
|
|
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-10">
|
|
<div className="flex items-center gap-3 mb-8">
|
|
<CreditCard className="h-7 w-7 text-accent-600" />
|
|
<h1 className="text-3xl font-bold text-primary-800">{t.subscription.title}</h1>
|
|
</div>
|
|
|
|
{/* Current status */}
|
|
{loading ? (
|
|
<div className="mb-8 p-4 bg-gray-50 border border-gray-200 rounded-lg animate-pulse h-16" />
|
|
) : status?.has_active_subscription && status.subscription ? (
|
|
<div className="mb-8 p-4 bg-success-50 border border-success-200 rounded-lg flex items-start gap-3">
|
|
<CheckCircle className="h-5 w-5 text-success-600 mt-0.5 flex-shrink-0" />
|
|
<div>
|
|
<p className="font-medium text-success-800">
|
|
{t.subscription.activePlan}:{' '}
|
|
{status.subscription.plan === 'monthly' ? t.subscription.monthly : t.subscription.yearly}
|
|
</p>
|
|
<p className="text-sm text-success-700 mt-0.5">
|
|
{t.subscription.activeUntil}{' '}
|
|
{new Date(status.subscription.ends_at).toLocaleDateString('fr-TG')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="mb-8 p-4 bg-warning-50 border border-warning-200 rounded-lg flex items-start gap-3">
|
|
<Clock className="h-5 w-5 text-warning-600 mt-0.5 flex-shrink-0" />
|
|
<div>
|
|
<p className="font-medium text-warning-800">{t.subscription.noSubscription}</p>
|
|
<p className="text-sm text-warning-700 mt-0.5">{t.subscription.noSubscriptionDesc}</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Plan cards */}
|
|
<h2 className="text-xl font-semibold text-primary-800 mb-4">{t.subscription.choosePlan}</h2>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
|
{/* Monthly */}
|
|
<Card className="border-2 border-gray-200 hover:border-accent-400 transition-colors">
|
|
<CardHeader>
|
|
<h3 className="text-lg font-bold text-primary-800">{t.subscription.monthly}</h3>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-3xl font-bold text-accent-700 mb-1">
|
|
{formatter.format(1000)}
|
|
</p>
|
|
<p className="text-sm text-primary-500 mb-4">{t.subscription.perMonth}</p>
|
|
<p className="text-sm text-primary-600 mb-6">{t.subscription.monthlyDesc}</p>
|
|
<Button
|
|
variant="secondary"
|
|
className="w-full"
|
|
disabled={initiating !== null}
|
|
onClick={() => handleSubscribe('monthly')}
|
|
>
|
|
{initiating === 'monthly' ? t.payment.processing : t.subscription.subscribe}
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Yearly */}
|
|
<Card className="border-2 border-accent-400 relative">
|
|
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
|
<span className="bg-accent-500 text-white text-xs font-bold px-3 py-1 rounded-full">
|
|
{t.subscription.saveMonths}
|
|
</span>
|
|
</div>
|
|
<CardHeader>
|
|
<h3 className="text-lg font-bold text-primary-800">{t.subscription.yearly}</h3>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-3xl font-bold text-accent-700 mb-1">
|
|
{formatter.format(10000)}
|
|
</p>
|
|
<p className="text-sm text-primary-500 mb-4">{t.subscription.perYear}</p>
|
|
<p className="text-sm text-primary-600 mb-6">{t.subscription.yearlyDesc}</p>
|
|
<Button
|
|
variant="secondary"
|
|
className="w-full"
|
|
disabled={initiating !== null}
|
|
onClick={() => handleSubscribe('yearly')}
|
|
>
|
|
{initiating === 'yearly' ? t.payment.processing : t.subscription.subscribe}
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<p className="mt-6 text-center text-sm text-primary-500">
|
|
<Link to="/agency/dashboard" className="text-accent-600 hover:underline">
|
|
← Back to Dashboard
|
|
</Link>
|
|
</p>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SubscriptionPage;
|