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 iconGemini from '@/assets/icons/gemini.svg'; import type { GeminiKeyConfig } from '@/types'; import { maskApiKey } from '@/utils/format'; import { calculateStatusBarData, type KeyStats, type UsageDetail } from '@/utils/usage'; import styles from '@/pages/AiProvidersPage.module.scss'; import type { GeminiFormState } from '../types'; import { ProviderList } from '../ProviderList'; import { ProviderStatusBar } from '../ProviderStatusBar'; import { getStatsBySource, hasDisableAllModelsRule } from '../utils'; import { GeminiModal } from './GeminiModal'; interface GeminiSectionProps { configs: GeminiKeyConfig[]; keyStats: KeyStats; usageDetails: UsageDetail[]; loading: boolean; disableControls: boolean; isSaving: boolean; isSwitching: boolean; isModalOpen: boolean; modalIndex: number | null; onAdd: () => void; onEdit: (index: number) => void; onDelete: (index: number) => void; onToggle: (index: number, enabled: boolean) => void; onCloseModal: () => void; onSave: (data: GeminiFormState, index: number | null) => Promise; } export function GeminiSection({ configs, keyStats, usageDetails, loading, disableControls, isSaving, isSwitching, isModalOpen, modalIndex, onAdd, onEdit, onDelete, onToggle, onCloseModal, onSave, }: GeminiSectionProps) { const { t } = useTranslation(); const actionsDisabled = disableControls || isSaving || isSwitching; const toggleDisabled = disableControls || loading || isSaving || isSwitching; const statusBarCache = useMemo(() => { const cache = new Map>(); const allApiKeys = new Set(); configs.forEach((config) => config.apiKey && allApiKeys.add(config.apiKey)); allApiKeys.forEach((apiKey) => { cache.set(apiKey, calculateStatusBarData(usageDetails, apiKey)); }); return cache; }, [configs, usageDetails]); const initialData = modalIndex !== null ? configs[modalIndex] : undefined; return ( <> {t('ai_providers.gemini_title')} } extra={ } > items={configs} loading={loading} keyField={(item) => item.apiKey} emptyTitle={t('ai_providers.gemini_empty_title')} emptyDescription={t('ai_providers.gemini_empty_desc')} onEdit={onEdit} onDelete={onDelete} actionsDisabled={actionsDisabled} getRowDisabled={(item) => hasDisableAllModelsRule(item.excludedModels)} renderExtraActions={(item, index) => ( void onToggle(index, value)} /> )} renderContent={(item, index) => { const stats = getStatsBySource(item.apiKey, keyStats, maskApiKey); const headerEntries = Object.entries(item.headers || {}); const configDisabled = hasDisableAllModelsRule(item.excludedModels); const excludedModels = item.excludedModels ?? []; const statusData = statusBarCache.get(item.apiKey) || calculateStatusBarData([], item.apiKey); return (
{t('ai_providers.gemini_item_title')} #{index + 1}
{t('common.api_key')}: {maskApiKey(item.apiKey)}
{item.prefix && (
{t('common.prefix')}: {item.prefix}
)} {item.baseUrl && (
{t('common.base_url')}: {item.baseUrl}
)} {headerEntries.length > 0 && (
{headerEntries.map(([key, value]) => ( {key}: {value} ))}
)} {configDisabled && (
{t('ai_providers.config_disabled_badge')}
)} {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}
); }} />
); }