import React, { useState } from 'react';
import { useQuery, useMutation } from '@tanstack/react-query';
import { settingsApi } from '../../api/settingsApi';
import { CreditCard, Crown, Check, AlertCircle, Loader2, Users, Globe, Plug, Rocket } from 'lucide-react';
/* βββ Styling constants (match SettingsPage) ββββββββββββββββββββββββ */
const btnClass =
'rounded-lg bg-brand-600 py-2 px-4 text-sm font-semibold text-white hover:bg-brand-700 disabled:opacity-50';
const btnOutlineClass =
'rounded-lg border border-surface-300 py-2 px-4 text-sm font-medium text-surface-700 hover:bg-surface-50 disabled:opacity-50';
/* βββ Usage bar βββββββββββββββββββββββββββββββββββββββββββββββββββββ */
interface UsageBarProps {
label: string;
icon: React.ElementType;
used: number;
limit: number;
percentage: number;
}
function UsageBar({ label, icon: Icon, used, limit, percentage }: UsageBarProps) {
const clampedPct = Math.min(percentage, 100);
const barColor =
percentage >= 90
? 'bg-red-500'
: percentage >= 70
? 'bg-yellow-500'
: 'bg-brand-500';
return (
<div>
<div className="mb-1.5 flex items-center justify-between">
<div className="flex items-center gap-2 text-sm font-medium text-surface-700">
<Icon className="h-4 w-4 text-surface-400" />
{label}
</div>
<span className="text-xs text-surface-500">
{used}/{limit} used
</span>
</div>
<div className="h-2.5 w-full overflow-hidden rounded-full bg-surface-100">
<div
className={`h-full rounded-full transition-all duration-300 ${barColor}`}
style={{ width: `${clampedPct}%` }}
/>
</div>
</div>
);
}
/* βββ Upgrade modal βββββββββββββββββββββββββββββββββββββββββββββββββ */
interface UpgradeModalProps {
open: boolean;
onClose: () => void;
onUpgrade: () => void;
isLoading: boolean;
error: string | null;
}
function UpgradeModal({ open, onClose, onUpgrade, isLoading, error }: UpgradeModalProps) {
if (!open) return null;
return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"
onClick={onClose}
>
<div
className="w-full max-w-md rounded-xl border border-surface-200 bg-white p-6 shadow-xl"
onClick={(e) => e.stopPropagation()}
>
<h3 className="mb-2 text-lg font-semibold text-surface-900">Upgrade Plan</h3>
<p className="mb-4 text-sm text-surface-600">
Stripe billing integration is currently in progress. Upgrade functionality will be available soon.
</p>
{error && (
<div className="mb-4 flex items-start gap-2 rounded-lg bg-red-50 p-3 text-sm text-red-700">
<AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
{error}
</div>
)}
<div className="flex items-center justify-end gap-3">
<button
type="button"
onClick={onClose}
className={btnOutlineClass}
disabled={isLoading}
>
Close
</button>
<button
type="button"
onClick={onUpgrade}
disabled={isLoading}
className={btnClass}
>
{isLoading ? (
<span className="flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
Processingβ¦
</span>
) : (
'Try Upgrade'
)}
</button>
</div>
</div>
</div>
);
}
/* βββ BillingTab ββββββββββββββββββββββββββββββββββββββββββββββββββββ */
export default function BillingTab() {
const [showUpgrade, setShowUpgrade] = useState(false);
const [upgradeError, setUpgradeError] = useState<string | null>(null);
const { data: subscription, isLoading: subLoading } = useQuery({
queryKey: ['subscription'],
queryFn: settingsApi.getSubscription,
staleTime: 60_000,
refetchOnWindowFocus: false,
});
const { data: usage, isLoading: usageLoading } = useQuery({
queryKey: ['usage'],
queryFn: settingsApi.getUsage,
staleTime: 60_000,
refetchOnWindowFocus: false,
});
const upgradeMutation = useMutation({
mutationFn: () => settingsApi.upgradeSubscription(),
onSuccess: () => {
setShowUpgrade(false);
setUpgradeError(null);
},
onError: (err: unknown) => {
setUpgradeError(
(err as { response?: { status?: number; data?: { error?: string } } })?.response?.data?.error ||
'Upgrade is not yet available β Stripe integration is in progress.'
);
},
});
const handleUpgrade = () => {
setUpgradeError(null);
upgradeMutation.mutate();
};
const plan = subscription?.plan;
const usageData = usage?.usage;
const usageLimits = usage?.limits;
const usagePct = usage?.percentages;
/* βββ Loading state ββββββββββββββββββββββββββββββββββββββββββββ */
if (subLoading || usageLoading) {
return (
<div className="rounded-xl border border-surface-200 bg-white p-6">
<div className="flex items-center gap-3 text-surface-500">
<Loader2 className="h-5 w-5 animate-spin" />
<span className="text-sm">Loading billing informationβ¦</span>
</div>
</div>
);
}
/* βββ Empty state ββββββββββββββββββββββββββββββββββββββββββββββ */
if (!plan) {
return (
<div className="rounded-xl border border-surface-200 bg-white p-6">
<p className="text-sm text-surface-500">No subscription data available.</p>
</div>
);
}
const currencySymbol = plan.currency === 'USD' ? '$' : plan.currency;
const isPro = plan.name.toLowerCase().includes('pro') || plan.name.toLowerCase().includes('enterprise');
return (
<div className="space-y-6">
{/* βββ Current Plan Card βββββββββββββββββββββββββββββββββββ */}
<div className="rounded-xl border border-surface-200 bg-white p-6">
<div className="mb-4 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className={`rounded-lg p-2 ${isPro ? 'bg-brand-50' : 'bg-surface-100'}`}>
<Crown className={`h-5 w-5 ${isPro ? 'text-brand-600' : 'text-surface-500'}`} />
</div>
<div>
<h3 className="text-sm font-semibold text-surface-900">
{plan.name} Plan
</h3>
<p className="text-xs text-surface-500">
{subscription?.status} Β· {subscription?.billing_cycle} billing
</p>
</div>
</div>
<div className="text-right">
<div className="text-2xl font-bold text-surface-900">
{currencySymbol}{plan.price}
</div>
<div className="text-xs text-surface-500">per month</div>
</div>
</div>
{/* Feature list */}
<div className="space-y-2">
{plan.features.map((feature: string) => (
<div key={feature} className="flex items-center gap-2 text-sm text-surface-600">
<Check className="h-4 w-4 shrink-0 text-green-500" />
{feature}
</div>
))}
</div>
<div className="mt-4">
<button
type="button"
onClick={() => {
setUpgradeError(null);
setShowUpgrade(true);
}}
className={btnClass}
>
<span className="flex items-center gap-2">
<Rocket className="h-4 w-4" />
Upgrade Plan
</span>
</button>
</div>
</div>
{/* βββ Usage Section βββββββββββββββββββββββββββββββββββββββ */}
<div className="rounded-xl border border-surface-200 bg-white p-6">
<h3 className="mb-4 flex items-center gap-2 text-sm font-semibold text-surface-700">
<CreditCard className="h-4 w-4 text-surface-400" />
Resource Usage
</h3>
<div className="space-y-5">
{usageData && usageLimits && usagePct && (
<>
<UsageBar
label="Seats"
icon={Users}
used={usageData.seats}
limit={usageLimits.seats}
percentage={usagePct.seats}
/>
<UsageBar
label="Markets"
icon={Globe}
used={usageData.markets}
limit={usageLimits.markets}
percentage={usagePct.markets}
/>
<UsageBar
label="Integrations"
icon={Plug}
used={usageData.integrations}
limit={usageLimits.integrations}
percentage={usagePct.integrations}
/>
</>
)}
</div>
</div>
{/* βββ Upgrade Modal βββββββββββββββββββββββββββββββββββββββ */}
<UpgradeModal
open={showUpgrade}
onClose={() => setShowUpgrade(false)}
onUpgrade={handleUpgrade}
isLoading={upgradeMutation.isPending}
error={upgradeError}
/>
</div>
);
}