import { CalendarDaysIcon, CreditCardIcon, StarIcon } from "@heroicons/react/20/solid"; import { type MetaFunction } from "@remix-run/react"; import { type PlanDefinition } from "@trigger.dev/platform"; import { redirect, typedjson, useTypedLoaderData } from "remix-typedjson"; import { Feedback } from "~/components/Feedback"; import { MainCenteredContainer, PageBody, PageContainer } from "~/components/layout/AppLayout"; import { Button, LinkButton } from "~/components/primitives/Buttons"; import { Callout } from "~/components/primitives/Callout"; import { DateTime } from "~/components/primitives/DateTime"; import { InfoPanel } from "~/components/primitives/InfoPanel"; import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader"; import { Paragraph } from "~/components/primitives/Paragraph"; import { prisma } from "~/db.server"; import { featuresForRequest } from "~/features.server"; import { resolveOrgIdFromSlug } from "~/models/organization.server"; import { getCurrentPlan, getPlans } from "~/services/platform.v3.server"; import { dashboardLoader } from "~/services/routeBuilders/dashboardBuilder"; import { OrganizationParamsSchema, organizationPath, v3StripePortalPath, } from "~/utils/pathBuilder"; import { PricingPlans } from "../resources.orgs.$organizationSlug.select-plan"; export const meta: MetaFunction = () => { return [ { title: `Billing | Trigger.dev`, }, ]; }; export const loader = dashboardLoader( { params: OrganizationParamsSchema, context: async (params) => { const organizationId = await resolveOrgIdFromSlug(params.organizationSlug); return organizationId ? { organizationId } : {}; }, authorization: { action: "manage", resource: { type: "billing" }, message: "With your current role, you can't manage billing.", }, }, async ({ params, request, user }) => { const userId = user.id; const { organizationSlug } = params; const { isManagedCloud } = featuresForRequest(request); if (!isManagedCloud) { return redirect(organizationPath({ slug: organizationSlug })); } const organization = await prisma.organization.findFirst({ where: { slug: organizationSlug, members: { some: { userId } } }, }); if (!organization) { throw new Response(null, { status: 404, statusText: "Organization not found" }); } const currentPlan = await getCurrentPlan(organization.id); const showSelfServe = currentPlan?.v3Subscription?.showSelfServe !== false; //periods const periodStart = new Date(); periodStart.setUTCHours(0, 0, 0, 0); periodStart.setUTCDate(1); const periodEnd = new Date(); periodEnd.setUTCMonth(periodEnd.getMonth() + 1); periodEnd.setUTCDate(0); periodEnd.setUTCHours(0, 0, 0, 0); const daysRemaining = Math.ceil( (periodEnd.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24) ); // Extract 'message' from search params const url = new URL(request.url); const message = url.searchParams.get("message"); if (!showSelfServe) { return typedjson({ showSelfServe: false as const, ...currentPlan, organizationSlug, periodStart, periodEnd, daysRemaining, message, }); } const plans = await getPlans(); if (!plans) { throw new Response(null, { status: 404, statusText: "Plans not found" }); } return typedjson({ showSelfServe: true as const, ...plans, ...currentPlan, organizationSlug, periodStart, periodEnd, daysRemaining, message, }); } ); export default function ChoosePlanPage() { const loaderData = useTypedLoaderData(); const { showSelfServe, v3Subscription, organizationSlug, periodStart, periodEnd, daysRemaining, message, } = loaderData; return ( {v3Subscription?.isPaying && showSelfServe && ( <> Invoices Manage card details )} {showSelfServe ? (
{message && ( {message} )}
{planLabel( v3Subscription?.plan, v3Subscription?.canceledAt !== undefined, periodEnd )}
{v3Subscription?.isPaying ? (
Billing period: {" "} to ( {daysRemaining} days remaining)
) : null}
) : ( Contact us} /> } > Your billing is managed by our team. Get in touch for invoices, plan changes, or other billing questions. )}
); } function planLabel(plan: PlanDefinition | undefined, canceled: boolean, periodEnd: Date) { if (!plan || plan.type === "free") { return "You're on the Free plan"; } if (plan.type === "enterprise") { return "You're on the Enterprise plan"; } const text = `You're on the $${plan.tierPrice}/mo ${plan.title} plan`; if (canceled) { return ( <> {text}. From you're on the Free plan. ); } return text; }