diff --git a/apps/webapp/app/components/billing/PricingTiers.tsx b/apps/webapp/app/components/billing/PricingTiers.tsx index 508198e03..d13cf6351 100644 --- a/apps/webapp/app/components/billing/PricingTiers.tsx +++ b/apps/webapp/app/components/billing/PricingTiers.tsx @@ -56,74 +56,119 @@ export function PricingTiers({ className )} > - + ); } -export function TierFree({ plan }: { plan: Plan }) { +export function TierFree({ + plan, + organizationSlug, + showActionText, +}: { + plan: Plan; + organizationSlug: string; + showActionText: boolean; +}) { + const lastSubmission = useActionData(); + const [form] = useForm({ + id: "subscribe", + // TODO: type this + lastSubmission: lastSubmission as any, + onValidate({ formData }) { + return parse(formData, { schema: SetPlanBodySchema }); + }, + }); + + const navigation = useNavigation(); + const isLoading = navigation.state === "submitting" || navigation.state === "loading"; + + const currentPlan = useCurrentPlan(); + const isCurrentPlan = + currentPlan?.subscription?.isPaying === undefined || + currentPlan?.subscription?.isPaying === false; + + let actionText = "Select plan"; + + if (showActionText) { + if (isCurrentPlan) { + actionText = "Current Plan"; + } else { + actionText = "Downgrade"; + } + } + return ( -
- - Up to {plan.concurrentRuns?.freeAllowance}{" "} - +
+ + Up to {plan.concurrentRuns?.freeAllowance}{" "} + + {pricingDefinitions.concurrentRuns.title} + + + + -
    - - Up to {plan.runs?.freeAllowance ? formatNumberCompact(plan.runs.freeAllowance) : ""}{" "} - - {pricingDefinitions.jobRuns.title} - - - - Unlimited{" "} - - Jobs - - - - Unlimited{" "} - - Tasks - - - - Unlimited{" "} - - Events - - - Unlimited team members - 24 hour log retention - Community support - Custom Integrations - Role-based access control - SSO - On-prem option -
+ {isLoading ? "Updating plan" : actionText} + +
    + + Up to {plan.runs?.freeAllowance ? formatNumberCompact(plan.runs.freeAllowance) : ""}{" "} + + {pricingDefinitions.jobRuns.title} + + + + Unlimited{" "} + + Jobs + + + + Unlimited{" "} + + Tasks + + + + Unlimited{" "} + + Events + + + Unlimited team members + 24 hour log retention + Community support + Custom Integrations + Role-based access control + SSO + On-prem option +
+ ); } @@ -138,7 +183,7 @@ export function TierPro({ showActionText: boolean; }) { const lastSubmission = useActionData(); - const [form, { path, feedbackType, message }] = useForm({ + const [form] = useForm({ id: "subscribe", // TODO: type this lastSubmission: lastSubmission as any, @@ -164,12 +209,16 @@ export function TierPro({ const isCurrentPlan = currentConcurrencyTier === concurrentBracketCode; - let actionText = isCurrentPlan ? "Current Plan" : "Select plan"; + let actionText = "Select plan"; - if (showActionText && !isCurrentPlan) { - const currentTierIndex = concurrencyTiers.findIndex((c) => c.code === currentConcurrencyTier); - const selectedTierIndex = concurrencyTiers.findIndex((c) => c.code === concurrentBracketCode); - actionText = currentTierIndex < selectedTierIndex ? "Upgrade" : "Downgrade"; + if (showActionText) { + if (isCurrentPlan) { + actionText = "Current Plan"; + } else { + const currentTierIndex = concurrencyTiers.findIndex((c) => c.code === currentConcurrencyTier); + const selectedTierIndex = concurrencyTiers.findIndex((c) => c.code === concurrentBracketCode); + actionText = currentTierIndex < selectedTierIndex ? "Upgrade" : "Downgrade"; + } } return ( diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx index 920961408..fa0ca526c 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx @@ -27,6 +27,7 @@ import { formatDurationInDays } from "~/utils"; import { Handle } from "~/utils/handle"; import { OrganizationParamsSchema, plansPath, usagePath } from "~/utils/pathBuilder"; import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; +import { ActiveSubscription } from "@trigger.dev/billing"; export async function loader({ params, request }: LoaderFunctionArgs) { const userId = await requireUserId(request); @@ -51,6 +52,29 @@ export const handle: Handle = { breadcrumb: (match) => , }; +function planLabel(subscription?: ActiveSubscription) { + if (!subscription) { + return "You're currently on the Free plan"; + } + if (!subscription.isPaying) { + return `You're currently on the ${subscription.plan.title} plan`; + } + const costDescription = subscription.plan.concurrentRuns.pricing + ? `\$${subscription.plan.concurrentRuns.pricing?.tierCost}/mo` + : ""; + if (subscription.canceledAt) { + return ( + <> + You're on the {costDescription} {subscription.plan.title} plan until{" "} + when you'll be on the Free + plan + + ); + } + + return `You're currently on the ${costDescription} ${subscription.plan.title} plan`; +} + export default function Page() { const { stripePortalLink } = useTypedLoaderData(); const organization = useOrganization(); @@ -93,11 +117,7 @@ export default function Page() { {currentPlan?.subscription && ( } - value={`You're currently on the${ - currentPlan.subscription.plan.concurrentRuns.pricing?.tierCost - ? ` \$${currentPlan.subscription.plan.concurrentRuns.pricing?.tierCost}/mo` - : "" - } ${currentPlan.subscription?.plan.title} plan`} + value={planLabel(currentPlan.subscription)} /> )} {currentPlan?.subscription?.isPaying && (