'use client'; import { useEffect, useState } from 'react'; import AdminLayout from '../../components/AdminLayout'; import { getSubscriptions } from '../../lib/api'; export default function SubscriptionsPage() { const [subscriptions, setSubscriptions] = useState([]); const [filter, setFilter] = useState('all'); const [loading, setLoading] = useState(true); useEffect(() => { loadSubscriptions(); }, []); const loadSubscriptions = async () => { try { const data = await getSubscriptions(); setSubscriptions(data.subscriptions || []); } catch (err) { console.error('Failed to load subscriptions:', err); } finally { setLoading(false); } }; const filteredSubscriptions = subscriptions.filter((sub) => { if (filter === 'all') return true; if (filter === 'active') return sub.status === 'active'; if (filter === 'premium') return sub.plan === 'premium' && sub.status === 'active'; if (filter === 'canceled') return sub.status === 'canceled'; return true; }); const stats = { total: subscriptions.length, active: subscriptions.filter(s => s.status === 'active').length, premium: subscriptions.filter(s => s.plan === 'premium' && s.status === 'active').length, free: subscriptions.filter(s => s.plan === 'free').length, canceled: subscriptions.filter(s => s.status === 'canceled').length, }; const mrr = stats.premium * 9.99; return (

Subscriptions

{['all', 'active', 'premium', 'canceled'].map((f) => ( ))}
{loading ? (

Loading...

) : (
User Plan Status Created Expires Stripe ID
{filteredSubscriptions.length === 0 ? (
No subscriptions found
) : ( filteredSubscriptions.map((sub) => (
{sub.user?.email || '—'} {sub.created_at ? new Date(sub.created_at).toLocaleDateString() : '—'} {sub.expires_at ? new Date(sub.expires_at).toLocaleDateString() : '—'} {sub.stripe_subscription_id || '—'}
)) )}
)}
); } function StatCard({ label, value, color }) { return (
{label}
{value}
); } function PlanBadge({ plan }) { const isPremium = plan === 'premium'; return ( {plan || 'free'} ); } function StatusBadge({ status }) { const colors = { active: { bg: '#D1FAE5', color: '#065F46' }, canceled: { bg: '#FEE2E2', color: '#991B1B' }, expired: { bg: '#FEE2E2', color: '#991B1B' }, trialing: { bg: '#DBEAFE', color: '#1E40AF' }, }; const style = colors[status] || colors.active; return ( {status || 'unknown'} ); } const styles = { header: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '24px', }, title: { fontSize: '24px', fontWeight: '600', }, filters: { display: 'flex', gap: '8px', }, filterBtn: { padding: '8px 16px', fontSize: '13px', fontWeight: '500', border: '1px solid var(--border)', borderRadius: '6px', background: 'white', color: 'var(--text-secondary)', cursor: 'pointer', }, filterBtnActive: { background: 'var(--primary)', color: 'white', borderColor: 'var(--primary)', }, statsGrid: { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '16px', marginBottom: '24px', }, statCard: { background: 'white', borderRadius: '12px', padding: '20px', textAlign: 'center', }, statLabel: { fontSize: '13px', color: 'var(--text-muted)', marginBottom: '4px', }, statValue: { fontSize: '28px', fontWeight: '700', }, loading: { color: 'var(--text-muted)', }, table: { background: 'white', borderRadius: '12px', overflow: 'hidden', }, tableHeader: { display: 'grid', gridTemplateColumns: '1.5fr 0.8fr 0.8fr 1fr 1fr 1.5fr', gap: '16px', padding: '16px 20px', background: 'var(--surface)', fontSize: '12px', fontWeight: '600', color: 'var(--text-muted)', textTransform: 'uppercase', }, tableRow: { display: 'grid', gridTemplateColumns: '1.5fr 0.8fr 0.8fr 1fr 1fr 1.5fr', gap: '16px', padding: '16px 20px', borderBottom: '1px solid var(--border)', alignItems: 'center', fontSize: '14px', }, email: { color: 'var(--text-secondary)', }, date: { color: 'var(--text-muted)', fontSize: '13px', }, stripeId: { fontFamily: 'monospace', fontSize: '11px', color: 'var(--text-muted)', }, empty: { padding: '48px', textAlign: 'center', color: 'var(--text-muted)', }, };