Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

56 linesCodeBlameRaw
1import { Suspense } from "react";
2import { Await, data } from "react-router";
3
4import { PRODUCTS } from "@g1t/contracts";
5
6import type { Route } from "./+types/usage";
7import { UsageFilterBar, UsageSkeleton } from "../../components/usage";
8import { page } from "../../lib/meta";
9import { billing } from "../../lib/services.server";
10import { getViewer, roleIn } from "../../lib/session.server";
11import { USAGE_API, UsageView, readFilters } from "../workspace/usage";
12import { Unavailable } from "../../components/boundary";
13
14// A project's usage: the workspace's Usage page, held to this one project.
15// Its members only, as the workspace's page is.
16
17export function meta({ params, ...args }: Route.MetaArgs) {
18 return page(args, { title: `Usage · ${params.owner}/${params.repo} · g1t` });
19}
20
21export async function loader({ params, context, request }: Route.LoaderArgs) {
22 const viewer = getViewer(context);
23 const role = roleIn(viewer, params.owner);
24 if (!role) throw data(null, { status: 404 });
25 const slug = params.owner.toLowerCase();
26 const project = `${slug}/${params.repo.toLowerCase()}`;
27 const url = new URL(request.url);
28 const filters = readFilters(url, project);
29 const report = billing
30 .usageReport(slug, viewer, { from: filters.from, until: filters.until, products: filters.products, projects: [project] })
31 .then((result) => (result.ok ? { report: result.value, error: null } : { report: null, error: result.error.message }));
32 return { slug, project, owner: role === "owner", filters, url: url.toString(), report };
33}
34
35export default function ProjectUsage({ loaderData }: Route.ComponentProps) {
36 const { slug, project, owner, filters, url, report } = loaderData;
37 return (
38 <div>
39 <h1 className="mb-4 text-lg font-semibold tracking-tight">Usage</h1>
40 <Suspense
41 fallback={
42 <UsageSkeleton>
43 <UsageFilterBar filters={filters} products={PRODUCTS} projects={[]} report={null} apiHref={USAGE_API} fixedProject={project} />
44 </UsageSkeleton>
45 }
46 >
47 {/* A report that never arrives says so below the page's heading. */}
48 <Await resolve={report} errorElement={<Unavailable what="Usage" />}>
49 {(loaded) => (
50 <UsageView slug={slug} owner={owner} filters={filters} url={url} report={loaded.report} error={loaded.error} fixedProject={project} />
51 )}
52 </Await>
53 </Suspense>
54 </div>
55 );
56}