import { mdiArrowUpBoldCircleOutline, mdiCheckCircleOutline, mdiCreditCardOutline, mdiRefresh, } from '@mdi/js' import axios from 'axios' import Head from 'next/head' import React, { ReactElement, useEffect, useState } from 'react' import BaseButton from '../components/BaseButton' import CardBox from '../components/CardBox' import SectionMain from '../components/SectionMain' import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton' import LayoutAuthenticated from '../layouts/Authenticated' import { getPageTitle } from '../config' import { SubscriptionPlan } from '../subscriptionPlans' type SubscriptionStatusResponse = { subscription: { planId: string planName: string status: string effectiveStatus: string isActive: boolean trialEndsAt?: string | null trialDaysLeft?: number | null priceMonthly: number currency: string } usage: { monthlyReviewRequests: number businesses: number teamMembers: number paymentConnectors: number periodStart?: string periodEnd?: string } limits: SubscriptionPlan['limits'] plans: SubscriptionPlan[] } const usageLabels: Array<{ key: keyof SubscriptionStatusResponse['usage'] limitKey: keyof SubscriptionPlan['limits'] label: string }> = [ { key: 'monthlyReviewRequests', limitKey: 'monthlyReviewRequests', label: 'Review requests this month' }, { key: 'businesses', limitKey: 'businesses', label: 'Businesses / locations' }, { key: 'teamMembers', limitKey: 'teamMembers', label: 'Team members' }, { key: 'paymentConnectors', limitKey: 'paymentConnectors', label: 'Connected payment providers' }, ] function formatDate(value?: string | null) { if (!value) return 'Not set' return new Intl.DateTimeFormat('en', { month: 'short', day: 'numeric', year: 'numeric', }).format(new Date(value)) } function formatLimit(value: number) { return value.toLocaleString() } export default function SubscriptionPage() { const [status, setStatus] = useState(null) const [isLoading, setIsLoading] = useState(true) const [selectingPlanId, setSelectingPlanId] = useState('') const [message, setMessage] = useState('') const [error, setError] = useState('') const loadStatus = async () => { setIsLoading(true) try { const response = await axios.get('/subscription/me') setStatus(response.data) setError('') } catch (requestError) { console.error('Failed to load subscription status:', requestError) setError('Could not load your subscription status. Please refresh and try again.') } finally { setIsLoading(false) } } useEffect(() => { loadStatus() }, []) const selectPlan = async (planId: string) => { setSelectingPlanId(planId) setError('') setMessage('') try { const response = await axios.post('/subscription/select-plan', { planId }) setStatus(response.data) setMessage(`Your trial plan is now ${response.data.subscription.planName}.`) } catch (requestError) { console.error('Failed to select subscription plan:', requestError) if (axios.isAxiosError(requestError) && requestError.response?.data) { setError(String(requestError.response.data)) } else { setError('Could not update your plan. Please try again.') } } finally { setSelectingPlanId('') } } const currentPlanId = status?.subscription.planId return ( <> {getPageTitle('Subscription')} {message && (
{message}
)} {error && (
{error}
)} {isLoading && !status ? ( Loading subscription details... ) : status ? ( <>

Current plan

{status.subscription.planName}

Status: {status.subscription.effectiveStatus}. Trial ends {formatDate(status.subscription.trialEndsAt)} {status.subscription.trialDaysLeft !== null && status.subscription.trialDaysLeft !== undefined ? ` (${status.subscription.trialDaysLeft} days left)` : ''} .

Monthly price

${status.subscription.priceMonthly}

per month after trial

{usageLabels.map((item) => { const used = Number(status.usage[item.key]) || 0 const limit = Number(status.limits[item.limitKey]) || 1 const percent = Math.min(100, Math.round((used / limit) * 100)) const isNearLimit = percent >= 80 return (

{item.label}

{formatLimit(used)} / {formatLimit(limit)}

) })}
{status.plans.map((plan) => { const isCurrent = currentPlanId === plan.id const isPro = plan.id === 'pro' return ( {isPro && (
Pro growth tools
)}

Review Flow

{plan.name}

{plan.tagline}

${plan.priceMonthly} /month

{formatLimit(plan.limits.monthlyReviewRequests)}

requests/month

{formatLimit(plan.limits.businesses)}

businesses

{formatLimit(plan.limits.teamMembers)}

team members

{formatLimit(plan.limits.paymentConnectors)}

connectors

selectPlan(plan.id)} />

Included

{plan.features.map((feature) => (
{feature}
))}
) })}
) : null} ) } SubscriptionPage.getLayout = function getLayout(page: ReactElement) { return {page} }