import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/Button'; import { LoadingSpinner } from '@/components/ui/LoadingSpinner'; import { SelectionCheckbox } from '@/components/ui/SelectionCheckbox'; import { ToggleSwitch } from '@/components/ui/ToggleSwitch'; import { IconBot, IconCode, IconDownload, IconInfo, IconTrash2, } from '@/components/ui/icons'; import { ProviderStatusBar } from '@/components/providers/ProviderStatusBar'; import type { AuthFileItem } from '@/types'; import { resolveAuthProvider } from '@/utils/quota'; import { calculateStatusBarData, normalizeAuthIndex, type KeyStats } from '@/utils/usage'; import { formatFileSize } from '@/utils/format'; import { QUOTA_PROVIDER_TYPES, formatModified, getAuthFileStatusMessage, getTypeColor, getTypeLabel, isRuntimeOnlyAuthFile, parsePriorityValue, resolveAuthFileStats, type QuotaProviderType, type ResolvedTheme, } from '@/features/authFiles/constants'; import type { AuthFileStatusBarData } from '@/features/authFiles/hooks/useAuthFilesStatusBarCache'; import { AuthFileQuotaSection } from '@/features/authFiles/components/AuthFileQuotaSection'; import styles from '@/pages/AuthFilesPage.module.scss'; const HEALTHY_STATUS_MESSAGES = new Set(['ok', 'healthy', 'ready', 'success', 'available']); export type AuthFileCardProps = { file: AuthFileItem; selected: boolean; resolvedTheme: ResolvedTheme; disableControls: boolean; deleting: string | null; statusUpdating: Record; quotaFilterType: QuotaProviderType | null; keyStats: KeyStats; statusBarCache: Map; onShowModels: (file: AuthFileItem) => void; onShowDetails: (file: AuthFileItem) => void; onDownload: (name: string) => void; onOpenPrefixProxyEditor: (file: AuthFileItem) => void; onDelete: (name: string) => void; onToggleStatus: (file: AuthFileItem, enabled: boolean) => void; onToggleSelect: (name: string) => void; }; const resolveQuotaType = (file: AuthFileItem): QuotaProviderType | null => { const provider = resolveAuthProvider(file); if (!QUOTA_PROVIDER_TYPES.has(provider as QuotaProviderType)) return null; return provider as QuotaProviderType; }; export function AuthFileCard(props: AuthFileCardProps) { const { t } = useTranslation(); const { file, selected, resolvedTheme, disableControls, deleting, statusUpdating, quotaFilterType, keyStats, statusBarCache, onShowModels, onShowDetails, onDownload, onOpenPrefixProxyEditor, onDelete, onToggleStatus, onToggleSelect, } = props; const fileStats = resolveAuthFileStats(file, keyStats); const isRuntimeOnly = isRuntimeOnlyAuthFile(file); const isAistudio = (file.type || '').toLowerCase() === 'aistudio'; const showModelsButton = !isRuntimeOnly || isAistudio; const typeColor = getTypeColor(file.type || 'unknown', resolvedTheme); const quotaType = quotaFilterType && resolveQuotaType(file) === quotaFilterType ? quotaFilterType : null; const showQuotaLayout = Boolean(quotaType) && !isRuntimeOnly; const providerCardClass = quotaType === 'antigravity' ? styles.antigravityCard : quotaType === 'claude' ? styles.claudeCard : quotaType === 'codex' ? styles.codexCard : quotaType === 'gemini-cli' ? styles.geminiCliCard : quotaType === 'kimi' ? styles.kimiCard : ''; const rawAuthIndex = file['auth_index'] ?? file.authIndex; const authIndexKey = normalizeAuthIndex(rawAuthIndex); const statusData = (authIndexKey && statusBarCache.get(authIndexKey)) || calculateStatusBarData([]); const rawStatusMessage = getAuthFileStatusMessage(file); const hasStatusWarning = Boolean(rawStatusMessage) && !HEALTHY_STATUS_MESSAGES.has(rawStatusMessage.toLowerCase()); const priorityValue = parsePriorityValue(file.priority ?? file['priority']); const noteValue = typeof file.note === 'string' ? file.note.trim() : ''; return (
{!isRuntimeOnly && ( onToggleSelect(file.name)} aria-label={ selected ? t('auth_files.batch_deselect') : t('auth_files.batch_select_all') } title={selected ? t('auth_files.batch_deselect') : t('auth_files.batch_select_all')} /> )} {getTypeLabel(t, file.type || 'unknown')} {file.name}
{t('auth_files.file_size')}: {file.size ? formatFileSize(file.size) : '-'} {t('auth_files.file_modified')}: {formatModified(file)} {priorityValue !== undefined && ( {t('auth_files.priority_display')}: {priorityValue} )}
{noteValue && (
{t('auth_files.note_display')}: {noteValue}
)} {rawStatusMessage && hasStatusWarning && (
{rawStatusMessage}
)}
{t('stats.success')}: {fileStats.success} {t('stats.failure')}: {fileStats.failure}
{showQuotaLayout && quotaType && ( )}
{showModelsButton && ( )} {!isRuntimeOnly && ( <> )} {!isRuntimeOnly && (
onToggleStatus(file, value)} />
)} {isRuntimeOnly && (
{t('auth_files.type_virtual') || '虚拟认证文件'}
)}
); }