import { useState, useEffect } from 'react'; import { Plus, Pencil, Trash2, RefreshCw, Loader2, GitBranch, Container, Globe, Wrench} from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { toast } from 'sonner'; import { useConfirm } from '@/components/ui/confirm-dialog'; import { deployApi } from '@/api'; import CredentialFormDialog from './CredentialFormDialog'; import ToolConfigTab from './ToolConfigTab'; import type { JsonData } from '@/types/common'; interface Repo { id: number; name: string; description: string; url: string; auth_type: string; credential_id: number | null; enabled: boolean; created_by: string; updated_at: string; } interface Registry { id: number; name: string; description: string; registry_url: string; namespace: string; auth_type: string; credential_id: number | null; enabled: boolean; created_by: string; updated_at: string; } interface Environment { id: number; name: string; display_name: string; type: string; color: string; sort_order: number; description: string; enabled: boolean; } function EnvFormDialog({ open, onClose, onSaved, editData }: { open: boolean; onClose: () => void; onSaved: () => void; editData: Environment | null }) { const [name, setName] = useState(''); const [displayName, setDisplayName] = useState(''); const [envType, setEnvType] = useState('development'); const [color, setColor] = useState('#3B82F6'); const [description, setDescription] = useState(''); const [sortOrder, setSortOrder] = useState(50); const [submitting, setSubmitting] = useState(false); useEffect(() => { if (editData) { setName(editData.name); setDisplayName(editData.display_name); setEnvType(editData.type); setColor(editData.color); setDescription(editData.description || ''); setSortOrder(editData.sort_order); } else { setName(''); setDisplayName(''); setEnvType('development'); setColor('#3B82F6'); setDescription(''); setSortOrder(50); } }, [editData, open]); const handleSubmit = async () => { if (!name.trim() || !displayName.trim()) { toast.error('请填写名称'); return; } setSubmitting(true); try { if (editData) await deployApi.updateEnvironment(editData.id, { name, display_name: displayName, type: envType, color, sort_order: sortOrder, description }); else await deployApi.createEnvironment({ name, display_name: displayName, type: envType, color, sort_order: sortOrder, description }); toast.success(editData ? '已更新' : '已创建'); onSaved(); onClose(); } catch { toast.error('操作失败'); } finally { setSubmitting(false); } }; return ( {editData ? '编辑环境' : '新增环境'}
setName(e.target.value)} placeholder="production" />
setDisplayName(e.target.value)} placeholder="生产环境" />
setColor(e.target.value)} type="color" className="w-16 h-9 p-1" />
setSortOrder(Number(e.target.value))} type="number" />
setDescription(e.target.value)} placeholder="可选" />
); } export default function DeployCredentials() { const _confirm = useConfirm(); const [repos, setRepos] = useState([]); const [registries, setRegistries] = useState([]); const [environments, setEnvironments] = useState([]); const [loading, setLoading] = useState(false); const [editorOpen, setEditorOpen] = useState(false); const [editType, setEditType] = useState<'repo' | 'registry'>('repo'); const [editingId, setEditingId] = useState(null); const [envDialogOpen, setEnvDialogOpen] = useState(false); const [editEnv, setEditEnv] = useState(null); const fetchAll = async () => { setLoading(true); try { const [rR, rReg, rEnv] = await Promise.all([deployApi.getRepos(), deployApi.getRegistries(), deployApi.getEnvironments()]); setRepos((rR as JsonData)?.list || []); setRegistries((rReg as JsonData)?.list || []); setEnvironments((rEnv as JsonData)?.list || []); } catch { toast.error('获取数据失败'); } finally { setLoading(false); } }; useEffect(() => { fetchAll(); }, []); const handleDelete = async (type: 'repo' | 'registry' | 'env', item: JsonData) => { const ok = await _confirm({ title: '删除', description: `确定删除「${item.name || item.display_name}」?`, confirmText: '删除', variant: 'destructive' }); if (!ok) return; try { if (type === 'repo') await deployApi.deleteRepo(item.id); else if (type === 'registry') await deployApi.deleteRegistry(item.id); else await deployApi.deleteEnvironment(item.id); toast.success('已删除'); fetchAll(); } catch { toast.error('删除失败'); } }; const envTypeLabel: Record = { production: '生产', staging: '预发布', testing: '测试', development: '开发' }; return (

发布配置管理

Git 仓库 ({repos.length}) 镜像仓库 ({registries.length}) 环境管理 ({environments.length}) 工具配置 {loading ?
加载中...
: repos.length === 0 ?
暂无 Git 仓库认证配置
: (
{repos.map(r => ( ))}
名称 仓库 URL 认证方式 状态 操作
{r.name} {r.url} {r.auth_type} {r.enabled ? '启用' : '禁用'}
)}
{loading ?
加载中...
: registries.length === 0 ?
暂无镜像仓库认证配置
: (
{registries.map(r => ( ))}
名称 Registry URL 命名空间 认证 状态 操作
{r.name} {r.registry_url} {r.namespace || '-'} {r.auth_type} {r.enabled ? '启用' : '禁用'}
)}
{/* Environments tab */}
{loading ?
加载中...
: environments.length === 0 ?
暂无环境配置,点击"新增环境"创建
: (
{environments.map(env => ( ))}
环境标识 显示名称 类型 颜色 排序 状态 操作
{env.name} {env.display_name} {envTypeLabel[env.type] || env.type}
{env.sort_order} {env.enabled ? '启用' : '禁用'}
)}
{/* Tools config tab */}
setEnvDialogOpen(false)} onSaved={fetchAll} editData={editEnv} />
); }