feat(quota): optimize quota lookup list layout

This commit is contained in:
lzt404 committed 2026-10-04 19:57:22 +08:00
1 parent eab80af225
commit 3c5114ebbb
2 files changed
+75 -1

No files matched your search

+68
View File
@@ -21,3 +21,71 @@
line-height: 1.4;
font-variant-numeric: tabular-nums;
}
/* Dense account list inspired by the quota overview reference. */
.quota-page .quota-group-list { gap: 14px; }
.quota-page .quota-table-head {
display: grid;
grid-template-columns: minmax(220px, 1.35fr) minmax(100px, .65fr) minmax(110px, .7fr) minmax(280px, 2fr) minmax(112px, .7fr);
align-items: center;
gap: 18px;
padding: 0 18px 6px;
color: var(--theme-7a746c);
font-size: var(--font-size-meta);
font-weight: var(--font-weight-semibold);
}
.quota-page .quota-provider-group { gap: 7px; }
.quota-page .quota-group-heading { min-height: 24px; padding: 0 3px; }
.quota-page .quota-group-heading h2 { font-size: var(--font-size-label); color: var(--theme-7a746c); }
.quota-page .quota-group-heading .provider-logo { width: 18px; height: 18px; }
.quota-page .quota-provider-count { padding: 2px 7px; font-size: 11px; }
.quota-page .real-quota-grid { display: grid; gap: 8px; }
.quota-page .real-quota-card {
display: grid;
grid-template-columns: minmax(220px, 1.35fr) minmax(100px, .65fr) minmax(110px, .7fr) minmax(280px, 2fr) minmax(112px, .7fr);
align-items: center;
gap: 14px 18px;
min-height: 104px;
padding: 14px 18px;
border: 1px solid var(--border-color);
border-radius: 9px;
background: var(--bg-card);
box-shadow: 0 1px 2px rgb(40 45 55 / 3%);
}
.quota-page .real-quota-card:hover { border-color: #b7c9ed; box-shadow: 0 3px 12px rgb(55 95 180 / 8%); }
.quota-page .real-quota-card-header { display: contents; }
.quota-page .quota-identity { display: flex; align-items: center; gap: 10px; min-width: 0; }
.quota-page .quota-identity > div { display: grid; gap: 4px; min-width: 0; }
.quota-page .quota-account-icon { width: 30px; height: 30px; flex: 0 0 auto; }
.quota-page .real-quota-card-header strong { color: var(--theme-2d2a26); font-size: var(--font-size-body); }
.quota-page .real-quota-card-header span { font-size: var(--font-size-meta); }
.quota-page .quota-plan, .quota-page .quota-status { display: grid; gap: 4px; min-width: 0; }
.quota-page .quota-plan strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.quota-page .quota-plan span, .quota-page .quota-status small { color: var(--theme-7a746c); font-size: var(--font-size-meta); }
.quota-page .quota-status-badge { width: fit-content; padding: 3px 8px; border-radius: 6px; background: var(--theme-f4f1ea); color: var(--theme-7a746c); font-size: var(--font-size-meta); font-weight: var(--font-weight-semibold); }
.quota-page .quota-status-badge.ready { background: #e8f8ee; color: #149447; }
.quota-page .quota-status-badge.error { background: #fff0ee; color: #d94b42; }
.quota-page .quota-row-list { grid-column: 4; gap: 9px; min-width: 0; }
.quota-page .real-quota-row { gap: 5px; }
.quota-page .real-quota-row > div:first-child { font-size: var(--font-size-meta); }
.quota-page .real-quota-track { height: 7px; background: #edf0f4; }
.quota-page .real-quota-track span { background: #20bf62; }
.quota-page .real-quota-row small { font-size: 11px; }
.quota-page .quota-card-actions { grid-column: 5; grid-row: 1; justify-content: flex-end; align-self: center; flex-wrap: wrap; }
.quota-page .quota-card-actions .icon-button { width: 30px; height: 30px; border-radius: 7px; background: #eef4ff; color: #347df4; }
.quota-page .quota-card-message, .quota-page .quota-card-error, .quota-page .quota-action-feedback, .quota-page .quota-reset-credit-summary { grid-column: 1 / -1; }
.quota-page .quota-card-message { min-height: 36px; }
.quota-page .quota-card-error { min-height: 36px; }
@media (max-width: 980px) {
.quota-page .quota-table-head { display: none; }
.quota-page .real-quota-card { grid-template-columns: minmax(180px, 1fr) minmax(100px, .7fr) minmax(100px, .7fr) 38px; }
.quota-page .quota-row-list { grid-column: 1 / -1; }
.quota-page .quota-card-actions { grid-column: 4; }
}
@media (max-width: 640px) {
.quota-page .real-quota-card { grid-template-columns: 1fr auto; padding: 13px; }
.quota-page .quota-plan, .quota-page .quota-status { grid-column: 1 / -1; }
.quota-page .quota-card-actions { grid-column: 2; grid-row: 1; }
.quota-page .quota-row-list { grid-column: 1 / -1; }
}
+7 -1
View File
@@ -166,6 +166,7 @@ export function QuotaPage() {
<div className="management-empty"><AlertCircle size={24} /><strong>{t('quota.empty.title')}</strong><span>{t('quota.empty.description')}</span></div>
) : (
<div className="quota-group-list">
<div className="quota-table-head" aria-hidden="true"><span>{t('authFiles.list.credential')}</span><span>{t('authFiles.list.plan')}</span><span>{t('authFiles.list.status')}</span><span>{t('authFiles.list.quota')}</span><span>{t('authFiles.list.actions')}</span></div>
{grouped.map(([provider, items]) => (
<section className="quota-provider-group" key={provider}>
<div className="quota-group-heading">
@@ -193,7 +194,12 @@ export function QuotaCard({ file, quota, onRefresh, onReset }: { file: AuthFile;
return (
<article className="panel real-quota-card">
<div className="real-quota-card-header">
<div><strong title={name}>{name}</strong><span>{provider ? providerMeta[provider].label : t('quota.unknownProvider')}{quota.plan ? ' · ' + quota.plan : ''}</span></div>
<div className="quota-identity">
<img src={provider ? providerMeta[provider].icon : ''} alt="" className="quota-account-icon" />
<div><strong title={name}>{name}</strong><span>{provider ? providerMeta[provider].label : t('quota.unknownProvider')}</span></div>
</div>
<div className="quota-plan"><strong>{quota.plan || '—'}</strong><span>{quota.subscriptionActiveUntil ? formatQuotaTimestamp(quota.subscriptionActiveUntil, locale) : ''}</span></div>
<div className="quota-status"><span className={quota.status === 'success' ? 'quota-status-badge ready' : quota.status === 'error' ? 'quota-status-badge error' : 'quota-status-badge'}>{quota.status === 'success' ? t('common.enabled') : quota.status === 'loading' ? t('quota.querying') : quota.status === 'error' ? t('common.unavailable') : t('quota.notFetched')}</span><small>{t('authFiles.settings.priority')} 0</small></div>
<div className="quota-card-actions">
{onReset && (quota.resetCredits ?? 0) > 0 ? <button type="button" className="secondary-button compact-button" onClick={onReset} disabled={!canResetCodexQuota(file, quota)} title={t('quota.reset')}>{t('quota.reset')}</button> : null}
<button type="button" className="icon-button quiet" onClick={onRefresh} disabled={disabled || quota.status === 'loading'} title={disabled ? t('quota.fileDisabled') : t('quota.refresh')} aria-label={disabled ? t('quota.fileDisabled') : t('quota.refresh')}><RefreshCw size={16} className={quota.status === 'loading' ? 'spin' : ''} aria-hidden="true" /></button>