refactor(quota): simplify reset expiry display

- Remove redundant reset count and applicability labels
- Display each Codex and Claude reset expiry directly
- Improve reset expiry layout with clearer spacing and separators
- Update quota panel and quota page tests
This commit is contained in:
lzt404 committed 2026-10-05 00:45:17 +08:00
1 parent 1532bdb056
commit 7c3008dd81
8 files changed
+10 -19

No files matched your search

+1 -6
View File
@@ -24,12 +24,10 @@ export function AuthFileQuotaPanel({ quota, file, disabled, onRefresh, onReset,
const compactLayout = compact || dense;
const fileDisabled = readBoolean(file, 'disabled');
const showReset = Boolean(onReset && ((quota.resetCredits ?? 0) > 0 || hasPendingClaudeReset(file)));
const resetCreditsLabel = quota.resetCredits !== undefined ? t('authFiles.quota.resets', { count: quota.resetCredits }) : '';
const accountCreditsLabel = quota.creditsUnlimited
? t('quota.creditUnlimited')
: quota.creditBalance !== undefined ? quota.creditBalance : '';
const denseMetadata = dense ? [
quota.resetCreditsApplicable !== undefined ? t('quota.resetApplicable', { count: quota.resetCreditsApplicable }) : '',
quota.subscriptionActiveUntil ? t('quota.subscriptionExpiry', { time: formatQuotaTimestamp(quota.subscriptionActiveUntil, locale) }) : '',
accountCreditsLabel ? t('quota.creditBalance') + ': ' + accountCreditsLabel : '',
].filter(Boolean).join(' · ') : '';
@@ -52,10 +50,9 @@ export function AuthFileQuotaPanel({ quota, file, disabled, onRefresh, onReset,
};
return (
<section className={`credential-quota${compactLayout ? ' credential-quota-compact' : ''}${dense ? ' credential-quota-dense' : ''}`} aria-label={t('authFiles.quota.aria')} aria-busy={loading} title={denseMetadata || undefined}>
{!compactLayout || showReset || (dense && resetCreditsLabel) ? <div className="credential-quota-heading">
{!compactLayout || showReset ? <div className="credential-quota-heading">
{!compactLayout ? <strong>{t('authFiles.settings.quotaRemaining')}</strong> : null}
{!compactLayout && quota.plan ? <span className="credential-quota-plan">{quota.plan}</span> : null}
{dense && resetCreditsLabel ? <small className="credential-quota-reset-credits">{resetCreditsLabel}</small> : null}
{showReset ? <button type="button" className="secondary-button compact-button credential-quota-reset" onClick={onReset} disabled={disabled || !canResetQuota(file, quota)}
title={t('quota.reset')}>{t(hasPendingClaudeReset(file) ? 'quota.claude.retry' : 'quota.reset')}</button> : null}
{!compactLayout ? <button type="button" className="credential-quota-refresh" disabled={disabled || loading} onClick={onRefresh}
@@ -79,8 +76,6 @@ export function AuthFileQuotaPanel({ quota, file, disabled, onRefresh, onReset,
</details> : null}
</> : <p className="credential-quota-empty">{t('authFiles.quota.empty')}</p>}
{!dense ? <div className="credential-quota-footnotes">
{!dense && quota.resetCredits !== undefined ? <small>{resetCreditsLabel}</small> : null}
{quota.resetCreditsApplicable !== undefined ? <small>{t('quota.resetApplicable', { count: quota.resetCreditsApplicable })}</small> : null}
{quota.subscriptionActiveUntil ? <small>{t('quota.subscriptionExpiry', { time: formatQuotaTimestamp(quota.subscriptionActiveUntil, locale) })}</small> : null}
{accountCreditsLabel ? <small>{t('quota.creditBalance')}: {accountCreditsLabel}</small> : null}
+4 -4
View File
@@ -2,12 +2,12 @@
width: 100%;
min-width: 0;
display: grid;
gap: 4px;
gap: 0;
color: var(--text-secondary);
font-size: var(--font-size-meta);
}
.reset-credit-expiries > strong { font-size: inherit; font-weight: 500; }
.reset-credit-expiries ol { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
.reset-credit-expiries li { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 8px; }
.reset-credit-expiries ol { display: grid; gap: 5px; margin: 0; padding: 0; list-style: none; }
.reset-credit-expiries li { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: 8px; padding: 2px 0; }
.reset-credit-expiries li + li { border-top: 1px solid color-mix(in srgb, var(--border-color) 65%, transparent); }
.reset-credit-expiries li span { overflow-wrap: anywhere; }
.credential-quota-dense .reset-credit-expiries { font-size: 10px; line-height: 1.4; }
-1
View File
@@ -7,7 +7,6 @@ export function ResetCreditExpiries({ quota }: { quota: QuotaState }) {
const expiries = quotaResetExpiries(quota);
if (!expiries.length) return null;
return <div className="reset-credit-expiries">
<strong>{t('quota.resetCreditExpiries')}</strong>
<ol>{expiries.map((expiry, index) => <li key={`${expiry}-${index}`}>
<span>{t('quota.resetCreditExpiry', { index: index + 1 })}</span>
<span>{formatQuotaTimestamp(expiry, locale)}</span>
-2
View File
@@ -209,8 +209,6 @@ export function QuotaCard({ file, quota, onRefresh, onReset }: { file: AuthFile;
</> : null}
{quota.status === 'success' && (provider === 'codex' || provider === 'claude') ? <div className="quota-reset-credit-summary">
{(quota.creditsUnlimited || quota.creditBalance !== undefined) ? <span>{t('quota.creditBalance')} <strong>{quota.creditsUnlimited ? t('quota.creditUnlimited') : quota.creditBalance}</strong></span> : null}
<span>{t('quota.resetCredits')} <strong>{quota.resetCredits ?? '—'}</strong></span>
{quota.resetCreditsApplicable !== undefined ? <span>{t('quota.resetApplicable', { count: quota.resetCreditsApplicable })}</span> : null}
<ResetCreditExpiries quota={quota} />
<MessageNotice message={quota.resetCreditsError ? name + ': ' + t('quota.resetCreditsWarning', { error: quota.resetCreditsError }) : null} />
</div> : null}
+2 -2
View File
@@ -31,8 +31,8 @@ describe('compact credential panels', () => {
expect(beforeDetails).not.toContain('Extra model');
expect(compact).toMatch(/<details[^>]*><summary>Details \(\+1\)<\/summary>.*Extra model.*Additional quota detail.*<\/details>/);
expect(compact).toContain('title="Reset Quota"');
expect(compact).toContain('2 manual resets');
expect(compact).toContain('Currently applicable: 0');
expect(compact).not.toContain('2 manual resets');
expect(compact).not.toContain('Currently applicable: 0');
expect(compact).not.toContain('credential-quota-refresh');
expect(compact).not.toContain(quota.plan!);
+2 -2
View File
@@ -30,9 +30,9 @@ it('shows reset credits beside the button while preserving applicability and exp
});
const resetButton = html.match(/<button[^>]*credential-quota-reset[^>]*>[\s\S]*?<\/button>/)?.[0];
expect(resetButton).toContain('Reset');
expect(html).toMatch(/credential-quota-reset-credits[^>]*>[^<]*2/);
expect(html).not.toContain('Manual resets');
expect(resetButton).not.toContain('disabled=""');
expect(html).toContain('Currently applicable: 0');
expect(html).not.toContain('Currently applicable: 0');
expect(html).toContain('Subscription expires');
});
-1
View File
@@ -57,7 +57,6 @@ test('loads grant counts and renders a working reset action on both surfaces', a
]) {
expect(html).toContain('Reset Quota');
expect(html).not.toContain('disabled=""');
expect(html).toContain('2');
}
updateQuotaCache({ [quotaKey(file)]: quota });
expect(await resetQuotaWithConfirmation(file, async () => false)).toBe('cancelled');
+1 -1
View File
@@ -18,7 +18,7 @@ describe('quota card rendering', () => {
expect(html).toMatch(/<button[^>]*title="Reset Quota"[^>]*>Reset Quota<\/button>/);
expect(html).not.toMatch(/<button[^>]*disabled=""[^>]*>Reset Quota<\/button>/);
expect(html).not.toContain('No applicable reset credits');
expect(html).toContain('Currently applicable: 0');
expect(html).not.toContain('Currently applicable: 0');
expect(html).not.toContain('temporary failure');
expect(html).toContain('Expires 01/01/2030, 00:00');
expect(html).not.toContain('Subscription expires:');