'use client' import { useState } from 'react' import Link from 'next/link' import { Button, toast, Spinner } from '@ciphera-net/ui' import { CreditCard, ArrowSquareOut } from '@phosphor-icons/react' import { useSubscription } from '@/lib/swr/dashboard' import { createPortalSession, cancelSubscription, resumeSubscription } from '@/lib/api/billing' import { formatDateLong } from '@/lib/utils/formatDate' import { getAuthErrorMessage } from '@ciphera-net/ui' export default function WorkspaceBillingTab() { const { data: subscription, isLoading, mutate } = useSubscription() const [cancelling, setCancelling] = useState(false) const handleManageBilling = async () => { try { const { url } = await createPortalSession() if (url) window.open(url, '_blank') } catch (err) { toast.error(getAuthErrorMessage(err as Error) || 'Failed to open billing portal') } } const handleCancel = async () => { if (!confirm('Are you sure you want to cancel your subscription?')) return setCancelling(true) try { await cancelSubscription() await mutate() toast.success('Subscription cancelled') } catch (err) { toast.error(getAuthErrorMessage(err as Error) || 'Failed to cancel subscription') } finally { setCancelling(false) } } const handleResume = async () => { try { await resumeSubscription() await mutate() toast.success('Subscription resumed') } catch (err) { toast.error(getAuthErrorMessage(err as Error) || 'Failed to resume subscription') } } if (isLoading) { return (
) } if (!subscription) { return (

No subscription

You're on the free plan.

) } const planLabel = (() => { const raw = subscription.plan_id?.startsWith('price_') ? 'Pro' : subscription.plan_id === 'free' || !subscription.plan_id ? 'Free' : subscription.plan_id return raw === 'Free' || raw === 'Pro' ? raw : raw.charAt(0).toUpperCase() + raw.slice(1) })() const isActive = subscription.subscription_status === 'active' || subscription.subscription_status === 'trialing' return (

Billing & Subscription

Manage your plan, usage, and payment details.

{/* Plan card */}

{planLabel} Plan

{isActive && ( {subscription.subscription_status === 'trialing' ? 'Trial' : 'Active'} )} {subscription.cancel_at_period_end && ( Cancelling )}
{/* Usage stats */}
{typeof subscription.sites_count === 'number' && (

Sites

{subscription.sites_count}

)} {subscription.pageview_limit > 0 && typeof subscription.pageview_usage === 'number' && (

Pageviews

{subscription.pageview_usage.toLocaleString()} / {subscription.pageview_limit.toLocaleString()}

)} {subscription.current_period_end && (

{subscription.cancel_at_period_end ? 'Ends' : 'Renews'}

{formatDateLong(new Date(subscription.current_period_end))}

)} {subscription.pageview_limit > 0 && (

Limit

{subscription.pageview_limit.toLocaleString()} / mo

)}
{/* Actions */}
{subscription.has_payment_method && ( )} {isActive && !subscription.cancel_at_period_end && ( )} {subscription.cancel_at_period_end && ( )}
) }