Improved some of the display
This commit is contained in:
@@ -56,74 +56,119 @@ export function PricingTiers({
|
||||
className
|
||||
)}
|
||||
>
|
||||
<TierFree plan={plans.free} />
|
||||
<TierFree plan={plans.free} organizationSlug={organizationSlug} showActionText={true} />
|
||||
<TierPro plan={plans.paid} organizationSlug={organizationSlug} showActionText={true} />
|
||||
<TierEnterprise />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<TierContainer>
|
||||
<Header title={plan.title} cost={0} />
|
||||
<TierLimit>
|
||||
Up to {plan.concurrentRuns?.freeAllowance}{" "}
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.concurrentRuns.title}
|
||||
content={pricingDefinitions.concurrentRuns.content}
|
||||
<Form action={`/resources/${organizationSlug}/subscribe`} method="post" {...form.props}>
|
||||
<Header title={plan.title} cost={0} />
|
||||
<TierLimit>
|
||||
Up to {plan.concurrentRuns?.freeAllowance}{" "}
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.concurrentRuns.title}
|
||||
content={pricingDefinitions.concurrentRuns.content}
|
||||
>
|
||||
{pricingDefinitions.concurrentRuns.title}
|
||||
</DefinitionTip>
|
||||
</TierLimit>
|
||||
<input type="hidden" name="type" value="free" />
|
||||
<Button
|
||||
variant="secondary/large"
|
||||
fullWidth
|
||||
className="text-md my-6 font-medium"
|
||||
disabled={isLoading || isCurrentPlan}
|
||||
LeadingIcon={isLoading ? "spinner-white" : undefined}
|
||||
>
|
||||
{pricingDefinitions.concurrentRuns.title}
|
||||
</DefinitionTip>
|
||||
</TierLimit>
|
||||
<Button variant="secondary/large" fullWidth className="text-md my-6 font-medium">
|
||||
Choose Plan
|
||||
</Button>
|
||||
<ul className="flex flex-col gap-2.5">
|
||||
<FeatureItem checked>
|
||||
Up to {plan.runs?.freeAllowance ? formatNumberCompact(plan.runs.freeAllowance) : ""}{" "}
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.jobRuns.title}
|
||||
content={pricingDefinitions.jobRuns.content}
|
||||
>
|
||||
{pricingDefinitions.jobRuns.title}
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<FeatureItem checked>
|
||||
Unlimited{" "}
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.jobs.title}
|
||||
content={pricingDefinitions.jobs.content}
|
||||
>
|
||||
Jobs
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<FeatureItem checked>
|
||||
Unlimited{" "}
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.tasks.title}
|
||||
content={pricingDefinitions.tasks.content}
|
||||
>
|
||||
Tasks
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<FeatureItem checked>
|
||||
Unlimited{" "}
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.events.title}
|
||||
content={pricingDefinitions.events.content}
|
||||
>
|
||||
Events
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<FeatureItem checked>Unlimited team members</FeatureItem>
|
||||
<FeatureItem checked>24 hour log retention</FeatureItem>
|
||||
<FeatureItem checked>Community support</FeatureItem>
|
||||
<FeatureItem>Custom Integrations</FeatureItem>
|
||||
<FeatureItem>Role-based access control</FeatureItem>
|
||||
<FeatureItem>SSO</FeatureItem>
|
||||
<FeatureItem>On-prem option</FeatureItem>
|
||||
</ul>
|
||||
{isLoading ? "Updating plan" : actionText}
|
||||
</Button>
|
||||
<ul className="flex flex-col gap-2.5">
|
||||
<FeatureItem checked>
|
||||
Up to {plan.runs?.freeAllowance ? formatNumberCompact(plan.runs.freeAllowance) : ""}{" "}
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.jobRuns.title}
|
||||
content={pricingDefinitions.jobRuns.content}
|
||||
>
|
||||
{pricingDefinitions.jobRuns.title}
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<FeatureItem checked>
|
||||
Unlimited{" "}
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.jobs.title}
|
||||
content={pricingDefinitions.jobs.content}
|
||||
>
|
||||
Jobs
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<FeatureItem checked>
|
||||
Unlimited{" "}
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.tasks.title}
|
||||
content={pricingDefinitions.tasks.content}
|
||||
>
|
||||
Tasks
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<FeatureItem checked>
|
||||
Unlimited{" "}
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.events.title}
|
||||
content={pricingDefinitions.events.content}
|
||||
>
|
||||
Events
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<FeatureItem checked>Unlimited team members</FeatureItem>
|
||||
<FeatureItem checked>24 hour log retention</FeatureItem>
|
||||
<FeatureItem checked>Community support</FeatureItem>
|
||||
<FeatureItem>Custom Integrations</FeatureItem>
|
||||
<FeatureItem>Role-based access control</FeatureItem>
|
||||
<FeatureItem>SSO</FeatureItem>
|
||||
<FeatureItem>On-prem option</FeatureItem>
|
||||
</ul>
|
||||
</Form>
|
||||
</TierContainer>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
|
||||
@@ -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) => <BreadcrumbLink to={match.pathname} title="Usage & Billing" />,
|
||||
};
|
||||
|
||||
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{" "}
|
||||
<DateTime includeTime={false} date={subscription.canceledAt} /> 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<typeof loader>();
|
||||
const organization = useOrganization();
|
||||
@@ -93,11 +117,7 @@ export default function Page() {
|
||||
{currentPlan?.subscription && (
|
||||
<PageInfoProperty
|
||||
icon={<ReceiptRefundIcon className="h-4 w-4 text-green-600" />}
|
||||
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 && (
|
||||
|
||||
Reference in New Issue
Block a user