"use client"; import { LocaleLink } from "@i18n/routing"; import { type Config, config } from "@repo/config"; import { usePlanData } from "@saas/payments/hooks/plan-data"; import { useCreateCheckoutLinkMutation } from "@saas/payments/lib/api"; import type { PlanId } from "@saas/payments/types"; import { useLocaleCurrency } from "@shared/hooks/locale-currency"; import { useRouter } from "@shared/hooks/router"; import { Button } from "@ui/components/button"; import { Tabs, TabsList, TabsTrigger } from "@ui/components/tabs"; import { cn } from "@ui/lib"; import { ArrowRightIcon, BadgePercentIcon, CheckIcon, PhoneIcon, StarIcon, } from "lucide-react"; import { useTranslations } from "next-intl"; import { useState } from "react"; const plans = config.payments.plans as Config["payments"]["plans"]; export function PricingTable({ className, userId, organizationId, activePlanId, }: { className?: string; userId?: string; organizationId?: string; activePlanId?: string; }) { const t = useTranslations(); const router = useRouter(); const localeCurrency = useLocaleCurrency(); const [loading, setLoading] = useState(false); const [interval, setInterval] = useState<"month" | "year">("month"); const { planData } = usePlanData(); const createCheckoutLinkMutation = useCreateCheckoutLinkMutation(); const onSelectPlan = async (planId: PlanId, productId?: string) => { if (!(userId || organizationId)) { router.push("/auth/signup"); return; } const plan = plans[planId]; const price = plan.prices?.find( (price) => price.productId === productId, ); if (!price) { return; } setLoading(planId); try { const { checkoutLink } = await createCheckoutLinkMutation.mutateAsync({ type: price.type === "one-time" ? "one-time" : "subscription", productId: price.productId, organizationId, redirectUrl: window.location.href, }); window.location.href = checkoutLink; } catch (error) { console.error(error); } finally { setLoading(false); } }; const filteredPlans = Object.entries(plans).filter( ([planId]) => planId !== activePlanId && (!activePlanId || planId !== "free"), ); const hasSubscriptions = filteredPlans.some(([_, plan]) => plan.prices?.some((price) => price.type === "recurring"), ); return (
{hasSubscriptions && (
setInterval(value as typeof interval) } data-test="price-table-interval-tabs" > {t("pricing.monthly")} {t("pricing.yearly")}
)}
= 2, "@3xl:grid-cols-3": filteredPlans.length >= 3, "@4xl:grid-cols-4": filteredPlans.length >= 4, })} > {filteredPlans .filter(([planId]) => planId !== activePlanId) .map(([planId, plan]) => { const { isFree, isEnterprise, prices, recommended } = plan; const { title, description, features } = planData[planId as keyof typeof planData]; let price = prices?.find( (price) => !price.hidden && (price.type === "one-time" || price.interval === interval) && price.currency === localeCurrency, ); if (isFree) { price = { amount: 0, currency: localeCurrency, interval, productId: "", type: "recurring", }; } if (!(price || isEnterprise)) { return null; } return (
{recommended && (
{t("pricing.recommended")}
)}

{title}

{description && (
{description}
)} {!!features?.length && (
    {features.map( (feature, key) => (
  • {feature}
  • ), )}
)} {price && "trialPeriodDays" in price && price.trialPeriodDays && (
{t("pricing.trialPeriod", { days: price.trialPeriodDays, })}
)}
{price && ( {Intl.NumberFormat("en-US", { style: "currency", currency: price.currency, minimumFractionDigits: 0, }).format(price.amount)} {"interval" in price && ( {" / "} {interval === "month" ? t( "pricing.month", { count: price.intervalCount ?? 1, }, ) : t( "pricing.year", { count: price.intervalCount ?? 1, }, )} )} {organizationId && "seatBased" in price && price.seatBased && ( {" / "} {t( "pricing.perSeat", )} )} )} {isEnterprise ? ( ) : ( )}
); })}
); }