"use client"; import { Fragment, useCallback, useEffect, useState } from "react"; import { toast } from "sonner"; import { Plus, Trash2, Pencil, PlugZap } from "lucide-react"; import { api, type Connection } from "@/components/api"; import { fmtTime } from "@/components/format"; import { ConnectionDialog } from "@/components/connection-dialog"; import { ConfirmDialog } from "@/components/confirm-dialog"; import { Button } from "@/components/ui/button"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; interface TestResult { ok: boolean; output: string; } export default function ConnectionsPage() { const [list, setList] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [dialogOpen, setDialogOpen] = useState(false); const [editing, setEditing] = useState(null); const [deleting, setDeleting] = useState(null); const [deleteLoading, setDeleteLoading] = useState(false); const [testingId, setTestingId] = useState(null); const [testResults, setTestResults] = useState>({}); const [expandedId, setExpandedId] = useState(null); const load = useCallback(() => { api("/api/connections") .then(setList) .catch((e) => setError(e instanceof Error ? e.message : "加载失败")) .finally(() => setLoading(false)); }, []); useEffect(load, [load]); async function testConn(c: Connection) { setTestingId(c.id); try { const res = await api(`/api/connections/${c.id}/test`, { method: "POST", }); setTestResults((m) => ({ ...m, [c.id]: res })); setExpandedId(c.id); if (res.ok) toast.success(`连接「${c.name}」测试成功`); else toast.error(`连接「${c.name}」测试失败`); } catch (e) { toast.error(e instanceof Error ? e.message : "测试失败"); } finally { setTestingId(null); } } async function doDelete() { if (!deleting) return; setDeleteLoading(true); try { await api(`/api/connections/${deleting.id}`, { method: "DELETE" }); toast.success(`已删除连接「${deleting.name}」`); setDeleting(null); load(); } catch (e) { toast.error(e instanceof Error ? e.message : "删除失败"); } finally { setDeleteLoading(false); } } return (

Connections

{loading ? (
加载中…
) : error ? (
加载失败:{error}
) : list.length === 0 ? (
还没有连接,点击右上角「新建连接」添加 MySQL / MariaDB / TiDB 数据源。
) : (
名称 类型 Host 数据库 创建时间 操作 {list.map((c) => ( {c.name} {c.type} {c.host}:{c.port} {c.database} {fmtTime(c.created_at)}
{expandedId === c.id && testResults[c.id] ? (
                          {testResults[c.id].output ||
                            (testResults[c.id].ok ? "连接成功" : "连接失败")}
                        
) : null}
))}
)} {dialogOpen ? ( ) : null} !o && setDeleting(null)} title={`删除连接「${deleting?.name ?? ""}」?`} description="删除后引用该连接的流水线将无法运行。" loading={deleteLoading} onConfirm={doDelete} />
); }