From c1480cef8b2310f044c93690a26ef738164feeed Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Fri, 1 Dec 2023 16:07:08 +0000 Subject: [PATCH] Data for the plans page --- .../app/presenters/BillingPresenter.server.ts | 15 ++++++++ .../app/presenters/OrgBillingPlanPresenter.ts | 16 ++++++++ .../route.tsx | 24 ++++++++++++ .../route.tsx | 38 ++++++++++--------- 4 files changed, 75 insertions(+), 18 deletions(-) create mode 100644 apps/webapp/app/presenters/OrgBillingPlanPresenter.ts diff --git a/apps/webapp/app/presenters/BillingPresenter.server.ts b/apps/webapp/app/presenters/BillingPresenter.server.ts index 4818facfd..3ebfa13f1 100644 --- a/apps/webapp/app/presenters/BillingPresenter.server.ts +++ b/apps/webapp/app/presenters/BillingPresenter.server.ts @@ -82,4 +82,19 @@ export class BillingPresenter { return undefined; } } + + async getPlans() { + if (!this.#billingClient) return undefined; + try { + const result = await this.#billingClient.plans(); + if (!result.success) { + logger.error("Error getting plans", { error: result.error }); + return undefined; + } + return result; + } catch (e) { + logger.error("Error getting plans", { error: e }); + return undefined; + } + } } diff --git a/apps/webapp/app/presenters/OrgBillingPlanPresenter.ts b/apps/webapp/app/presenters/OrgBillingPlanPresenter.ts new file mode 100644 index 000000000..d5525a33b --- /dev/null +++ b/apps/webapp/app/presenters/OrgBillingPlanPresenter.ts @@ -0,0 +1,16 @@ +import { PrismaClient, prisma } from "~/db.server"; +import { logger } from "~/services/logger.server"; +import { BillingPresenter } from "./BillingPresenter.server"; + +export class OrgBillingPlanPresenter { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call({ slug }: { slug: string }) { + const billingPresenter = new BillingPresenter(true); + return billingPresenter.getPlans(); + } +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx index b38cdd829..46fe5125e 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx @@ -1,16 +1,40 @@ +import { LoaderFunctionArgs } from "@remix-run/server-runtime"; +import { redirect, typedjson, useTypedLoaderData } from "remix-typedjson"; import { PricingCalculator } from "~/components/billing/PricingCalculator"; import { PricingTiers, TierEnterprise, TierFree, TierPro } from "~/components/billing/PricingTiers"; import { RunsVolumeDiscountTable } from "~/components/billing/RunsVolumeDiscountTable"; import { BreadcrumbLink } from "~/components/navigation/Breadcrumb"; import { Callout } from "~/components/primitives/Callout"; import { Header2 } from "~/components/primitives/Headers"; +import { featuresForRequest } from "~/features.server"; +import { OrgBillingPlanPresenter } from "~/presenters/OrgBillingPlanPresenter"; import { Handle } from "~/utils/handle"; +import { OrganizationParamsSchema, organizationBillingPath } from "~/utils/pathBuilder"; + +export async function loader({ params, request }: LoaderFunctionArgs) { + const { organizationSlug } = OrganizationParamsSchema.parse(params); + + const { isManagedCloud } = featuresForRequest(request); + if (!isManagedCloud) { + return redirect(organizationBillingPath({ slug: organizationSlug })); + } + + const presenter = new OrgBillingPlanPresenter(); + const plans = await presenter.call({ slug: organizationSlug }); + if (!plans) { + throw new Response(null, { status: 404 }); + } + + return typedjson({ plans }); +} export const handle: Handle = { breadcrumb: (match) => , }; export default function Page() { + const { plans } = useTypedLoaderData(); + 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 0b003998f..713009815 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx @@ -6,6 +6,7 @@ import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; import { BreadcrumbLink } from "~/components/navigation/Breadcrumb"; import { LinkButton } from "~/components/primitives/Buttons"; +import { DateTime } from "~/components/primitives/DateTime"; import { PageButtons, PageHeader, @@ -16,17 +17,16 @@ import { PageTitle, PageTitleRow, } from "~/components/primitives/PageHeader"; +import { featuresForRequest } from "~/features.server"; +import { useFeatures } from "~/hooks/useFeatures"; import { useOrganization } from "~/hooks/useOrganizations"; +import { BillingPresenter } from "~/presenters/BillingPresenter.server"; import { OrgUsagePresenter } from "~/presenters/OrgUsagePresenter.server"; import { requireUserId } from "~/services/session.server"; +import { formatDurationMilliseconds } from "~/utils"; import { Handle } from "~/utils/handle"; import { OrganizationParamsSchema, PlansPath, UsagePath } from "~/utils/pathBuilder"; import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; -import { useFeatures } from "~/hooks/useFeatures"; -import { DateTime } from "~/components/primitives/DateTime"; -import { formatDuration, formatDurationMilliseconds } from "~/utils"; -import { featuresForRequest } from "~/features.server"; -import { BillingPresenter } from "~/presenters/BillingPresenter.server"; export async function loader({ params, request }: LoaderFunctionArgs) { const userId = await requireUserId(request); @@ -118,19 +118,21 @@ export default function Page() { )} - + {isManagedCloud && ( + + )}