diff --git a/apps/fabro-web/app/routes/automation-detail.tsx b/apps/fabro-web/app/routes/automation-detail.tsx index 906d887f0..19bac1ecd 100644 --- a/apps/fabro-web/app/routes/automation-detail.tsx +++ b/apps/fabro-web/app/routes/automation-detail.tsx @@ -12,9 +12,11 @@ import { } from "@heroicons/react/24/outline"; import type { Automation, + BoardColumn, ListRunsSortEnum, } from "@qltysh/fabro-api-client"; +import { toRunWithStatus } from "../data/runs"; import { ApiError, apiData, automationsApi } from "../lib/api-client"; import { useAutomation, useAutomationRuns } from "../lib/queries"; import { queryKeys } from "../lib/query-keys"; @@ -22,14 +24,21 @@ import { useDataUpdatedAt } from "../hooks/use-data-updated-at"; import { useTickingNow } from "../lib/time"; import { formatRelativeTime } from "../lib/format"; import { ColumnPickerButton } from "../components/runs-list/column-picker-button"; +import { FilterButton } from "../components/runs-list/filter-button"; import { + STATUS_FILTER_OPTIONS, + createdCutoffMsFor, + createdFilterOptions, hiddenColumnsFromSearchParams, + parseCreatedFilter, parseDirection, parsePage, parsePageSize, parseSort, + parseStatusFilter, } from "../components/runs-list/preferences"; import { RunsListView } from "../components/runs-list/runs-list-view"; +import { StatusFilterButton } from "../components/runs-list/status-filter-button"; import { serializeHiddenColumns, type ToggleableColumn, @@ -210,6 +219,13 @@ function AutomationRunsList({ automationId }: { automationId: string }) { const direction = parseDirection(urlSearchParams.get("direction")); const page = parsePage(urlSearchParams.get("page")); const pageSize = parsePageSize(urlSearchParams.get("size")); + const repoFilter = urlSearchParams.get("repo") || "all"; + const createdFilter = parseCreatedFilter(urlSearchParams.get("created")); + const rawStatus = urlSearchParams.get("status"); + const statusFilter = useMemo( + () => (rawStatus == null ? new Set() : parseStatusFilter(rawStatus)), + [rawStatus], + ); const hiddenColumns = useMemo( () => hiddenColumnsFromSearchParams(urlSearchParams), [urlSearchParams], @@ -251,6 +267,28 @@ function AutomationRunsList({ automationId }: { automationId: string }) { if (serialized) p.set("hide", serialized); else p.set("hide", ""); }); + const setRepoFilter = (value: string) => + updateParams((p) => { + if (value && value !== "all") p.set("repo", value); + else p.delete("repo"); + p.delete("page"); + }); + const setCreatedFilter = (value: ReturnType) => + updateParams((p) => { + if (value !== "all") p.set("created", value); + else p.delete("created"); + p.delete("page"); + }); + const setStatusFilter = (next: Set) => + updateParams((p) => { + const trivial = next.size === 0 || next.size === STATUS_FILTER_OPTIONS.length; + if (trivial) { + p.delete("status"); + } else { + p.set("status", STATUS_FILTER_OPTIONS.filter((c) => next.has(c)).join(",")); + } + p.delete("page"); + }); const onSortClick = (key: ListRunsSortEnum) => updateParams((p) => { if (sort === key) { @@ -267,6 +305,15 @@ function AutomationRunsList({ automationId }: { automationId: string }) { offset: (page - 1) * pageSize, }); + const allRepos = useMemo(() => { + const repos = new Set(); + for (const run of runsQuery.data?.data ?? []) { + repos.add(toRunWithStatus(run).repo); + } + return Array.from(repos).sort(); + }, [runsQuery.data]); + const createdCutoffMs = createdCutoffMsFor(createdFilter); + const now = useTickingNow(true, 15_000); const updatedAt = useDataUpdatedAt(runsQuery.data); @@ -303,6 +350,25 @@ function AutomationRunsList({ automationId }: { automationId: string }) { /> + + + ({ value: repo, label: repo })), + ]} + onChange={setRepoFilter} + /> +
{updatedAt != null ? ( @@ -344,9 +410,10 @@ function AutomationRunsList({ automationId }: { automationId: string }) { onPageChange={setPage} onPageSizeChange={setPageSize} query={lowerQuery} - repoFilter="all" + repoFilter={repoFilter} workflowFilter="all" - createdCutoffMs={null} + statusFilter={statusFilter} + createdCutoffMs={createdCutoffMs} />
);