import { useMemo } from "react"; import { CheckCircle2, ExternalLink, Loader2, MinusCircle, RefreshCw, XCircle } from "lucide-react"; import type { PrCheckStatus } from "../api"; import "./PrChecksList.css"; interface PrChecksListProps { checks: PrCheckStatus[]; rollup: string; lastCheckedAt?: string; loading: boolean; error?: string | null; onRefresh: () => void; } const FAILING_STATES = new Set(["failure", "cancelled", "timed_out", "action_required", "startup_failure"]); const PENDING_STATES = new Set(["pending", "stale"]); function getCheckPriority(check: PrCheckStatus): number { if (FAILING_STATES.has(check.state)) return check.required ? 0 : 1; if (PENDING_STATES.has(check.state)) return 2; if (check.state === "success" || check.state === "neutral" || check.state === "skipped") return 3; return 4; } function formatDuration(startedAt?: string, completedAt?: string): string | null { if (!startedAt || !completedAt) return null; const start = Date.parse(startedAt); const end = Date.parse(completedAt); if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) return null; const seconds = Math.floor((end - start) / 1000); const mins = Math.floor(seconds / 60); const rem = seconds % 60; return `${String(mins).padStart(2, "0")}:${String(rem).padStart(2, "0")}`; } function relativeTime(value?: string): string | null { if (!value) return null; const ts = Date.parse(value); if (!Number.isFinite(ts)) return null; const delta = Math.max(0, Math.floor((Date.now() - ts) / 1000)); return `updated ${delta}s ago`; } export function PrChecksList({ checks, rollup: _rollup, lastCheckedAt, loading, error, onRefresh }: PrChecksListProps) { const sortedChecks = useMemo(() => [...checks].sort((a, b) => { const byPriority = getCheckPriority(a) - getCheckPriority(b); if (byPriority !== 0) return byPriority; return a.name.localeCompare(b.name); }), [checks]); const summary = useMemo(() => { return checks.reduce( (acc, check) => { if (FAILING_STATES.has(check.state)) acc.failing += 1; else if (PENDING_STATES.has(check.state)) acc.pending += 1; else acc.passing += 1; return acc; }, { passing: 0, failing: 0, pending: 0 }, ); }, [checks]); return (
{summary.passing} passing, {summary.failing} failing, {summary.pending} pending
{lastCheckedAt ? {relativeTime(lastCheckedAt)} : null}
{error ? (
{error}
) : null} {sortedChecks.length === 0 ? (
No checks reported yet
) : (
{sortedChecks.map((check) => { const failing = FAILING_STATES.has(check.state); const pending = PENDING_STATES.has(check.state); const duration = formatDuration(check.startedAt, check.completedAt); return (
{check.name} {check.required ? Required : null} {duration ? {duration} : null}
{check.detailsUrl ? ( View details ) : null}
); })}
)}
); }