From 8eeeb675faecdb4a01bfa87b482c5f126ec93d22 Mon Sep 17 00:00:00 2001 From: Yumi Date: Sat, 5 Sep 2026 18:56:33 -0600 Subject: [PATCH 1/3] fix(gui): render subscription credits quota row Co-authored-by: SB Yoon <44089734+yansigit@users.noreply.github.com> --- gui/src/components/QuotaBars.tsx | 20 ++++++++++++++++++++ tests/gui/quota-bars-rows.test.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/gui/src/components/QuotaBars.tsx b/gui/src/components/QuotaBars.tsx index 2715f48608..e840433a25 100644 --- a/gui/src/components/QuotaBars.tsx +++ b/gui/src/components/QuotaBars.tsx @@ -26,6 +26,7 @@ function rawCustomWindowRank(rawLabel: string): number { if (rawLabel === "5h") return 0; if (rawLabel === "First-party models") return 2; if (rawLabel === "API usage") return 3; + if (rawLabel === "Total subscription credits") return 4.5; return 5; } @@ -96,6 +97,22 @@ export function buildQuotaRows(quota: AccountQuota | null, plan: string | null | }, }); } + if (displayQuota.creditsUsd && typeof displayQuota.creditsUsd.percent === "number") { + const hasCreditCustom = displayQuota.customWindows?.some(w => /credits?/i.test(w.label)); + if (!hasCreditCustom) { + const localized = localizeCustomQuotaLabel("Total subscription credits", t); + ranked.push({ + rank: rawCustomWindowRank("Total subscription credits"), + row: { + customLabel: "Total subscription credits", + label: localized, + limitLabel: localized, + percent: displayQuota.creditsUsd.percent, + resetAt: displayQuota.creditsUsd.expiresAt, + }, + }); + } + } return ranked.sort((a, b) => a.rank - b.rank).map(entry => entry.row); } @@ -107,6 +124,9 @@ export function maxQuotaUtilisation(quota: AccountQuota | null): number { for (const w of quota.customWindows ?? []) { if (typeof w.percent === "number") vals.push(w.percent); } + if (typeof quota.creditsUsd?.percent === "number") { + vals.push(quota.creditsUsd.percent); + } return vals.length ? Math.max(...vals) : -1; } diff --git a/tests/gui/quota-bars-rows.test.ts b/tests/gui/quota-bars-rows.test.ts index 57ddd47cfd..64c14869a4 100644 --- a/tests/gui/quota-bars-rows.test.ts +++ b/tests/gui/quota-bars-rows.test.ts @@ -69,6 +69,29 @@ describe("buildQuotaRows (WP070)", () => { expect(rows.map(r => r.label)).toEqual(["quota.totalSubscriptionCredits"]); }); + test("direct creditsUsd renders Total subscription credits with resetAt", () => { + const rows = buildQuotaRows(quota({ + fiveHourPercent: 0, + weeklyPercent: 0, + creditsUsd: { used: 89.96, limit: 90, remaining: 0.04, percent: 99.96, expiresAt: 1790430938000 }, + }), null, t); + expect(rows.map(r => r.limitLabel)).toEqual([ + "quota.fiveHourLimit", + "quota.weeklyLimit", + "quota.totalSubscriptionCredits", + ]); + expect(rows[2]?.percent).toBe(99.96); + expect(rows[2]?.resetAt).toBe(1790430938000); + }); + + test("direct creditsUsd does not duplicate an existing credits custom window", () => { + const rows = buildQuotaRows(quota({ + customWindows: [{ label: "Total subscription credits", percent: 50 }], + creditsUsd: { used: 50, limit: 100, remaining: 50, percent: 50 }, + }), null, t); + expect(rows.map(r => r.label)).toEqual(["quota.totalSubscriptionCredits"]); + }); + test("null and empty quotas produce no rows; 30-day plans strip to monthly", () => { expect(buildQuotaRows(null, null, t)).toEqual([]); expect(buildQuotaRows(quota({}), null, t)).toEqual([]); @@ -93,6 +116,11 @@ describe("maxQuotaUtilisation", () => { fiveHourPercent: 10, customWindows: [{ label: "x", percent: 95 }], }))).toBe(95); + expect(maxQuotaUtilisation(quota({ + fiveHourPercent: 0, + weeklyPercent: 0, + creditsUsd: { used: 90, limit: 90, remaining: 0, percent: 100 }, + }))).toBe(100); }); }); From 90c7316b5721a759729f58339ffe7aa3d5fb50d5 Mon Sep 17 00:00:00 2001 From: Yumi Date: Sat, 5 Sep 2026 19:48:27 -0600 Subject: [PATCH 2/3] fix(gui): match subscription credit window exactly Co-authored-by: SB Yoon <44089734+yansigit@users.noreply.github.com> --- gui/src/components/QuotaBars.tsx | 6 ++++-- tests/gui/quota-bars-rows.test.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/gui/src/components/QuotaBars.tsx b/gui/src/components/QuotaBars.tsx index e840433a25..f16b0189d4 100644 --- a/gui/src/components/QuotaBars.tsx +++ b/gui/src/components/QuotaBars.tsx @@ -98,8 +98,10 @@ export function buildQuotaRows(quota: AccountQuota | null, plan: string | null | }); } if (displayQuota.creditsUsd && typeof displayQuota.creditsUsd.percent === "number") { - const hasCreditCustom = displayQuota.customWindows?.some(w => /credits?/i.test(w.label)); - if (!hasCreditCustom) { + const hasSubscriptionCreditsCustom = displayQuota.customWindows?.some( + w => w.label.trim().toLowerCase() === "total subscription credits", + ); + if (!hasSubscriptionCreditsCustom) { const localized = localizeCustomQuotaLabel("Total subscription credits", t); ranked.push({ rank: rawCustomWindowRank("Total subscription credits"), diff --git a/tests/gui/quota-bars-rows.test.ts b/tests/gui/quota-bars-rows.test.ts index 64c14869a4..cf1d7a55c6 100644 --- a/tests/gui/quota-bars-rows.test.ts +++ b/tests/gui/quota-bars-rows.test.ts @@ -92,6 +92,14 @@ describe("buildQuotaRows (WP070)", () => { expect(rows.map(r => r.label)).toEqual(["quota.totalSubscriptionCredits"]); }); + test("unrelated credit windows do not suppress direct subscription credits", () => { + const rows = buildQuotaRows(quota({ + customWindows: [{ label: "API credits", percent: 20 }], + creditsUsd: { used: 50, limit: 100, remaining: 50, percent: 50 }, + }), null, t); + expect(rows.map(r => r.label)).toEqual(["quota.totalSubscriptionCredits", "API credits"]); + }); + test("null and empty quotas produce no rows; 30-day plans strip to monthly", () => { expect(buildQuotaRows(null, null, t)).toEqual([]); expect(buildQuotaRows(quota({}), null, t)).toEqual([]); From fdc238e1c19b45113074e1f61406e9101a50058c Mon Sep 17 00:00:00 2001 From: Yumi Date: Sat, 5 Sep 2026 20:21:36 -0600 Subject: [PATCH 3/3] fix(gui): align subscription credit identity Co-authored-by: SB Yoon <44089734+yansigit@users.noreply.github.com> --- gui/src/components/QuotaBars.tsx | 29 +++++++++++++++++++++-------- tests/gui/quota-bars-rows.test.ts | 10 +++++++++- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/gui/src/components/QuotaBars.tsx b/gui/src/components/QuotaBars.tsx index f16b0189d4..7b485eca71 100644 --- a/gui/src/components/QuotaBars.tsx +++ b/gui/src/components/QuotaBars.tsx @@ -30,6 +30,15 @@ function rawCustomWindowRank(rawLabel: string): number { return 5; } +const SUBSCRIPTION_CREDITS_LABEL = "Total subscription credits"; + +function canonicalCustomWindowLabel(rawLabel: string): string { + const trimmed = rawLabel.trim(); + return trimmed.toLowerCase() === SUBSCRIPTION_CREDITS_LABEL.toLowerCase() + ? SUBSCRIPTION_CREDITS_LABEL + : trimmed; +} + function localizeCustomQuotaLabel(rawLabel: string, t: TFn): string { switch (rawLabel) { case "First-party models": @@ -85,11 +94,12 @@ export function buildQuotaRows(quota: AccountQuota | null, plan: string | null | }); } for (const w of displayQuota.customWindows ?? []) { - const localized = localizeCustomQuotaLabel(w.label, t); + const customLabel = canonicalCustomWindowLabel(w.label); + const localized = localizeCustomQuotaLabel(customLabel, t); ranked.push({ - rank: rawCustomWindowRank(w.label), + rank: rawCustomWindowRank(customLabel), row: { - customLabel: w.label, + customLabel, label: localized, limitLabel: localized, percent: w.percent, @@ -99,14 +109,14 @@ export function buildQuotaRows(quota: AccountQuota | null, plan: string | null | } if (displayQuota.creditsUsd && typeof displayQuota.creditsUsd.percent === "number") { const hasSubscriptionCreditsCustom = displayQuota.customWindows?.some( - w => w.label.trim().toLowerCase() === "total subscription credits", + w => canonicalCustomWindowLabel(w.label) === SUBSCRIPTION_CREDITS_LABEL, ); if (!hasSubscriptionCreditsCustom) { - const localized = localizeCustomQuotaLabel("Total subscription credits", t); + const localized = localizeCustomQuotaLabel(SUBSCRIPTION_CREDITS_LABEL, t); ranked.push({ - rank: rawCustomWindowRank("Total subscription credits"), + rank: rawCustomWindowRank(SUBSCRIPTION_CREDITS_LABEL), row: { - customLabel: "Total subscription credits", + customLabel: SUBSCRIPTION_CREDITS_LABEL, label: localized, limitLabel: localized, percent: displayQuota.creditsUsd.percent, @@ -126,7 +136,10 @@ export function maxQuotaUtilisation(quota: AccountQuota | null): number { for (const w of quota.customWindows ?? []) { if (typeof w.percent === "number") vals.push(w.percent); } - if (typeof quota.creditsUsd?.percent === "number") { + const hasSubscriptionCreditsCustom = quota.customWindows?.some( + w => canonicalCustomWindowLabel(w.label) === SUBSCRIPTION_CREDITS_LABEL, + ); + if (!hasSubscriptionCreditsCustom && typeof quota.creditsUsd?.percent === "number") { vals.push(quota.creditsUsd.percent); } return vals.length ? Math.max(...vals) : -1; diff --git a/tests/gui/quota-bars-rows.test.ts b/tests/gui/quota-bars-rows.test.ts index cf1d7a55c6..e4d0f0a24e 100644 --- a/tests/gui/quota-bars-rows.test.ts +++ b/tests/gui/quota-bars-rows.test.ts @@ -86,10 +86,11 @@ describe("buildQuotaRows (WP070)", () => { test("direct creditsUsd does not duplicate an existing credits custom window", () => { const rows = buildQuotaRows(quota({ - customWindows: [{ label: "Total subscription credits", percent: 50 }], + customWindows: [{ label: " TOTAL SUBSCRIPTION CREDITS ", percent: 50 }], creditsUsd: { used: 50, limit: 100, remaining: 50, percent: 50 }, }), null, t); expect(rows.map(r => r.label)).toEqual(["quota.totalSubscriptionCredits"]); + expect(rows[0]?.customLabel).toBe("Total subscription credits"); }); test("unrelated credit windows do not suppress direct subscription credits", () => { @@ -130,6 +131,13 @@ describe("maxQuotaUtilisation", () => { creditsUsd: { used: 90, limit: 90, remaining: 0, percent: 100 }, }))).toBe(100); }); + + test("subscription-credit urgency follows the visible canonical custom window", () => { + expect(maxQuotaUtilisation(quota({ + customWindows: [{ label: " total subscription credits ", percent: 25 }], + creditsUsd: { used: 99, limit: 100, remaining: 1, percent: 99 }, + }))).toBe(25); + }); }); describe("barWidth", () => {