Files
PaperClipAI/ui/src/components/CostByUserTable.tsx
T

57 lines
3.2 KiB
TypeScript

import type { CostByUserReport } from "@paperclipai/shared";
import { CostEstimateLabel } from "./CostEstimateLabel";
import { Identity } from "./Identity";
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
import { formatCents, formatTokens } from "../lib/utils";
export function CostByUserTable({ report }: { report: CostByUserReport }) {
return (
<Card className="gap-3 py-4">
<CardHeader className="gap-0 px-5">
<CardTitle className="text-base">By user</CardTitle>
</CardHeader>
<CardContent className="px-5">
<div className="overflow-x-auto">
<table aria-label="Costs by user" className="w-full text-sm">
<thead className="text-muted-foreground">
<tr className="border-b border-border">
<th scope="col" className="py-2 pr-4 text-left font-medium">User</th>
<th scope="col" className="px-4 py-2 text-right font-medium">Runs</th>
<th scope="col" className="px-4 py-2 text-right font-medium">Input</th>
<th scope="col" className="px-4 py-2 text-right font-medium">Output</th>
<th scope="col" className="py-2 pl-4 text-right font-medium">Cost</th>
</tr>
</thead>
<tbody>
{report.rows.length === 0 && (
<tr><td colSpan={5} className="py-3 text-muted-foreground">No user-attributed costs yet.</td></tr>
)}
{report.rows.map(row => (
<tr key={row.userId ?? "unattributed"} className="border-b border-border last:border-0">
<th scope="row" className="py-3 pr-4 text-left font-normal">
{row.userId ? <Identity name={row.userName ?? "Unknown user"} avatarUrl={row.userImage} /> : "Unattributed"}
</th>
<td className="px-4 py-3 text-right tabular-nums">{row.runCount.toLocaleString()}</td>
<td className="px-4 py-3 text-right tabular-nums">
{formatTokens(row.inputTokens + row.cachedInputTokens)}
{row.cachedInputTokens > 0 && <div className="whitespace-nowrap text-xs text-muted-foreground">{formatTokens(row.cachedInputTokens)} cached</div>}
</td>
<td className="px-4 py-3 text-right tabular-nums">{formatTokens(row.outputTokens)}</td>
<td className="py-3 pl-4 text-right tabular-nums">
<div>{row.costCents === 0 && row.eventCount > 0 && row.unpricedEventCount === row.eventCount ? "—" : formatCents(row.costCents)}</div>
<CostEstimateLabel eventCount={row.eventCount} estimatedEventCount={row.estimatedEventCount} />
{row.unpricedEventCount > 0 && <div className="whitespace-nowrap text-xs text-muted-foreground">{row.unpricedEventCount} unpriced {row.unpricedEventCount === 1 ? "charge" : "charges"}</div>}
</td>
</tr>
))}
</tbody>
</table>
</div>
{report.rows.some(row => row.userId === null) && (
<p className="mt-3 text-xs text-muted-foreground">Unattributed costs have no recorded user in this organization.</p>
)}
</CardContent>
</Card>
);
}