From c5d252bf7e26b6e872f6528470e54c8c7c0a2d8a Mon Sep 17 00:00:00 2001 From: agentHits Date: Thu, 6 Aug 2026 01:06:48 +0300 Subject: [PATCH] feat(providers): display account subscription / plan expiration date in dashboard (#1060) --- gui/src/components/codex-account-pool-cards.tsx | 15 +++++++++++++-- .../provider-workspace/ProviderAuthPanel.tsx | 11 ++++++++++- gui/src/components/provider-workspace/types.ts | 1 + gui/src/i18n/de.ts | 3 ++- gui/src/i18n/en.ts | 3 ++- gui/src/i18n/ja.ts | 3 ++- gui/src/i18n/ko.ts | 3 ++- gui/src/i18n/ru.ts | 3 ++- gui/src/i18n/zh.ts | 3 ++- src/codex/auth-api.ts | 3 +++ tests/oauth-accounts-api.test.ts | 1 + 11 files changed, 40 insertions(+), 9 deletions(-) diff --git a/gui/src/components/codex-account-pool-cards.tsx b/gui/src/components/codex-account-pool-cards.tsx index 8accb3d87..3fc37b8d5 100644 --- a/gui/src/components/codex-account-pool-cards.tsx +++ b/gui/src/components/codex-account-pool-cards.tsx @@ -1,4 +1,4 @@ -import { useT } from "../i18n/shared"; +import { useT, useI18n } from "../i18n/shared"; import { IconAlert, IconPause, IconPlay, IconX } from "../icons"; import { displayAccountId } from "../lib/privacy"; import type { CodexAccountEntry } from "./codex-account-pool-types"; @@ -49,6 +49,7 @@ export function CodexAccountPoolCards({ doctorCopyOutcomeFor?: (accountId: string) => "copied" | "unavailable" | null; }) { const t = useT(); + const { locale } = useI18n(); const isNext = (account: CodexAccountEntry) => !account.paused && activeId === account.id; return ( @@ -125,7 +126,16 @@ export function CodexAccountPoolCards({ -
{a.email}{a.plan ? ` · ${a.plan}` : ""} · {t("prov.accountId")}: {displayAccountId(a.id)}
+
{(() => { + const parts = [a.email, a.plan ? a.plan : "", `${t("prov.accountId")}: ${displayAccountId(a.id)}`].filter(Boolean); + if (typeof a.expiresAt === "number" && a.expiresAt > 0) { + const expDate = new Date(a.expiresAt); + if (!Number.isNaN(expDate.getTime())) { + parts.push(t("prov.expiresAt", { date: expDate.toLocaleDateString(locale, { month: "short", day: "numeric", year: "numeric" }) })); + } + } + return parts.join(" · "); + })()}
{healthSummary && (
{healthSummary}
)} @@ -156,6 +166,7 @@ export function CodexAccountPoolReauthBanner({ onReauth: () => void; }) { const t = useT(); + const { locale } = useI18n(); return (
{t("codexAuth.tokenExpired")} diff --git a/gui/src/components/provider-workspace/ProviderAuthPanel.tsx b/gui/src/components/provider-workspace/ProviderAuthPanel.tsx index 6a19c5f0d..fb1d8b42c 100644 --- a/gui/src/components/provider-workspace/ProviderAuthPanel.tsx +++ b/gui/src/components/provider-workspace/ProviderAuthPanel.tsx @@ -217,7 +217,16 @@ export default function ProviderAuthPanel({