"use client";
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import type { SubscriptionGateState } from "@/lib/polar/current-subscription";
import { PlansModal } from "@/components/billing/plans-modal";
import { getPlanById, getPlanByProductId, type PolarPlan } from "@/lib/polar/plans";
import { useSubscriptionQuery } from "@/lib/hooks/queries/use-subscription-query";
import { useProductsQuery } from "@/lib/hooks/queries/use-products-query";
import { Skeleton } from "@/components/ui/skeleton";
import { usePermissions } from "@/lib/hooks/use-permissions";
import { PERMISSIONS } from "@/lib/auth/permissions";
interface SubscriptionPaywallProps {
// No props required - component fetches its own data
}
export function SubscriptionPaywall({}: SubscriptionPaywallProps = {}) {
const router = useRouter();
const {
hasPermission,
isInitialized: permissionsReady,
} = usePermissions();
const hasSubscriptionPermission = hasPermission(PERMISSIONS.ORG_MANAGE);
const shouldLoadSubscription = permissionsReady && hasSubscriptionPermission;
const [isPlansOpen, setPlansOpen] = useState(false);
const {
data: state,
isLoading,
error: subscriptionError,
} = useSubscriptionQuery({
enabled: shouldLoadSubscription,
});
const { data: products, error: productsError } = useProductsQuery({
enabled: shouldLoadSubscription,
});
// Redirect to dashboard if subscription becomes active
useEffect(() => {
if (state?.isActive) {
router.replace("/dashboard");
}
}, [state?.isActive, router]);
if (permissionsReady && !hasSubscriptionPermission) {
return (
Subscription access restricted
You don't have permission to manage subscription or billing settings. Contact your workspace administrator if you believe this is an error.
);
}
const loadError = subscriptionError ?? productsError;
if (shouldLoadSubscription && loadError) {
return (
We couldn't load your subscription
{loadError.message || "Please refresh the page or reach out to support."}
);
}
if (!shouldLoadSubscription || isLoading || !state) {
return (
);
}
// If subscription is active but haven't redirected yet, show loading
if (state.isActive) {
return (
Redirecting to dashboard...
);
}
const usage = state.usage;
const included = usage?.included ?? 1000; // Default fallback
const used = usage?.used ?? 0;
const remaining = usage?.remaining ?? included;
const plan = resolvePlanFromState(state, products ?? []);
const planPrice = plan?.price ?? null;
const formattedPrice = planPrice != null ? formatUsd(planPrice) : null;
const interval = plan?.interval === "month" ? "per month" : "per billing period";
const contactHref =
process.env.NEXT_PUBLIC_CONTACT_EMAIL ||
process.env.NOTIFICATION_EMAIL ||
"mailto:info@yourdomain.com";
console.info("[Polar] Rendering SubscriptionPaywall", {
isActive: state.isActive,
reason: state.reason,
status: state.status,
included,
used,
remaining,
plan: plan?.id ?? "unknown",
planPrice,
});
const featureList = buildFeatureList({
includedInvoices: included,
plan,
});
return (
{plan?.name ?? "Pro Plan"}
Unlock the full AP automation experience
Process invoices faster, eliminate duplicates, and stay ahead of the month-end crunch.
Subscribe now to regain access to the dashboard.
{featureList.map((feature) => (
))}
Talk to sales
);
}
interface FeatureProps {
title: string;
description: string;
}
function Feature({ title, description }: FeatureProps) {
return (
);
}
interface UsageItemProps {
label: string;
value: number;
total: number;
}
function UsageItem({ label, value, total }: UsageItemProps) {
const percentage = Math.min(Math.round((value / Math.max(total, 1)) * 100), 100);
return (
{label}
{value.toLocaleString()}
of {total.toLocaleString()}
);
}
function resolvePlanFromState(state: SubscriptionGateState, products: PolarPlan[]): PolarPlan | null {
if (state.planId) {
const byId = getPlanById(products, state.planId);
if (byId) {
return byId;
}
}
if (state.subscription?.productId) {
const bySubscriptionProduct = getPlanByProductId(products, state.subscription.productId);
if (bySubscriptionProduct) {
return bySubscriptionProduct;
}
}
if (state.productId) {
const byProduct = getPlanByProductId(products, state.productId);
if (byProduct) {
return byProduct;
}
}
return null;
}
function buildFeatureList({
includedInvoices,
plan,
}: {
includedInvoices: number;
plan: PolarPlan | null;
}) {
const features: Array<{ title: string; description: string }> = [
{
title: `${includedInvoices.toLocaleString()} invoices / month included`,
description: "Metered usage with overage protection. Track consumption in real time.",
},
];
if (plan?.price != null) {
features.push({
title: `${formatUsd(plan.price)} flat subscription`,
description: "Predictable billing aligned with your finance team’s needs.",
});
}
if (plan?.benefits?.length) {
for (const benefit of plan.benefits) {
features.push({
title: benefit,
description: "Included with your current plan.",
});
}
} else {
features.push(
{
title: "Approvals & anomaly detection",
description: "Keep approvers accountable and surface risk before it hits ERP.",
},
{
title: "Export-ready payments",
description: "Generate payment files that drop straight into NetSuite and SAP.",
}
);
}
return features;
}
function formatUsd(amount: number) {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: amount % 1 === 0 ? 0 : 2,
}).format(amount);
}