feat(quota): show OAuth token expiry on provider cards (small, blue, informative) (#3178)

Show OAuth token expiry on provider cards (small, blue, informative). Integrated into release/v3.8.10.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-04 18:33:34 -03:00
committed by GitHub
parent 42df1703c6
commit 3ac40829c0
4 changed files with 84 additions and 1 deletions

View File

@@ -4,7 +4,8 @@ import { useTranslations } from "next-intl";
import Badge from "@/shared/components/Badge";
import ProviderIcon from "@/shared/components/ProviderIcon";
import { pickDisplayValue } from "@/shared/utils/maskEmail";
import { STATUS_EMOJI, type CardStatus } from "../utils";
import { STATUS_EMOJI, formatCountdown, type CardStatus } from "../utils";
import { translateUsageOrFallback } from "../i18nFallback";
interface Props {
connection: any;
@@ -41,6 +42,27 @@ export default function QuotaCardHeader({
connection.provider
);
// OAuth token expiry — informative only. Shown small/blue for connections that
// expose a concrete token expiry (e.g. Codex), so an operator can see at a
// glance when the access token rotates. Hidden for API-key / no-expiry connections.
const tokenExpiryIso =
connection.authType === "oauth"
? connection.tokenExpiresAt || connection.expiresAt || null
: null;
const tokenExpiryMs = tokenExpiryIso ? new Date(tokenExpiryIso).getTime() : NaN;
const hasTokenExpiry = Number.isFinite(tokenExpiryMs);
const tokenCountdown = hasTokenExpiry ? formatCountdown(tokenExpiryIso) : null;
const tokenExpiryLabel = !hasTokenExpiry
? null
: tokenCountdown
? translateUsageOrFallback(t, "tokenExpiresIn", `Token expires in ${tokenCountdown}`, {
time: tokenCountdown,
})
: translateUsageOrFallback(t, "tokenExpired", "Token expired");
const tokenExpiryTitle = hasTokenExpiry
? new Date(tokenExpiryMs).toLocaleString()
: undefined;
return (
<div className="flex items-start justify-between gap-2 px-3 pt-2.5 pb-1.5">
<div className="flex items-start gap-2 min-w-0 flex-1">
@@ -85,6 +107,14 @@ export default function QuotaCardHeader({
<span className="text-[11px] text-text-muted truncate" title={accountName ?? ""}>
{accountName}
</span>
{tokenExpiryLabel && (
<span
className={`text-[10px] truncate ${tokenCountdown ? "text-sky-500" : "text-rose-500"}`}
title={tokenExpiryTitle}
>
{tokenExpiryLabel}
</span>
)}
</div>
</div>
<div className="flex items-center gap-0.5 shrink-0">

View File

@@ -6289,6 +6289,8 @@
"notApplicable": "N/A",
"rawPlanWithValue": "Raw plan: {plan}",
"noPlanFromProvider": "No plan from provider",
"tokenExpiresIn": "Token expires in {time}",
"tokenExpired": "Token expired",
"noQuotaData": "No quota data",
"cardExpand": "Expand",
"cardCollapse": "Collapse",

View File

@@ -7611,6 +7611,8 @@
"notApplicable": "N/D",
"rawPlanWithValue": "Plano bruto: {plan}",
"noPlanFromProvider": "Sem plano do provedor",
"tokenExpiresIn": "Token expira em {time}",
"tokenExpired": "Token expirado",
"noQuotaData": "Sem dados de cota",
"cardExpand": "Expandir",
"cardCollapse": "Recolher",

View File

@@ -0,0 +1,49 @@
/**
* The quota card shows an informative (small, blue) OAuth token-expiry line for
* connections that expose a concrete token expiry (e.g. Codex), and an expired
* marker otherwise. API-key / no-expiry connections show nothing.
*/
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const headerPath = path.join(
repoRoot,
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/parts/QuotaCardHeader.tsx"
);
const source = fs.readFileSync(headerPath, "utf8");
test("QuotaCardHeader derives token expiry only for OAuth connections with a known expiry", () => {
assert.match(source, /connection\.authType === "oauth"/, "must gate on oauth auth type");
assert.match(
source,
/connection\.tokenExpiresAt \|\| connection\.expiresAt/,
"must read tokenExpiresAt with expiresAt fallback"
);
assert.match(source, /formatCountdown\(/, "must format the countdown");
});
test("QuotaCardHeader renders the expiry as a small blue (sky) informative line", () => {
assert.match(source, /text-\[10px\]/, "expiry line must be small (10px)");
assert.match(source, /text-sky-500/, "active expiry must be blue (sky-500)");
assert.match(source, /tokenExpiresIn/, "must use the tokenExpiresIn i18n key");
assert.match(source, /tokenExpired/, "must use the tokenExpired i18n key");
});
test("token expiry i18n keys exist in en and pt-BR", () => {
for (const locale of ["en", "pt-BR"]) {
const msgs = JSON.parse(
fs.readFileSync(path.join(repoRoot, `src/i18n/messages/${locale}.json`), "utf8")
);
assert.ok(msgs.usage?.tokenExpiresIn, `${locale}: usage.tokenExpiresIn must exist`);
assert.ok(msgs.usage?.tokenExpired, `${locale}: usage.tokenExpired must exist`);
assert.match(
msgs.usage.tokenExpiresIn,
/\{time\}/,
`${locale}: tokenExpiresIn must interpolate {time}`
);
}
});