import { useMemo, useState } from "react";
import { useParams } from "react-router";
import type { EventEnvelope } from "@qltysh/fabro-api-client";
import {
DebugEventDetailsPanel,
DebugEventRow,
EventSearchInput,
MultiSelectFilter,
debugCategory,
debugCategoryLabel,
} from "../components/event-debug";
import { StageSidebar } from "../components/stage-sidebar";
import { EmptyState, ErrorState, LoadingState } from "../components/state";
import { useRun, useRunEventsList, useRunStages } from "../lib/queries";
import { mapRunStagesToSidebarStages } from "../lib/stage-sidebar";
export const handle = { wide: true, fullHeight: true };
export default function RunEvents() {
const { id } = useParams();
const runQuery = useRun(id);
const stagesQuery = useRunStages(id);
const eventsQuery = useRunEventsList(id);
const stages = useMemo(
() => mapRunStagesToSidebarStages(stagesQuery.data),
[stagesQuery.data],
);
return (
void eventsQuery.mutate()}
runStart={runQuery.data?.start_time ?? runQuery.data?.created_at}
/>
);
}
function EventsView({
events,
error,
onRetry,
runStart,
}: {
events: EventEnvelope[] | undefined;
error: unknown;
onRetry: () => void;
runStart: string | undefined;
}) {
const [openSeq, setOpenSeq] = useState(null);
const [selectedCategories, setSelectedCategories] = useState([]);
const [search, setSearch] = useState("");
const all = events ?? [];
const availableCategories = useMemo(() => {
const set = new Set();
for (const event of all) {
if (event.event) set.add(debugCategory(event.event));
}
return Array.from(set).sort();
}, [all]);
const filtered = useMemo(() => {
const useCategoryFilter = selectedCategories.length > 0;
const cats = new Set(selectedCategories);
const needle = search.toLowerCase();
return all.filter((event) => {
const name = event.event ?? "";
if (useCategoryFilter && !cats.has(debugCategory(name))) return false;
if (needle) {
const blob = `${name} ${JSON.stringify(event.properties ?? {})}`.toLowerCase();
if (!blob.includes(needle)) return false;
}
return true;
});
}, [all, selectedCategories, search]);
const openEvent = useMemo(
() => (openSeq != null ? all.find((e) => e.seq === openSeq) ?? null : null),
[all, openSeq],
);
const allCategoriesSelected =
selectedCategories.length === 0 ||
selectedCategories.length === availableCategories.length;
const isFiltering = !allCategoriesSelected || search.length > 0;
function clearFilters() {
setSelectedCategories([]);
setSearch("");
}
if (error) {
return (
);
}
if (events === undefined) {
return (
);
}
return (
<>
selected={selectedCategories}
options={availableCategories}
labelOf={debugCategoryLabel}
onChange={setSelectedCategories}
emptyMeansAll
/>
{isFiltering && (
)}
{all.length > 0 && (
{isFiltering
? `${filtered.length.toLocaleString()} of ${all.length.toLocaleString()} events`
: `${all.length.toLocaleString()} events`}
)}
{all.length === 0 ? (
) : filtered.length === 0 ? (
No events match these filters.
) : (
filtered.map((event) => (
setOpenSeq(event.seq)}
/>
))
)}
setOpenSeq(null)} />
>
);
}
function errorMessage(error: unknown): string | undefined {
return error instanceof Error ? error.message : undefined;
}