import { Fragment, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/Button'; import { Card } from '@/components/ui/Card'; import { ToggleSwitch } from '@/components/ui/ToggleSwitch'; import iconClaude from '@/assets/icons/claude.svg'; import type { ProviderKeyConfig } from '@/types'; import { maskApiKey } from '@/utils/format'; import { buildCandidateUsageSourceIds, calculateStatusBarData, type KeyStats, type UsageDetail, } from '@/utils/usage'; import styles from '@/pages/AiProvidersPage.module.scss'; import { ProviderList } from '../ProviderList'; import { ProviderStatusBar } from '../ProviderStatusBar'; import { getStatsBySource, hasDisableAllModelsRule } from '../utils'; interface ClaudeSectionProps { configs: ProviderKeyConfig[]; keyStats: KeyStats; usageDetails: UsageDetail[]; loading: boolean; disableControls: boolean; isSwitching: boolean; onAdd: () => void; onEdit: (index: number) => void; onDelete: (index: number) => void; onToggle: (index: number, enabled: boolean) => void; } export function ClaudeSection({ configs, keyStats, usageDetails, loading, disableControls, isSwitching, onAdd, onEdit, onDelete, onToggle, }: ClaudeSectionProps) { const { t } = useTranslation(); const actionsDisabled = disableControls || loading || isSwitching; const toggleDisabled = disableControls || loading || isSwitching; const statusBarCache = useMemo(() => { const cache = new Map>(); configs.forEach((config) => { if (!config.apiKey) return; const candidates = buildCandidateUsageSourceIds({ apiKey: config.apiKey, prefix: config.prefix, }); if (!candidates.length) return; const candidateSet = new Set(candidates); const filteredDetails = usageDetails.filter((detail) => candidateSet.has(detail.source)); cache.set(config.apiKey, calculateStatusBarData(filteredDetails)); }); return cache; }, [configs, usageDetails]); return ( <> {t('ai_providers.claude_title')} } extra={ } > items={configs} loading={loading} keyField={(item) => item.apiKey} emptyTitle={t('ai_providers.claude_empty_title')} emptyDescription={t('ai_providers.claude_empty_desc')} onEdit={onEdit} onDelete={onDelete} actionsDisabled={actionsDisabled} getRowDisabled={(item) => hasDisableAllModelsRule(item.excludedModels)} renderExtraActions={(item, index) => ( void onToggle(index, value)} /> )} renderContent={(item) => { const stats = getStatsBySource(item.apiKey, keyStats, item.prefix); const headerEntries = Object.entries(item.headers || {}); const configDisabled = hasDisableAllModelsRule(item.excludedModels); const excludedModels = item.excludedModels ?? []; const statusData = statusBarCache.get(item.apiKey) || calculateStatusBarData([]); return (
{t('ai_providers.claude_item_title')}
{t('common.api_key')}: {maskApiKey(item.apiKey)}
{item.prefix && (
{t('common.prefix')}: {item.prefix}
)} {item.baseUrl && (
{t('common.base_url')}: {item.baseUrl}
)} {item.proxyUrl && (
{t('common.proxy_url')}: {item.proxyUrl}
)} {headerEntries.length > 0 && (
{headerEntries.map(([key, value]) => ( {key}: {value} ))}
)} {configDisabled && (
{t('ai_providers.config_disabled_badge')}
)} {item.models?.length ? (
{t('ai_providers.claude_models_count')}: {item.models.length} {item.models.map((model) => ( {model.name} {model.alias && model.alias !== model.name && ( {model.alias} )} ))}
) : null} {excludedModels.length ? (
{t('ai_providers.excluded_models_count', { count: excludedModels.length })}
{excludedModels.map((model) => ( {model} ))}
) : null}
{t('stats.success')}: {stats.success} {t('stats.failure')}: {stats.failure}
); }} />
); }