Files
Cli-Proxy-API-Management-Ce…/src/pages/QuotaPage.tsx
Lany798 f77cbad98e feat: add Kiro (AWS CodeWhisperer) quota display support
- Add Kiro quota types (KiroFreeTrialInfo, KiroUsageBreakdown, KiroQuotaPayload, KiroQuotaState)
- Add Kiro API constants and request headers
- Add isKiroFile validator and parseKiroQuotaPayload parser
- Add KIRO_CONFIG with fetchKiroQuota and renderKiroItems
- Add kiroQuota state to useQuotaStore
- Add Kiro QuotaSection to QuotaPage
- Add Kiro styles (.kiroGrid, .kiroControls, .kiroControl, .kiroCard)
- Add i18n translations for kiro_quota (zh-CN and en)

Features:
- Separate display for base credits and bonus credits (freeTrialInfo)
- Total credits summary
- Correct parsing of seconds timestamp (scientific notation like 1.769904E9)
- Reset time display
- Subscription type display
2026-01-31 23:47:46 +08:00

100 lines
2.7 KiB
TypeScript

/**
* Quota management page - coordinates the four quota sections.
*/
import { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHeaderRefresh } from '@/hooks/useHeaderRefresh';
import { useAuthStore } from '@/stores';
import { authFilesApi, configFileApi } from '@/services/api';
import {
QuotaSection,
ANTIGRAVITY_CONFIG,
CODEX_CONFIG,
GEMINI_CLI_CONFIG,
KIRO_CONFIG
} from '@/components/quota';
import type { AuthFileItem } from '@/types';
import styles from './QuotaPage.module.scss';
export function QuotaPage() {
const { t } = useTranslation();
const connectionStatus = useAuthStore((state) => state.connectionStatus);
const [files, setFiles] = useState<AuthFileItem[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const disableControls = connectionStatus !== 'connected';
const loadConfig = useCallback(async () => {
try {
await configFileApi.fetchConfigYaml();
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : t('notification.refresh_failed');
setError((prev) => prev || errorMessage);
}
}, [t]);
const loadFiles = useCallback(async () => {
setLoading(true);
setError('');
try {
const data = await authFilesApi.list();
setFiles(data?.files || []);
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : t('notification.refresh_failed');
setError(errorMessage);
} finally {
setLoading(false);
}
}, [t]);
const handleHeaderRefresh = useCallback(async () => {
await Promise.all([loadConfig(), loadFiles()]);
}, [loadConfig, loadFiles]);
useHeaderRefresh(handleHeaderRefresh);
useEffect(() => {
loadFiles();
loadConfig();
}, [loadFiles, loadConfig]);
return (
<div className={styles.container}>
<div className={styles.pageHeader}>
<h1 className={styles.pageTitle}>{t('quota_management.title')}</h1>
<p className={styles.description}>{t('quota_management.description')}</p>
</div>
{error && <div className={styles.errorBox}>{error}</div>}
<QuotaSection
config={ANTIGRAVITY_CONFIG}
files={files}
loading={loading}
disabled={disableControls}
/>
<QuotaSection
config={KIRO_CONFIG}
files={files}
loading={loading}
disabled={disableControls}
/>
<QuotaSection
config={CODEX_CONFIG}
files={files}
loading={loading}
disabled={disableControls}
/>
<QuotaSection
config={GEMINI_CLI_CONFIG}
files={files}
loading={loading}
disabled={disableControls}
/>
</div>
);
}