fix(web): show empty state on Billing tab when no models were used

Runs with only non-billable stages rendered as a header + empty body
+ all-dashes "Total" row, which looked broken. Show the existing
EmptyState panel ("No model usage") instead, keeping the original
"No stages yet" message for runs that haven't started executing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-05-09 16:41:28 -04:00
parent 39d8e65547
commit 0f30150264
No known key found for this signature in database
2 changed files with 14 additions and 9 deletions

View file

@ -66,7 +66,7 @@ describe("RunBilling", () => {
delete (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT;
});
test("hides completed non-billable stages but still renders the totals row", () => {
test("shows a no-model-usage empty state when every stage is non-billable", () => {
const renderer = renderBilling(
billing({
stages: [
@ -93,12 +93,12 @@ describe("RunBilling", () => {
);
const text = textFromNode(renderer.toJSON());
expect(text).toContain("No model usage");
expect(text).toContain("This run didn't call any AI models.");
expect(text).not.toContain("No stages yet");
expect(text).not.toContain("By model");
expect(text).not.toContain("start");
expect(text).not.toContain("command");
expect(text).toMatch(/—\s*\/\s*—/);
expect(text).toContain("1m 1s");
expect(text).not.toContain("By model");
expect(text).not.toContain("No stages yet");
});
test("renders mixed LLM and non-LLM rows while counting only LLM rows by model", () => {

View file

@ -121,13 +121,18 @@ export default function RunBilling({ params }: { params: { id: string } }) {
: null;
const totalUsdMicros = billing?.totals.total_usd_micros;
const modelStageCount = modelBreakdown.reduce((sum, row) => sum + row.stages, 0);
const visibleRows = rows.filter(isVisibleRow);
if (!rows.length) {
if (!visibleRows.length) {
return (
<div className="py-12">
<EmptyState
title="No stages yet"
description="Stages will appear as soon as the run starts executing."
title={rows.length ? "No model usage" : "No stages yet"}
description={
rows.length
? "This run didn't call any AI models."
: "Stages will appear as soon as the run starts executing."
}
/>
</div>
);
@ -147,7 +152,7 @@ export default function RunBilling({ params }: { params: { id: string } }) {
</tr>
</thead>
<tbody>
{rows.filter(isVisibleRow).map((row) => (
{visibleRows.map((row) => (
<tr key={row.stage} className="border-b border-line last:border-b-0">
<td className="px-4 py-3 text-fg-2">{row.stage}</td>
<td className="px-4 py-3 font-mono text-xs text-fg-3">