You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

266 lines
9.8 KiB

'use client'
import { Calendar, Check, Info } from 'lucide-react'
import { Badge } from '@/components/ui/badge'
import { Spinner } from '@/components/ui/spinner'
import { useQuery } from '@tanstack/react-query'
import httpBrowserClient from '@/lib/httpBrowserClient'
import { ApiEndpoints } from '@/config/api'
import Link from 'next/link'
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip'
export default function SubscriptionInfo() {
const {
data: currentSubscription,
isLoading: isLoadingSubscription,
error: subscriptionError,
} = useQuery({
queryKey: ['currentSubscription'],
queryFn: () =>
httpBrowserClient
.get(ApiEndpoints.billing.currentSubscription())
.then((res) => res.data),
})
// Format price with currency symbol
const formatPrice = (
amount: number | null | undefined,
currency: string | null | undefined
) => {
if (amount == null || currency == null) return 'Free'
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency.toUpperCase() || 'USD',
minimumFractionDigits: 2,
})
return formatter.format(amount / 100)
}
const getBillingInterval = (interval: string | null | undefined) => {
if (!interval) return ''
return interval.toLowerCase() === 'month' ? 'monthly' : 'yearly'
}
if (isLoadingSubscription)
return (
<div className='flex justify-center items-center h-full min-h-[200px]'>
<Spinner size='sm' />
</div>
)
if (subscriptionError)
return (
<p className='text-sm text-destructive'>
Failed to load subscription information
</p>
)
return (
<div className='bg-gradient-to-br from-white to-gray-50 dark:from-gray-800 dark:to-gray-900 border rounded-lg shadow p-5'>
<div className='flex items-center justify-between mb-5'>
<div>
<h3 className='text-xl font-bold text-gray-900 dark:text-white'>
{currentSubscription?.plan?.name || 'Free Plan'}
</h3>
<div className='flex items-center gap-2'>
<p className='text-sm text-gray-500 dark:text-gray-400'>
Current subscription
</p>
{currentSubscription?.amount > 0 && (
<Badge variant='outline' className='text-xs font-medium'>
{formatPrice(
currentSubscription?.amount,
currentSubscription?.currency
)}
{currentSubscription?.recurringInterval && (
<span className='ml-1'>
/{' '}
{getBillingInterval(currentSubscription?.recurringInterval)}
</span>
)}
</Badge>
)}
</div>
</div>
<div
className={`flex items-center px-2 py-0.5 rounded-full ${
currentSubscription?.status === 'active'
? 'bg-green-50 dark:bg-green-900/30'
: currentSubscription?.status === 'past_due'
? 'bg-amber-50 dark:bg-amber-900/30'
: 'bg-gray-50 dark:bg-gray-800/50'
}`}
>
<Check
className={`h-3 w-3 mr-1 ${
currentSubscription?.status === 'active'
? 'text-green-600 dark:text-green-400'
: currentSubscription?.status === 'past_due'
? 'text-amber-600 dark:text-amber-400'
: 'text-gray-600 dark:text-gray-400'
}`}
/>
<span
className={`text-xs font-medium ${
currentSubscription?.status === 'active'
? 'text-green-600 dark:text-green-400'
: currentSubscription?.status === 'past_due'
? 'text-amber-600 dark:text-amber-400'
: 'text-gray-600 dark:text-gray-400'
}`}
>
{currentSubscription?.status
? currentSubscription.status
.split('_')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
: 'Active'}
</span>
</div>
</div>
<div className='grid grid-cols-2 gap-3 mb-5'>
<div className='flex items-center space-x-2 bg-white dark:bg-gray-800 p-3 rounded-md shadow-sm'>
<Calendar className='h-4 w-4 text-brand-600 dark:text-brand-400' />
<div>
<p className='text-xs text-gray-500 dark:text-gray-400'>
Start Date
</p>
<p className='text-sm font-medium text-gray-900 dark:text-white'>
{currentSubscription?.subscriptionStartDate
? new Date(
currentSubscription?.subscriptionStartDate
).toLocaleDateString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
})
: 'N/A'}
</p>
</div>
</div>
<div className='flex items-center space-x-2 bg-white dark:bg-gray-800 p-3 rounded-md shadow-sm'>
<Calendar className='h-4 w-4 text-brand-600 dark:text-brand-400' />
<div>
<p className='text-xs text-gray-500 dark:text-gray-400'>
Next Payment
</p>
<p className='text-sm font-medium text-gray-900 dark:text-white'>
{currentSubscription?.currentPeriodEnd
? new Date(
currentSubscription?.currentPeriodEnd
).toLocaleDateString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
})
: 'N/A'}
</p>
</div>
</div>
</div>
<div className='bg-white dark:bg-gray-800 p-4 rounded-md shadow-sm mb-5'>
<p className='text-xs text-gray-500 dark:text-gray-400 mb-3 font-medium'>
Usage Limits
</p>
<div className='grid grid-cols-3 gap-3'>
<div className='bg-gray-50 dark:bg-gray-700/50 p-2 rounded-md'>
<p className='text-xs text-gray-500 dark:text-gray-400'>Daily</p>
<p className='text-sm font-medium text-gray-900 dark:text-white'>
{currentSubscription?.plan?.dailyLimit === -1
? 'Unlimited'
: currentSubscription?.plan?.dailyLimit || '0'}
{currentSubscription?.plan?.dailyLimit === -1 && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span className='inline-flex items-center'>
<Info className='h-4 w-4 text-gray-500 ml-1 cursor-pointer' />
</span>
</TooltipTrigger>
<TooltipContent>
<p>Unlimited (within monthly limit)</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</p>
</div>
<div className='bg-gray-50 dark:bg-gray-700/50 p-2 rounded-md'>
<p className='text-xs text-gray-500 dark:text-gray-400'>Monthly</p>
<p className='text-sm font-medium text-gray-900 dark:text-white'>
{currentSubscription?.plan?.monthlyLimit === -1
? 'Unlimited'
: currentSubscription?.plan?.monthlyLimit?.toLocaleString() ||
'0'}
{currentSubscription?.plan?.monthlyLimit === -1 && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span className='inline-flex items-center'>
<Info className='h-4 w-4 text-gray-500 ml-1 cursor-pointer' />
</span>
</TooltipTrigger>
<TooltipContent>
<p>Unlimited (within fair usage)</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</p>
</div>
<div className='bg-gray-50 dark:bg-gray-700/50 p-2 rounded-md'>
<p className='text-xs text-gray-500 dark:text-gray-400'>Bulk</p>
<p className='text-sm font-medium text-gray-900 dark:text-white'>
{currentSubscription?.plan?.bulkSendLimit === -1
? 'Unlimited'
: currentSubscription?.plan?.bulkSendLimit || '0'}
{currentSubscription?.plan?.bulkSendLimit === -1 && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span className='inline-flex items-center'>
<Info className='h-4 w-4 text-gray-500 ml-1 cursor-pointer' />
</span>
</TooltipTrigger>
<TooltipContent>
<p>Unlimited (within monthly limit)</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</p>
</div>
</div>
</div>
<div className='flex justify-end'>
{!currentSubscription?.plan?.name ||
currentSubscription?.plan?.name?.toLowerCase() === 'free' ? (
<Link
href='/checkout/pro'
className='text-sm font-medium text-white bg-brand-600 hover:bg-brand-700 px-4 py-2 rounded-md transition-colors'
>
Upgrade to Pro
</Link>
) : (
<Link
href='https://polar.sh/textbee/portal/'
className='text-sm font-medium text-white bg-brand-600 hover:bg-brand-700 px-4 py-2 rounded-md transition-colors'
>
Manage Subscription
</Link>
)}
</div>
</div>
)
}