145 lines · 5.3 KB
import { createFileRoute, redirect } from "@tanstack/react-router";
import { z } from "zod";
import { adminSetPlan, getAdminBilling } from "~/fns/billing";
import { useAction } from "~/hooks/use-action";
import { formatQuantity, meterDef } from "~/lib/meters";
import { Badge } from "~/ui/Badge";
import { Button } from "~/ui/Button";
import { ErrorBanner } from "~/ui/Callout";
import { EmptyState } from "~/ui/EmptyState";
import { PageContainer } from "~/ui/PageContainer";
import { Panel } from "~/ui/Panel";
import { Heading } from "~/ui/Heading";
import { TextLink } from "~/ui/TextLink";
import { cn } from "~/utils/cn";

export const Route = createFileRoute("/admin/billing")({
  beforeLoad: ({ context }) => {
    if (!context.viewer) throw redirect({ to: "/login" });
  },
  validateSearch: z.object({ period: z.string().optional() }),
  loaderDeps: ({ search }) => ({ period: search.period }),
  loader: ({ deps }) => getAdminBilling({ data: { period: deps.period } }),
  component: AdminBillingPage,
});

type Account = Awaited<ReturnType<typeof getAdminBilling>>["accounts"][number];
type Meter = Account["meters"][number];

const OVER = 1;
const NEAR = 0.8;

function usageTone(meter: Meter): "danger" | "warning" | "neutral" {
  if (!meter.included) return "neutral";
  const ratio = meter.quantity / meter.included;
  if (ratio >= OVER) return "danger";
  return ratio >= NEAR ? "warning" : "neutral";
}

function MeterLine({ meter }: { meter: Meter }) {
  const def = meterDef(meter.meter);