import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Plus, Pencil, Trash2, RefreshCw, Key, Shield, Search, Clock, CheckCircle2, XCircle, Zap, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { toast } from 'sonner'; import { useConfirm } from '@/components/ui/confirm-dialog'; import { useCredentials, useCredentialMutations } from '@/hooks/api'; import type { Credential, SecretType } from './credential-manager/types'; import { TYPE_CONFIG, TYPE_BADGE_CLASS } from './credential-manager/constants'; import { CredDetailDialog } from './credential-manager/CredDetailDialog'; import { CredFormDialog } from './credential-manager/CredFormDialog'; // 凭据管理页编排器 // Logan.Li export default function CredentialManager() { const { t } = useTranslation(); const confirm = useConfirm(); const [dialogOpen, setDialogOpen] = useState(false); const [editing, setEditing] = useState(null); const [detailId, setDetailId] = useState(null); const [detailOpen, setDetailOpen] = useState(false); // 筛选条件 // Logan.Li const [filterType, setFilterType] = useState('all'); const [filterSearch, setFilterSearch] = useState(''); const listParams = { page_size: 100, ...(filterSearch ? { search: filterSearch } : {}), ...(filterType && filterType !== 'all' ? { secret_type: filterType } : {}), }; const { data: credentials = [], isLoading: loading, refetch } = useCredentials(listParams); const { deleteMutation, toggleActiveMutation } = useCredentialMutations(); const handleDelete = async (cred: Credential) => { const ok = await confirm({ title: t('deploy.cred.deleteCred'), description: t('deploy.pipeline.deleteConfirm', { name: cred.name }), confirmText: t('common.delete'), variant: 'destructive', }); if (!ok) return; try { await deleteMutation.mutateAsync(cred.id); toast.success(t('deploy.toast.deleted')); } catch { toast.error(t('deploy.toast.deleteFailed')); } }; const handleToggleActive = async (cred: Credential) => { try { await toggleActiveMutation.mutateAsync({ id: cred.id, isActive: !cred.is_active }); toast.success(cred.is_active ? t('deploy.toast.disabled') : t('deploy.toast.enabled')); } catch { toast.error(t('deploy.toast.operationFailed')); } }; const handleCopyPrefix = (prefix: string) => { navigator.clipboard.writeText(prefix); toast.success(t('deploy.cred.refIdCopied')); }; const typeOptions = Object.entries(TYPE_CONFIG) as [SecretType, typeof TYPE_CONFIG[SecretType]][]; return (

{t('deploy.cred.title')}

{t('deploy.cred.subtitle')}

{/* 搜索与筛选 // Logan.Li */}
setFilterSearch(e.target.value)} placeholder={t('deploy.label.searchPlaceholder')} className="pl-8 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] h-9 text-sm" />
{t('deploy.cred.credCount', { count: credentials.length })}
{loading ? (
{t('deploy.label.loading')}
) : credentials.length === 0 ? (

{t('deploy.cred.noCredentials')}

{t('deploy.cred.subtitle')}

) : (
{credentials.map(cred => { const st = cred.secret_type as SecretType; const cfg = TYPE_CONFIG[st]; const isExpired = cred.expires_at && new Date(cred.expires_at) < new Date(); return ( { setDetailId(cred.id); setDetailOpen(true); }}> ); })}
{t('deploy.table.name')} {t('deploy.table.type')} {t('deploy.table.repoUrl')} {t('deploy.table.tagLabel')} {t('deploy.cred.refId')} {t('deploy.table.status')} {t('deploy.table.actions')}
{cfg ? : }
{cred.name}
{cred.variable_name && (
{cred.variable_name}
)}
{cfg?.label || st} {cred.summary || '-'}
{(cred.tags || []).slice(0, 3).map(t => ( {t} ))} {(cred.tags || []).length > 3 && ( +{cred.tags.length - 3} )}
{ e.stopPropagation(); handleCopyPrefix(cred.env_prefix); }}> {cred.env_prefix}
{cred.is_active ? ( {t('common.enable')} ) : ( {t('common.disable')} )} {isExpired && ( {t('deploy.status.expired')} )}
e.stopPropagation()}>
)} setDialogOpen(false)} onSaved={() => refetch()} editData={editing} /> setDetailOpen(false)} credId={detailId} />
); }