"use client"; import { useCallback, useEffect, useState } from "react"; import Link from "next/link"; import { useParams } from "next/navigation"; import { api, type Pipeline, type RunDetail, } from "@/components/api"; import { LogViewer } from "@/components/log-viewer"; import { StatusBadge } from "@/components/status-badge"; import { fmtTime, fmtDuration } from "@/components/format"; import { Card, CardContent, CardHeader, CardTitle, } from "@/components/ui/card"; export default function RunDetailPage() { const params = useParams<{ id: string }>(); const id = Number(params.id); const [run, setRun] = useState(null); const [pipelineName, setPipelineName] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const load = useCallback(() => { api(`/api/runs/${id}`) .then((r) => { setRun(r); api(`/api/pipelines/${r.pipeline_id}`) .then((p) => setPipelineName(p.name)) .catch(() => setPipelineName(null)); }) .catch((e) => setError(e instanceof Error ? e.message : "加载失败")) .finally(() => setLoading(false)); }, [id]); useEffect(load, [load]); if (loading) return
加载中…
; if (error || !run) return (
{error ? `加载失败:${error}` : "运行记录不存在"}
); const isActive = run.status === "running" || run.status === "queued"; return (

运行 #{run.id}

详情
流水线
{pipelineName ?? `#${run.pipeline_id}`}
开始时间
{fmtTime(run.started_at)}
结束时间
{fmtTime(run.finished_at)}
耗时
{fmtDuration(run.started_at, run.finished_at)}
进程 PID
{run.pid ?? "-"}
{run.error ? (
错误信息
                {run.error}
              
) : null}
日志{isActive ? "(实时)" : ""} {isActive ? ( ) : ( )}
); }