Show warnings about concurrency and runs on the plans page
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { ArrowUpCircleIcon } from "@heroicons/react/24/outline";
|
||||
import { LinkButton } from "../primitives/Buttons";
|
||||
import { Paragraph } from "../primitives/Paragraph";
|
||||
import { plansPath, organizationBillingPath } from "~/utils/pathBuilder";
|
||||
import { MatchedOrganization } from "~/hooks/useOrganizations";
|
||||
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
|
||||
import { formatNumberCompact } from "~/utils/numberFormatter";
|
||||
import { plansPath } from "~/utils/pathBuilder";
|
||||
import { LinkButton } from "../primitives/Buttons";
|
||||
import { Paragraph } from "../primitives/Paragraph";
|
||||
|
||||
type UpgradePromptProps = {
|
||||
organization: MatchedOrganization;
|
||||
@@ -12,14 +13,15 @@ type UpgradePromptProps = {
|
||||
export function UpgradePrompt({ organization }: UpgradePromptProps) {
|
||||
const currentPlan = useCurrentPlan();
|
||||
|
||||
if (!currentPlan || !currentPlan.usage.exceededRunCount) {
|
||||
if (!currentPlan || !currentPlan.usage.exceededRunCount || !currentPlan.usage.runCountCap) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full items-center gap-4 bg-gradient-to-r from-transparent to-indigo-900/50 pr-1.5">
|
||||
<Paragraph variant="extra-small" className="text-rose-500">
|
||||
You have exceeded the monthly {currentPlan.usage.runCountCap} Runs limit
|
||||
You have exceeded the monthly {formatNumberCompact(currentPlan.usage.runCountCap)} Runs
|
||||
limit
|
||||
</Paragraph>
|
||||
<LinkButton
|
||||
variant={"primary/small"}
|
||||
|
||||
@@ -9,8 +9,52 @@ export class OrgBillingPlanPresenter {
|
||||
this.#prismaClient = prismaClient;
|
||||
}
|
||||
|
||||
public async call({ slug }: { slug: string }) {
|
||||
const billingPresenter = new BillingService(true);
|
||||
return billingPresenter.getPlans();
|
||||
public async call({ slug, isManagedCloud }: { slug: string; isManagedCloud: boolean }) {
|
||||
const billingPresenter = new BillingService(isManagedCloud);
|
||||
const plans = await billingPresenter.getPlans();
|
||||
|
||||
if (plans === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const organization = await this.#prismaClient.organization.findFirst({
|
||||
where: {
|
||||
slug,
|
||||
},
|
||||
});
|
||||
|
||||
if (!organization) {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxConcurrency = await this.#prismaClient.$queryRaw<
|
||||
{ organization_id: string; max_concurrent_runs: BigInt }[]
|
||||
>`WITH events AS (
|
||||
SELECT
|
||||
re.event_time,
|
||||
re.organization_id,
|
||||
re.event_type,
|
||||
SUM(re.event_type) OVER (PARTITION BY re.organization_id ORDER BY re.event_time) AS running_total
|
||||
FROM
|
||||
triggerdotdev_events.run_executions re
|
||||
WHERE
|
||||
re.organization_id = ${organization.id}
|
||||
AND re.event_time >= DATE_TRUNC('month',
|
||||
CURRENT_DATE)
|
||||
)
|
||||
SELECT
|
||||
organization_id, MAX(running_total) AS max_concurrent_runs
|
||||
FROM
|
||||
events
|
||||
GROUP BY
|
||||
organization_id;`;
|
||||
|
||||
return {
|
||||
plans,
|
||||
maxConcurrency:
|
||||
maxConcurrency.at(0) !== undefined
|
||||
? Number(maxConcurrency[0].max_concurrent_runs)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,12 @@ import { BreadcrumbLink } from "~/components/navigation/Breadcrumb";
|
||||
import { Callout } from "~/components/primitives/Callout";
|
||||
import { Header2 } from "~/components/primitives/Headers";
|
||||
import { featuresForRequest } from "~/features.server";
|
||||
import { useFeatures } from "~/hooks/useFeatures";
|
||||
import { OrgBillingPlanPresenter } from "~/presenters/OrgBillingPlanPresenter";
|
||||
import { Handle } from "~/utils/handle";
|
||||
import { OrganizationParamsSchema, organizationBillingPath } from "~/utils/pathBuilder";
|
||||
import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route";
|
||||
import { formatNumberCompact } from "~/utils/numberFormatter";
|
||||
|
||||
export async function loader({ params, request }: LoaderFunctionArgs) {
|
||||
const { organizationSlug } = OrganizationParamsSchema.parse(params);
|
||||
@@ -24,12 +27,16 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
|
||||
}
|
||||
|
||||
const presenter = new OrgBillingPlanPresenter();
|
||||
const plans = await presenter.call({ slug: organizationSlug });
|
||||
if (!plans) {
|
||||
const result = await presenter.call({ slug: organizationSlug, isManagedCloud });
|
||||
if (!result) {
|
||||
throw new Response(null, { status: 404 });
|
||||
}
|
||||
|
||||
return typedjson({ plans, organizationSlug });
|
||||
return typedjson({
|
||||
plans: result.plans,
|
||||
maxConcurrency: result.maxConcurrency,
|
||||
organizationSlug,
|
||||
});
|
||||
}
|
||||
|
||||
export const handle: Handle = {
|
||||
@@ -37,16 +44,33 @@ export const handle: Handle = {
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const { plans, organizationSlug } = useTypedLoaderData<typeof loader>();
|
||||
const { plans, maxConcurrency, organizationSlug } = useTypedLoaderData<typeof loader>();
|
||||
const currentPlan = useCurrentPlan();
|
||||
|
||||
const hitConcurrencyLimit =
|
||||
currentPlan?.subscription?.limits.concurrentRuns && maxConcurrency
|
||||
? maxConcurrency >= currentPlan.subscription!.limits.concurrentRuns!
|
||||
: false;
|
||||
|
||||
const hitRunLimit = currentPlan?.usage?.runCountCap
|
||||
? currentPlan.usage.currentRunCount > currentPlan.usage.runCountCap
|
||||
: false;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<Callout variant={"pricing"}>
|
||||
Some of your Runs are being queued because your Run concurrency is limited to 50.
|
||||
</Callout>
|
||||
<Callout variant={"pricing"}>
|
||||
You have exceeded the monthly 10,000 Runs limit. Upgrade to a paid plan before Nov 30.
|
||||
</Callout>
|
||||
{hitConcurrencyLimit && (
|
||||
<Callout variant={"pricing"}>
|
||||
Some of your Runs are being queued because your Run concurrency is limited to{" "}
|
||||
{currentPlan?.subscription?.limits.concurrentRuns}.
|
||||
</Callout>
|
||||
)}
|
||||
{hitRunLimit && (
|
||||
<Callout variant={"pricing"}>
|
||||
{`You have exceeded the monthly
|
||||
${formatNumberCompact(currentPlan!.subscription!.limits.runs!)} Runs limit. Upgrade so you
|
||||
can continue to perform Runs.`}
|
||||
</Callout>
|
||||
)}
|
||||
<PricingTiers organizationSlug={organizationSlug} plans={plans} />
|
||||
<div>
|
||||
<Header2 spacing>Estimate your usage</Header2>
|
||||
|
||||
Reference in New Issue
Block a user