# Files changed · PR \#23835 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/23835/diffs)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

[fix\(web\): respect usage metric in time breakdown](https://git.cafe/pingdotgg/t3code/pull/23835.md)

Version: 3

Head commit: 6a08907243e1247d109439195bf031af6cd4131a

Base commit: ef92441313da327139df75daa8f723c0f42348f6

Comparison base: 52c2b38ea41ee2271f5aca527009d36d25432689

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/web/src/components/usage/UsagePage.tsx

Status: modified; +6; −4

```diff
@@ -93,6 +93,7 @@
   modelShare,
   sortModelsByTokens,
   speedCostSegments,
+  timeBreakdownMetric,
   tokenTypeSegments,
 } from "./usageBreakdown";
 import {
@@ -671,6 +672,7 @@
   const breakdownPeriods: readonly (DailyTotals | HourlyTotals)[] = (
     shownHourly ? merged.hourly : merged.daily
   ).toReversed();
+  const timeMetric = timeBreakdownMetric(metric);
   const breakdownModels =
     breakdown === "model" && metric === "tokens"
       ? sortModelsByTokens(merged.models)
@@ -1019,7 +1021,7 @@
                     </th>
                   ))}
                   <th className="py-2 pl-4 text-right font-normal">Total</th>
+                  <th className="py-2 pl-4 text-right font-normal">{timeMetric.secondaryLabel}</th>
-                  <th className="py-2 pl-4 text-right font-normal">Tokens</th>
                 </tr>
               </thead>
               <tbody>
@@ -1051,7 +1053,7 @@
                             figureClass(loadingProviders.has(provider)),
                           )}
                         >
-                          {formatUsd(period.byProvider.get(provider)?.costUsd ?? 0)}
+                          {timeMetric.formatValue(period.byProvider.get(provider))}
                         </td>
                       ))}
                       <td
@@ -1060,7 +1062,7 @@
                           figureClass(loadingPartial),
                         )}
                       >
+                        {timeMetric.formatValue(period)}
-                        {formatUsd(period.costUsd)}
                       </td>
                       <td
                         className={cn(
@@ -1068,7 +1070,7 @@
                           figureClass(loadingPartial),
                         )}
                       >
-                        {formatTokens(period.totalTokens)}
+                        {timeMetric.formatSecondaryValue(period)}
                       </td>
                     </tr>
                   ))
```

## apps/web/src/components/usage/usageBreakdown.test.ts

Status: modified; +39; −2

```diff
@@ -1,8 +1,45 @@
-import type { ModelTotals } from "@t3tools/shared/usageMerge";
+import type { DailyTotals, HourlyTotals, ModelTotals } from "@t3tools/shared/usageMerge";
 import { describe, expect, it } from "vite-plus/test";
 
+import { cacheHitRate, costPerMillionTokens, timeBreakdownMetric } from "./usageBreakdown";
+
+describe("time breakdown metric", () => {
+  const daily: DailyTotals = {
+    day: "2026-10-22",
+    costUsd: 3.5,
+    totalTokens: 2_500_000,
+    byProvider: new Map([
+      ["codex", { costUsd: 3.5, totalTokens: 2_000_000 }],
+      ["opencode", { costUsd: 0, totalTokens: 500_000 }],
+    ]),
+  };
+  const hourly: HourlyTotals = { ...daily, hourStart: "2026-10-22T10:00:00.000Z" };
+
+  it.each([
+    { resolution: "day", period: daily },
+    { resolution: "hour", period: hourly },
+  ])("switches $resolution provider and total figures with the selected metric", ({ period }) => {
+    for (const metric of ["cost", "tokens", "cost"] as const) {
+      const figures = timeBreakdownMetric(metric);
+      expect(figures.formatValue(period.byProvider.get("codex"))).toBe(
+        metric === "tokens" ? "2M" : "$3.50",
-import { cacheHitRate, costPerMillionTokens } from "./usageBreakdown";
+      );
+      expect(figures.formatValue(period.byProvider.get("opencode"))).toBe(
+        metric === "tokens" ? "500K" : "$0.00",
+      );
+      expect(figures.formatValue(period)).toBe(metric === "tokens" ? "2.50M" : "$3.50");
+      expect(figures.secondaryLabel).toBe(metric === "tokens" ? "Cost" : "Tokens");
+      expect(figures.formatSecondaryValue(period)).toBe(metric === "tokens" ? "$3.50" : "2.50M");
+    }
+  });
 
+  it("shows zero for a provider with no activity in the period", () => {
+    const missing = hourly.byProvider.get("claude");
+    expect(timeBreakdownMetric("cost").formatValue(missing)).toBe("$0.00");
+    expect(timeBreakdownMetric("tokens").formatValue(missing)).toBe("0");
+  });
+});
+
 const model = (
   name: string,
   totalTokens: number,
```

## apps/web/src/components/usage/usageBreakdown.ts

Status: modified; +21; −0

```diff
@@ -1,13 +1,34 @@
 import type { UsageTokenTotals } from "@t3tools/contracts";
+import { formatTokens, formatUsd } from "@t3tools/shared/usageFormat";
 import {
   isModelCostUnknown,
   type CategoryCost,
+  type DailyTotals,
   type ModelTotals,
   type SpeedCost,
 } from "@t3tools/shared/usageMerge";
 
 import type { ShareSegment } from "./UsageShareBar";
 
+export function timeBreakdownMetric(metric: "cost" | "tokens") {
+  const formatCost = (totals: Pick<DailyTotals, "costUsd" | "totalTokens"> | undefined) =>
+    formatUsd(totals?.costUsd ?? 0);
+  const formatTokenCount = (totals: Pick<DailyTotals, "costUsd" | "totalTokens"> | undefined) =>
+    formatTokens(totals?.totalTokens ?? 0);
+
+  return metric === "tokens"
+    ? {
+        formatValue: formatTokenCount,
+        secondaryLabel: "Cost",
+        formatSecondaryValue: formatCost,
+      }
+    : {
+        formatValue: formatCost,
+        secondaryLabel: "Tokens",
+        formatSecondaryValue: formatTokenCount,
+      };
+}
+
 export function sortModelsByTokens(models: readonly ModelTotals[]) {
   return models.toSorted(
     (left, right) => right.totalTokens - left.totalTokens || right.costUsd - left.costUsd,
```
