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}
>
)}
);
}