import { useEffect, useMemo, useState } from "react"; import { Link } from "react-router-dom"; import { fetchBuilds, statusLabel, type Build } from "../api/client"; import { getBuildDuration, isBuildFinished } from "../lib/duration"; export default function BuildList() { const [builds, setBuilds] = useState([]); const [error, setError] = useState(null); const [selectedPkg, setSelectedPkg] = useState(null); const [sidebarOpen, setSidebarOpen] = useState(false); useEffect(() => { fetchBuilds() .then(setBuilds) .catch((err) => setError(err instanceof Error ? err.message : "加载失败"), ); }, []); const packages = useMemo(() => { const map = new Map< string, { appName: string; latestAt: string; count: number } >(); for (const b of builds) { const existing = map.get(b.appIdentifier); if (!existing) { map.set(b.appIdentifier, { appName: b.appName, latestAt: b.createdAt, count: 1, }); } else { existing.count++; if (b.createdAt > existing.latestAt) { existing.latestAt = b.createdAt; existing.appName = b.appName; } } } return [...map.entries()] .sort((a, b) => b[1].latestAt.localeCompare(a[1].latestAt)) .map(([appIdentifier, { appName, count }]) => ({ appIdentifier, appName, count, })); }, [builds]); const filtered = selectedPkg ? builds.filter((b) => b.appIdentifier === selectedPkg) : builds; function selectPkg(pkg: string | null) { setSelectedPkg(pkg); setSidebarOpen(false); } return (
{sidebarOpen && (
setSidebarOpen(false)} /> )}

构建记录

{selectedPkg ? `${selectedPkg} 的构建记录` : "最近 20 条构建任务。"}

{error ?

{error}

: null} {!error && filtered.length === 0 ? (

暂无构建记录。

) : null} {filtered.length > 0 ? (
    {filtered.map((build) => (
  • {build.appName} ({build.appNameEn})
    v{build.appVersion} · {statusLabel(build.status)} {isBuildFinished(build.status) ? ` · 耗时 ${getBuildDuration(build).text}` : null}{" "} · {new Date(build.createdAt).toLocaleString()}
  • ))}
) : null}
); }