"use client"
import { dmSans125ClassName } from "@/lib/fonts"
import { cn } from "@lib/utils"
import { PLAN_DISPLAY_NAMES, useTokenUsage } from "@/hooks/use-token-usage"
import {
Dialog,
DialogContent,
DialogTrigger,
DialogClose,
} from "@ui/components/dialog"
import { useCustomer } from "autumn-js/react"
import { Check, X, LoaderIcon, Settings } from "lucide-react"
import { useState } from "react"
import { toast } from "sonner"
function SectionTitle({ children }: { children: React.ReactNode }) {
return (
{children}
)
}
function SettingsCard({ children }: { children: React.ReactNode }) {
return (
{children}
)
}
function PlanComparisonCard({
name,
price,
period,
description,
credits,
features,
highlight,
}: {
name: string
price: string
period: string
description: string
credits: string
features: string[]
highlight: boolean
}) {
return (
{name}
{highlight && (
RECOMMENDED
)}
{price}
{period && (
{period}
)}
{description}
{credits}
of usage included
{features.map((text) => (
-
{text}
))}
)
}
export default function Billing() {
const autumn = useCustomer()
const [isUpgrading, setIsUpgrading] = useState(false)
const [isCancelling, setIsCancelling] = useState(false)
const [isCancelDialogOpen, setIsCancelDialogOpen] = useState(false)
const {
usdIncluded,
usdSpent,
planUsagePct,
currentPlan,
hasPaidPlan,
isLoading: isCheckingStatus,
daysRemaining,
} = useTokenUsage(autumn)
const formatUsd = (n: number) =>
n.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
const planDisplayNames = PLAN_DISPLAY_NAMES
const handleUpgrade = async () => {
setIsUpgrading(true)
try {
const result = await autumn.attach({
planId: "api_pro",
successUrl: `${window.location.origin}/settings#billing`,
})
if (result?.paymentUrl) {
window.open(result.paymentUrl, "_self")
return
}
autumn.refetch?.()
} catch (error) {
console.error(error)
toast.error("Failed to start checkout. Please try again.")
} finally {
setIsUpgrading(false)
}
}
const cancellablePlanId =
currentPlan === "pro" || currentPlan === "scale"
? (`api_${currentPlan}` as const)
: null
const handleCancelSubscription = async () => {
if (!cancellablePlanId) return
setIsCancelling(true)
try {
await autumn.updateSubscription({
planId: cancellablePlanId,
cancelAction: "cancel_end_of_cycle",
})
autumn.refetch?.()
setIsCancelDialogOpen(false)
toast.success(
`Subscription cancelled. ${planDisplayNames[currentPlan]} features remain active until the end of your billing period.`,
)
} catch (error) {
console.error(error)
toast.error("Failed to cancel subscription. Please try again.")
} finally {
setIsCancelling(false)
}
}
return (
Billing & Subscription
{hasPaidPlan ? (
<>
{planDisplayNames[currentPlan]} plan
ACTIVE
Expanded memory with connections and more
Plan usage
{planUsagePct < 1 && planUsagePct > 0
? "< 1"
: Math.round(planUsagePct)}
% used
80
? "#ef4444"
: "linear-gradient(to right, #4BA0FA 80%, #002757 100%)",
}}
title={`$${formatUsd(usdSpent)} of $${formatUsd(usdIncluded)} used`}
/>
{daysRemaining !== null
? `Resets in ${daysRemaining} day${daysRemaining !== 1 ? "s" : ""}`
: ""}
{cancellablePlanId && (
)}
>
) : (
<>
Free Plan
You are on basic plan
Plan usage
{planUsagePct < 1 && planUsagePct > 0
? "< 1"
: Math.round(planUsagePct)}
% used
80 ? "#ef4444" : "#0054AD",
}}
title={`$${formatUsd(usdSpent)} of $${formatUsd(usdIncluded)} used`}
/>
{daysRemaining !== null
? `Resets in ${daysRemaining} day${daysRemaining !== 1 ? "s" : ""}`
: ""}
>
)}
)
}