import { Link, useSearchParams } from "react-router-dom"; import { useOwners, useRuns } from "../api/hooks.js"; import { formatTime, StatusBadge } from "../lib/format"; const PAGE_SIZES = [25, 50, 100]; const DEFAULT_LIMIT = 50; const TRIGGER_TYPES = ["HTTP", "cron", "manual"]; const SORT_COLUMNS = [ { key: "status", label: "Status" }, { key: "workflow", label: "Workflow" }, { key: "revision", label: "Revision" }, { key: "trigger", label: "Trigger" }, { key: "started_at", label: "Started" }, { key: "duration", label: "Duration" }, ]; function dateToAfterIso(date) { if (!date) return undefined; return `${date}T00:00:00.000Z`; } function dateToBeforeIso(date) { if (!date) return undefined; const d = new Date(`${date}T00:00:00.000Z`); d.setUTCDate(d.getUTCDate() + 1); return d.toISOString(); } function SortHeader({ column, label, sort, order, onSort }) { const active = sort === column; return ( ); } export function EventsPage() { const [params, setParams] = useSearchParams(); const workflow = params.get("workflow") || ""; const status = params.get("status") || ""; const owner = params.get("owner") || ""; const trigger = params.get("trigger") || ""; const afterDate = params.get("after") || ""; const beforeDate = params.get("before") || ""; const offset = Math.max(Number(params.get("offset")) || 0, 0); const limitParam = Number(params.get("limit")); const limit = PAGE_SIZES.includes(limitParam) ? limitParam : DEFAULT_LIMIT; const sort = params.get("sort") || "started_at"; const order = params.get("order") === "asc" ? "asc" : "desc"; const { data: owners = [] } = useOwners(); const { data, isLoading } = useRuns({ workflow: workflow || undefined, status: status || undefined, owner: owner || undefined, trigger: trigger || undefined, after: dateToAfterIso(afterDate), before: dateToBeforeIso(beforeDate), limit, offset, sort, order, }); const runs = data?.runs ?? []; const total = data?.total ?? 0; function update(key, value, resetOffset = true) { const next = new URLSearchParams(params); if (value) next.set(key, value); else next.delete(key); if (resetOffset && key !== "offset") next.delete("offset"); setParams(next); } function setSort(column) { const next = new URLSearchParams(params); if (sort === column) { next.set("order", order === "asc" ? "desc" : "asc"); } else { next.set("sort", column); next.set("order", "desc"); } next.delete("offset"); setParams(next); } return (

{status === "failed" ? "Failed events" : "Events"}

update("workflow", e.target.value)} />
{isLoading ? ( ) : runs.length === 0 ? (

No events match these filters.

) : ( <>
{SORT_COLUMNS.map(({ key, label }) => ( ))} {runs.map((r) => ( ))}
{r.workflow_name || r.workflow} {r.workflow_revision != null ? `#${r.workflow_revision}` : "unknown"} {r.trigger_type} {r.trigger_detail ? ` · ${r.trigger_detail}` : ""} {formatTime(r.started_at)} {r.duration_ms != null ? `${r.duration_ms}ms` : "—"}
{total === 0 ? "0" : `${offset + 1}–${offset + runs.length}`} of {total}
)}
); }