From 5f79a2bc0b1d0a259cfa4122b0af1991e0632b46 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Fri, 28 Jun 2024 19:54:42 +0100 Subject: [PATCH] Moved code to presenter and now using defer --- .../presenters/v3/UsagePresenter.server.ts | 63 +++++ .../route.tsx | 228 +++++++++--------- 2 files changed, 175 insertions(+), 116 deletions(-) create mode 100644 apps/webapp/app/presenters/v3/UsagePresenter.server.ts diff --git a/apps/webapp/app/presenters/v3/UsagePresenter.server.ts b/apps/webapp/app/presenters/v3/UsagePresenter.server.ts new file mode 100644 index 000000000..d001f04f8 --- /dev/null +++ b/apps/webapp/app/presenters/v3/UsagePresenter.server.ts @@ -0,0 +1,63 @@ +import { Prisma } from "@trigger.dev/database"; +import { sqlDatabaseSchema } from "~/db.server"; +import { BasePresenter } from "./basePresenter.server"; +import { getUsage, getUsageSeries } from "~/services/platform.v3.server"; +import { createTimeSeriesData } from "~/utils/graphs"; + +type Options = { + organizationId: string; +}; + +export class UsagePresenter extends BasePresenter { + public async call({ organizationId }: Options) { + //periods + const thirtyDaysAgo = new Date(); + thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); + thirtyDaysAgo.setHours(0, 0, 0, 0); + const endOfToday = new Date(); + endOfToday.setDate(endOfToday.getDate() + 1); + endOfToday.setHours(23, 59, 59, 999); + + const past30Days = getUsageSeries(organizationId, { + from: thirtyDaysAgo, + to: endOfToday, + window: "DAY", + }).then((data) => { + if (!data) return []; + return createTimeSeriesData({ + startDate: thirtyDaysAgo, + endDate: endOfToday, + window: "DAY", + data: + data.data.map((period) => ({ + date: new Date(period.windowStart), + value: period.value, + })) ?? [], + }).map((period) => ({ + date: period.date.toISOString(), + dollars: (period.value ?? 0) / 100, + })); + }); + + const startOfMonth = new Date(); + startOfMonth.setDate(1); + startOfMonth.setHours(0, 0, 0, 0); + + const now = new Date(); + const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999); + + const usage = getUsage(organizationId, { from: startOfMonth, to: endOfMonth }).then((data) => { + const current = (data?.cents ?? 0) / 100; + const percentageThroughMonth = new Date().getDate() / endOfMonth.getDate(); + return { + current: current, + projected: current / percentageThroughMonth, + }; + }); + + return { + past30Days, + usage, + }; + } +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.v3.usage/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.v3.usage/route.tsx index 47a74e7db..ff656cdbc 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.v3.usage/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.v3.usage/route.tsx @@ -1,7 +1,7 @@ import { ArrowRightIcon } from "@heroicons/react/24/solid"; import { LoaderFunctionArgs } from "@remix-run/server-runtime"; import { Bar, BarChart, Rectangle, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; -import { redirect, typedjson, useTypedLoaderData } from "remix-typedjson"; +import { redirect, typeddefer, typedjson, useTypedLoaderData } from "remix-typedjson"; import { UsageBar } from "~/components/billing/v3/UsageBar"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; import { Header2, Header3 } from "~/components/primitives/Headers"; @@ -14,6 +14,10 @@ import { createTimeSeriesData } from "~/utils/graphs"; import { formatCurrency } from "~/utils/numberFormatter"; import { OrganizationParamsSchema, organizationPath } from "~/utils/pathBuilder"; import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; +import { UsagePresenter } from "~/presenters/v3/UsagePresenter.server"; +import { Suspense } from "react"; +import { Await } from "@remix-run/react"; +import { Spinner } from "~/components/primitives/Spinner"; export async function loader({ params, request }: LoaderFunctionArgs) { await requireUserId(request); @@ -32,52 +36,11 @@ export async function loader({ params, request }: LoaderFunctionArgs) { throw new Response(null, { status: 404, statusText: "Organization not found" }); } - //periods - const thirtyDaysAgo = new Date(); - thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); - thirtyDaysAgo.setHours(0, 0, 0, 0); - const endOfToday = new Date(); - endOfToday.setDate(endOfToday.getDate() + 1); - endOfToday.setHours(23, 59, 59, 999); + const presenter = new UsagePresenter(); + const { past30Days, usage } = await presenter.call({ organizationId: organization.id }); - const past30Days = await getUsageSeries(organization.id, { - from: thirtyDaysAgo, - to: endOfToday, - window: "DAY", - }); - - const startOfMonth = new Date(); - startOfMonth.setDate(1); - startOfMonth.setHours(0, 0, 0, 0); - - const now = new Date(); - const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999); - - const usageData = await getUsage(organization.id, { from: startOfMonth, to: endOfMonth }); - - const current = (usageData?.cents ?? 0) / 100; - const percentageThroughMonth = new Date().getDate() / endOfMonth.getDate(); - const usage = { - current: current, - projected: current / percentageThroughMonth, - }; - - return typedjson({ - past30Days: past30Days - ? createTimeSeriesData({ - startDate: thirtyDaysAgo, - endDate: endOfToday, - window: "DAY", - data: - past30Days.data.map((period) => ({ - date: new Date(period.windowStart), - value: period.value, - })) ?? [], - }).map((period) => ({ - date: period.date, - dollars: (period.value ?? 0) / 100, - })) - : [], + return typeddefer({ + past30Days, usage, }); } @@ -109,79 +72,112 @@ export default function ChoosePlanPage() { - This month -
-
-
- Month-to-date -

- {formatCurrency(usage.current, false)} -

-
- -
- Projected -

{formatCurrency(usage.projected, false)}

+
+
+ This month +
+ }> + + {(usage) => ( + <> +
+
+ Month-to-date +

+ {formatCurrency(usage.current, false)} +

+
+ +
+ Projected +

+ {formatCurrency(usage.projected, false)} +

+
+
+ + + )} +
+
- -
- Past 30 days -
- Usage - - - { - if (!item.date) return ""; - const date = new Date(item.date); - if (date.getDate() === 1) { - return dateFormatter.format(date); - } - return `${date.getDate()}`; - }} - className="text-xs" - /> - `$${value.toFixed(2)}`} - /> - { - const dateString = data.at(0)?.payload.date; - if (!dateString) { - return ""; - } +
+ Past 30 days +
+ Usage + }> + + {(past30Days) => ( + + + { + if (!item.date) return ""; + const date = new Date(item.date); + if (date.getDate() === 1) { + return dateFormatter.format(date); + } + return `${date.getDate()}`; + }} + className="text-xs" + /> + `$${value.toFixed(2)}`} + /> + { + const dateString = data.at(0)?.payload.date; + if (!dateString) { + return ""; + } - return dateFormatter.format(new Date(dateString)); - }} - formatter={(value, data) => [`$${value.toLocaleString()}`, ""]} - /> - } /> - - + return dateFormatter.format(new Date(dateString)); + }} + formatter={(value, data) => [ + `$${ + typeof value === "number" ? value.toFixed(2) : value.toLocaleString() + }`, + "", + ]} + /> + } + /> + + + )} + + +
+