feat(quota): add quota page and update i18n

This commit is contained in:
Supra4E8C
2025-12-30 14:13:04 +08:00
parent 4e26b6c92d
commit 49b9259452
7 changed files with 2150 additions and 1666 deletions

View File

@@ -7,6 +7,7 @@ import { ApiKeysPage } from '@/pages/ApiKeysPage';
import { AiProvidersPage } from '@/pages/AiProvidersPage';
import { AuthFilesPage } from '@/pages/AuthFilesPage';
import { OAuthPage } from '@/pages/OAuthPage';
import { QuotaPage } from '@/pages/QuotaPage';
import { UsagePage } from '@/pages/UsagePage';
import { ConfigPage } from '@/pages/ConfigPage';
import { LogsPage } from '@/pages/LogsPage';
@@ -88,6 +89,7 @@ function App() {
<Route path="ai-providers" element={<AiProvidersPage />} />
<Route path="auth-files" element={<AuthFilesPage />} />
<Route path="oauth" element={<OAuthPage />} />
<Route path="quota" element={<QuotaPage />} />
<Route path="usage" element={<UsagePage />} />
<Route path="config" element={<ConfigPage />} />
<Route path="logs" element={<LogsPage />} />

View File

@@ -23,6 +23,7 @@ import {
IconSettings,
IconShield,
IconSlidersHorizontal,
IconTimer,
} from '@/components/ui/icons';
import { INLINE_LOGO_JPEG } from '@/assets/logoInline';
import {
@@ -41,6 +42,7 @@ const sidebarIcons: Record<string, ReactNode> = {
aiProviders: <IconBot size={18} />,
authFiles: <IconFileText size={18} />,
oauth: <IconShield size={18} />,
quota: <IconTimer size={18} />,
usage: <IconChartLine size={18} />,
config: <IconSettings size={18} />,
logs: <IconScrollText size={18} />,
@@ -355,6 +357,7 @@ export function MainLayout() {
{ path: '/ai-providers', label: t('nav.ai_providers'), icon: sidebarIcons.aiProviders },
{ path: '/auth-files', label: t('nav.auth_files'), icon: sidebarIcons.authFiles },
{ path: '/oauth', label: t('nav.oauth', { defaultValue: 'OAuth' }), icon: sidebarIcons.oauth },
{ path: '/quota', label: t('nav.quota_management'), icon: sidebarIcons.quota },
{ path: '/usage', label: t('nav.usage_stats'), icon: sidebarIcons.usage },
{ path: '/config', label: t('nav.config_management'), icon: sidebarIcons.config },
...(config?.loggingToFile

View File

@@ -90,6 +90,7 @@
"ai_providers": "AI Providers",
"auth_files": "Auth Files",
"oauth": "OAuth Login",
"quota_management": "Quota Management",
"usage_stats": "Usage Statistics",
"config_management": "Config Management",
"logs": "Logs Viewer",
@@ -704,6 +705,11 @@
"search_prev": "Previous",
"search_next": "Next"
},
"quota_management": {
"title": "Quota Management",
"description": "Monitor OAuth quota status for Antigravity, Codex, and Gemini CLI credentials.",
"refresh_files": "Refresh auth files"
},
"system_info": {
"title": "Management Center Info",
"connection_status_title": "Connection Status",

View File

@@ -90,6 +90,7 @@
"ai_providers": "AI 提供商",
"auth_files": "认证文件",
"oauth": "OAuth 登录",
"quota_management": "配额管理",
"usage_stats": "使用统计",
"config_management": "配置管理",
"logs": "日志查看",
@@ -704,6 +705,11 @@
"search_prev": "上一个",
"search_next": "下一个"
},
"quota_management": {
"title": "配额管理",
"description": "集中查看 OAuth 额度与剩余情况",
"refresh_files": "刷新认证文件"
},
"system_info": {
"title": "管理中心信息",
"connection_status_title": "连接状态",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,333 @@
@use '../styles/variables' as *;
@use '../styles/mixins' as *;
.container {
display: flex;
flex-direction: column;
gap: $spacing-lg;
}
.pageHeader {
display: flex;
flex-direction: column;
gap: $spacing-sm;
}
.pageTitle {
font-size: 28px;
font-weight: 700;
color: var(--text-primary);
margin: 0;
}
.description {
font-size: 14px;
color: var(--text-secondary);
margin: 0;
}
.headerActions {
display: flex;
gap: $spacing-sm;
flex-wrap: wrap;
}
.errorBox {
padding: $spacing-md;
background-color: rgba(239, 68, 68, 0.1);
border: 1px solid var(--danger-color);
border-radius: $radius-md;
color: var(--danger-color);
font-size: 14px;
}
.pageSizeSelect {
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: $radius-md;
background-color: var(--bg-primary);
color: var(--text-primary);
font-size: 14px;
cursor: pointer;
height: 38px;
box-sizing: border-box;
&:focus {
outline: none;
border-color: var(--primary-color);
}
}
.statsInfo {
padding: 8px 12px;
background-color: var(--bg-secondary);
border-radius: $radius-md;
font-size: 13px;
color: var(--text-secondary);
white-space: nowrap;
height: 38px;
box-sizing: border-box;
display: flex;
align-items: center;
}
.antigravityGrid,
.codexGrid,
.geminiCliGrid {
display: grid;
gap: $spacing-md;
grid-template-columns: repeat(3, minmax(0, 1fr));
@include tablet {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@include mobile {
grid-template-columns: 1fr;
}
}
.antigravityControls,
.codexControls,
.geminiCliControls {
display: flex;
gap: $spacing-md;
flex-wrap: wrap;
align-items: flex-end;
margin-bottom: $spacing-md;
}
.antigravityControl,
.codexControl,
.geminiCliControl {
display: flex;
flex-direction: column;
gap: 4px;
label {
font-size: 12px;
color: var(--text-secondary);
font-weight: 500;
white-space: nowrap;
}
}
.antigravityCard {
background-image: linear-gradient(
180deg,
rgba(224, 247, 250, 0.12),
rgba(224, 247, 250, 0)
);
}
.codexCard {
background-image: linear-gradient(
180deg,
rgba(255, 243, 224, 0.18),
rgba(255, 243, 224, 0)
);
}
.geminiCliCard {
background-image: linear-gradient(
180deg,
rgba(231, 239, 255, 0.2),
rgba(231, 239, 255, 0)
);
}
.quotaSection {
display: flex;
flex-direction: column;
gap: $spacing-sm;
padding-top: $spacing-sm;
margin-top: $spacing-xs;
border-top: 1px dashed var(--border-color);
}
.quotaRow {
display: flex;
flex-direction: column;
gap: $spacing-xs;
}
.quotaRowHeader {
display: flex;
align-items: center;
justify-content: space-between;
gap: $spacing-sm;
min-width: 0;
@include mobile {
flex-direction: column;
align-items: flex-start;
}
}
.quotaModel {
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
min-width: 0;
@include mobile {
white-space: normal;
}
}
.quotaBar {
height: 8px;
background-color: var(--bg-tertiary);
border-radius: 999px;
overflow: hidden;
}
.quotaBarFill {
height: 100%;
background-color: var(--success-color, #22c55e);
transition: width 0.2s ease;
}
.quotaBarFillHigh {
background-color: var(--success-color, #22c55e);
}
.quotaBarFillMedium {
background-color: var(--warning-color, #f59e0b);
}
.quotaBarFillLow {
background-color: var(--danger-color, #ef4444);
}
.quotaMeta {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--text-secondary);
white-space: nowrap;
@include mobile {
justify-content: flex-start;
}
}
.quotaPercent {
font-weight: 600;
color: var(--text-primary);
}
.quotaReset {
color: var(--text-tertiary);
}
.quotaAmount {
color: var(--text-secondary);
}
.quotaMessage {
font-size: 12px;
color: var(--text-tertiary);
text-align: center;
padding: $spacing-sm 0;
}
.quotaError {
font-size: 12px;
color: var(--danger-color);
background-color: rgba(239, 68, 68, 0.08);
border: 1px solid var(--danger-color);
border-radius: $radius-sm;
padding: $spacing-xs $spacing-sm;
}
.quotaWarning {
font-size: 12px;
color: var(--warning-color, #f59e0b);
background-color: rgba(245, 158, 11, 0.12);
border: 1px solid var(--warning-color, #f59e0b);
border-radius: $radius-sm;
padding: $spacing-xs $spacing-sm;
}
.codexPlan {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--text-secondary);
}
.codexPlanLabel {
color: var(--text-tertiary);
}
.codexPlanValue {
font-weight: 600;
color: var(--text-primary);
text-transform: capitalize;
}
.fileCard {
background-color: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: $radius-lg;
padding: $spacing-md;
display: flex;
flex-direction: column;
gap: $spacing-sm;
transition: transform $transition-fast, box-shadow $transition-fast, border-color $transition-fast;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
border-color: rgba(37, 99, 235, 0.2);
}
}
.cardHeader {
display: flex;
align-items: center;
gap: $spacing-sm;
min-height: 28px;
}
.typeBadge {
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
white-space: nowrap;
flex-shrink: 0;
}
.fileName {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
word-break: break-all;
line-height: 1.4;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: $spacing-md;
margin-top: $spacing-lg;
padding-top: $spacing-md;
border-top: 1px solid var(--border-color);
}
.pageInfo {
font-size: 13px;
color: var(--text-secondary);
padding: $spacing-xs $spacing-md;
background-color: var(--bg-secondary);
border-radius: $radius-md;
}

1796
src/pages/QuotaPage.tsx Normal file

File diff suppressed because it is too large Load Diff