"use client"; import { useEffect, useMemo, useState } from "react"; import type { SubscriptionGateState } from "@/lib/polar/current-subscription"; import { type PolarPlan } from "@/lib/polar/plans"; import { useProductsQuery } from "@/lib/hooks/queries/use-products-query"; interface PlansModalProps { open: boolean; onOpenChange: (open: boolean) => void; subscriptionState?: SubscriptionGateState | null; onPlanChangePending?: (pending: boolean) => void; } export function PlansModal({ open, onOpenChange, subscriptionState, onPlanChangePending, }: PlansModalProps) { const [selectedPlanId, setSelectedPlanId] = useState(null); const { data: products, isLoading, error } = useProductsQuery(); // Define current plan data BEFORE useEffects that depend on it const currentProductId = subscriptionState?.subscription?.productId ?? null; useEffect(() => { if (!open) { setSelectedPlanId(null); document.body.style.removeProperty("overflow"); return; } document.body.style.setProperty("overflow", "hidden"); }, [open]); useEffect(() => { return () => { onPlanChangePending?.(false); }; }, [onPlanChangePending]); useEffect(() => { return () => { document.body.style.removeProperty("overflow"); }; }, []); const plans = useMemo(() => { if (!products) return []; return products.map((plan) => { const isCurrent = Boolean(subscriptionState?.isActive) && currentProductId === plan.productId; return { ...plan, isCurrent, }; }); }, [currentProductId, subscriptionState?.isActive, products]); if (!open) { return null; } const handleSelectPlan = (plan: PolarPlan) => { if (subscriptionState?.isActive) { window.alert("Please cancel your current subscription before selecting a new plan."); return; } onPlanChangePending?.(true); setSelectedPlanId(plan.id); const url = `/api/billing/checkout?plan=${encodeURIComponent(plan.id)}`; setTimeout(() => { window.location.href = url; }, 150); }; return (

Choose your plan

Scale approvals without hitting limits

Plans are billed monthly through Polar. Seats and invoice quotas update immediately after checkout completes.

{isLoading && (

Loading plans...

)} {error && (

Failed to load plans. Please try again.

)} {!isLoading && !error && plans.length === 0 && (

No plans available.

)} {!isLoading && !error && plans.length > 0 && (
{plans.map((plan) => ( handleSelectPlan(plan)} /> ))}
)} {selectedPlanId && (
Redirecting to secure Polar checkout…
)}
); } interface PlanCardProps { plan: PolarPlan & { isCurrent: boolean }; disabled: boolean; isSelected: boolean; isCurrent: boolean; onSelect: () => void; } function PlanCard({ plan, disabled, isSelected, isCurrent, onSelect }: PlanCardProps) { const actionLabel = isCurrent ? "Current plan" : plan.price === 0 ? "Select plan" : `Select ${plan.name}`; return (
{isCurrent ? ( Current Plan ) : plan.badge && ( {plan.badge} )}

{plan.name}

{plan.description}

{formatCurrency(plan.price)} /month
    {plan.includedSeats !== null && (
  • {plan.includedSeats} seats included
  • )} {plan.includedInvoices !== null && (
  • {plan.includedInvoices.toLocaleString()} invoices per month
  • )} {plan.benefits.map((benefit) => (
  • {benefit}
  • ))}
); } function formatCurrency(amount: number) { return new Intl.NumberFormat(undefined, { style: "currency", currency: "USD", }).format(amount); }