fix(dashboard): surface Claude extraUsage credits in quota card (#6806) (#6896)

Enterprise-tier Claude accounts (default_raven_enterprise) don't get
five_hour/seven_day utilization windows from Anthropic's OAuth usage
endpoint — only an extra_usage credit-billing block. parseClaude()
only read data.quotas, so quotas stayed {} and the dashboard showed
"No quota data" even when extraUsage showed the account 100%
exhausted. parseClaude() now folds an enabled extraUsage block into a
credits-style quota row (mirroring parseCodex's bankedResetCredits
pattern), both when quotas is empty and when it's already populated.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-11 11:21:30 -03:00
committed by GitHub
parent 69e47cdec5
commit ac81235609
3 changed files with 127 additions and 1 deletions

View File

@@ -0,0 +1 @@
- fix(dashboard): surface Claude extraUsage credits in quota card when quotas is empty (#6806)

View File

@@ -158,12 +158,39 @@ function parseCodex(data: any) {
return quotas;
}
function buildClaudeExtraUsageQuota(extraUsage: any) {
const monthlyLimit = Number(extraUsage?.monthly_limit ?? 0);
const usedCredits = Number(extraUsage?.used_credits ?? 0);
const utilization = Number(extraUsage?.utilization ?? 0);
const remainingPercentage = Number.isFinite(utilization)
? Math.max(0, 100 - utilization)
: undefined;
const remaining = Number.isFinite(monthlyLimit) ? Math.max(0, monthlyLimit - usedCredits) : 0;
return buildCreditsQuota("extra_usage", remaining, remainingPercentage ?? 100, {
used: Number.isFinite(usedCredits) ? usedCredits : 0,
total: Number.isFinite(monthlyLimit) ? monthlyLimit : 0,
currency: extraUsage?.currency,
});
}
// #6806: some Claude plans (e.g. "default_raven_enterprise") return no
// five_hour/seven_day utilization windows at all — only a credit-billing
// extraUsage block — so quotas can be {} while extraUsage still holds real,
// actionable usage data. Fold it in instead of falling back to "No quota data".
function parseClaude(data: any) {
if (data?.message)
return [{ name: "error", used: 0, total: 0, resetAt: null, message: data.message }];
return quotaEntries(data).map(([name, quota]) =>
const quotas = quotaEntries(data).map(([name, quota]) =>
normalizeQuotaEntry(name, quota, { isPercentageOnly: true })
);
if (data?.extraUsage?.is_enabled) {
quotas.push(buildClaudeExtraUsageQuota(data.extraUsage));
}
return quotas;
}
function parseDeepseekQuota(quotaKey: string, quota: any) {

View File

@@ -0,0 +1,98 @@
import test from "node:test";
import assert from "node:assert/strict";
import { parseQuotaData } from "../../src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.tsx";
interface QuotaRow {
name: string;
isCredits?: boolean;
remainingPercentage?: number;
}
// Reproduces issue #6806: Claude Code "raven_enterprise" plan usage response has an
// empty `quotas: {}` (no five_hour/seven_day utilization fields returned by Anthropic
// for this plan) but a fully populated, 100%-exhausted `extraUsage` block. The UI must
// not fall back to "No quota data" when extraUsage has real, usable data.
test("#6806: parseQuotaData surfaces Claude extraUsage credits when quotas object is empty", () => {
const data = {
plan: "default_raven_enterprise",
quotas: {},
extraUsage: {
is_enabled: true,
monthly_limit: 120000,
used_credits: 120015,
utilization: 100,
currency: "USD",
decimal_places: 2,
disabled_reason: null,
daily: null,
weekly: null,
},
bootstrap: {
account_uuid: "redacted",
account_email: "redacted",
organization_uuid: "redacted",
organization_name: "redacted",
organization_type: "claude_enterprise",
organization_rate_limit_tier: "default_raven_enterprise",
},
};
const parsed = parseQuotaData("claude", data);
assert.ok(
parsed.length > 0,
`expected at least one quota row derived from extraUsage, got empty array: ${JSON.stringify(parsed)}`
);
const creditRow = parsed.find((row: QuotaRow) => row.isCredits);
assert.ok(creditRow, "expected a credits-style quota row derived from extraUsage");
assert.equal(creditRow.remainingPercentage, 0, "utilization 100% means 0% remaining");
});
test("#6806: parseQuotaData still surfaces extraUsage credits when quotas is also populated", () => {
const data = {
plan: "pro",
quotas: {
"session (5h)": { used: 10, total: 100, remainingPercentage: 90, resetAt: null },
},
extraUsage: {
is_enabled: true,
monthly_limit: 5000,
used_credits: 1000,
utilization: 20,
currency: "USD",
decimal_places: 2,
disabled_reason: null,
daily: null,
weekly: null,
},
};
const parsed = parseQuotaData("claude", data);
assert.equal(parsed.length, 2, `expected session quota + credits row, got: ${JSON.stringify(parsed)}`);
const creditRow = parsed.find((row: QuotaRow) => row.isCredits);
assert.ok(creditRow, "expected a credits-style quota row derived from extraUsage");
assert.equal(creditRow.remainingPercentage, 80, "utilization 20% means 80% remaining");
});
test("#6806: parseQuotaData does not add a credits row when extraUsage is disabled", () => {
const data = {
plan: "pro",
quotas: {
"session (5h)": { used: 10, total: 100, remainingPercentage: 90, resetAt: null },
},
extraUsage: {
is_enabled: false,
monthly_limit: 5000,
used_credits: 0,
utilization: 0,
},
};
const parsed = parseQuotaData("claude", data);
assert.equal(parsed.length, 1, `expected only the session quota, got: ${JSON.stringify(parsed)}`);
assert.equal(parsed.some((row: QuotaRow) => row.isCredits), false);
});