nexus/ui/src/api/costs.ts
Sai Shankar c53ab402d3 feat(ui): add resource and usage dashboard (/usage route)
adds a new /usage page that lets board operators see how much each ai
provider is consuming across any date window, with per-model breakdowns,
rolling 5h/24h/7d burn windows, weekly budget bars, and a deficit notch
when projected spend is on track to exceed the monthly budget.

- new GET /companies/:id/costs/by-provider endpoint aggregates cost events
  by provider + model with pro-rated billing type splits from heartbeat runs
- new GET /companies/:id/costs/window-spend endpoint returns rolling window
  spend (5h, 24h, 7d) per provider with no schema changes
- QuotaBar: reusable boxed-border progress bar with green/yellow/red
  threshold fill colors and optional deficit notch
- ProviderQuotaCard: per-provider card showing budget allocation bars,
  rolling windows, subscription usage, and model breakdown with token/cost
  share overlays
- Usage page: date preset toggles (mtd, 7d, 30d, ytd, all, custom),
  provider tabs, 30s polling plus ws invalidation on cost_event
- custom date range blocks queries until both dates are selected and
  treats boundaries as local-time (not utc midnight) so full days are
  included regardless of timezone
- query key to timestamp is floored to the nearest minute to prevent
  cache churn on every 30s refetch tick
2026-03-14 22:00:22 -05:00

31 lines
1.3 KiB
TypeScript

import type { CostSummary, CostByAgent, CostByProviderModel, CostWindowSpendRow } from "@paperclipai/shared";
import { api } from "./client";
export interface CostByProject {
projectId: string | null;
projectName: string | null;
costCents: number;
inputTokens: number;
outputTokens: number;
}
function dateParams(from?: string, to?: string): string {
const params = new URLSearchParams();
if (from) params.set("from", from);
if (to) params.set("to", to);
const qs = params.toString();
return qs ? `?${qs}` : "";
}
export const costsApi = {
summary: (companyId: string, from?: string, to?: string) =>
api.get<CostSummary>(`/companies/${companyId}/costs/summary${dateParams(from, to)}`),
byAgent: (companyId: string, from?: string, to?: string) =>
api.get<CostByAgent[]>(`/companies/${companyId}/costs/by-agent${dateParams(from, to)}`),
byProject: (companyId: string, from?: string, to?: string) =>
api.get<CostByProject[]>(`/companies/${companyId}/costs/by-project${dateParams(from, to)}`),
byProvider: (companyId: string, from?: string, to?: string) =>
api.get<CostByProviderModel[]>(`/companies/${companyId}/costs/by-provider${dateParams(from, to)}`),
windowSpend: (companyId: string) =>
api.get<CostWindowSpendRow[]>(`/companies/${companyId}/costs/window-spend`),
};