import { Fragment, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/Button'; import { Card } from '@/components/ui/Card'; import iconVertex from '@/assets/icons/vertex.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 } from '../utils'; import type { VertexFormState } from '../types'; import { VertexModal } from './VertexModal'; interface VertexSectionProps { configs: ProviderKeyConfig[]; 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; onCloseModal: () => void; onSave: (data: VertexFormState, index: number | null) => Promise; } export function VertexSection({ configs, keyStats, usageDetails, loading, disableControls, isSaving, isSwitching, isModalOpen, modalIndex, onAdd, onEdit, onDelete, onCloseModal, onSave, }: VertexSectionProps) { const { t } = useTranslation(); const actionsDisabled = disableControls || isSaving || 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]); const initialData = modalIndex !== null ? configs[modalIndex] : undefined; return ( <> {t('ai_providers.vertex_title')} } extra={ } > items={configs} loading={loading} keyField={(item) => item.apiKey} emptyTitle={t('ai_providers.vertex_empty_title')} emptyDescription={t('ai_providers.vertex_empty_desc')} onEdit={onEdit} onDelete={onDelete} actionsDisabled={actionsDisabled} renderContent={(item, index) => { const stats = getStatsBySource(item.apiKey, keyStats, item.prefix); const headerEntries = Object.entries(item.headers || {}); const statusData = statusBarCache.get(item.apiKey) || calculateStatusBarData([]); return (
{t('ai_providers.vertex_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}
)} {item.proxyUrl && (
{t('common.proxy_url')}: {item.proxyUrl}
)} {headerEntries.length > 0 && (
{headerEntries.map(([key, value]) => ( {key}: {value} ))}
)} {item.models?.length ? (
{t('ai_providers.vertex_models_count')}: {item.models.length} {item.models.map((model) => ( {model.name} {model.alias && ( {model.alias} )} ))}
) : null}
{t('stats.success')}: {stats.success} {t('stats.failure')}: {stats.failure}
); }} />
); }