Improve sources and runs history tables

This commit is contained in:
Abel Luck 2026-03-31 10:49:50 +02:00
parent df68aa95e9
commit 939cd9ea5d
7 changed files with 459 additions and 25 deletions

View file

@ -242,12 +242,129 @@ def _completed_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
)
def _completed_page_href(page: int) -> str:
return f"/runs?completed_page={page}"
def _completed_history_pagination(
*,
completed_page: int,
completed_page_size: int,
completed_total_count: int,
completed_total_pages: int,
) -> Renderable | None:
if completed_total_count <= completed_page_size:
return None
start_result = ((completed_page - 1) * completed_page_size) + 1
end_result = min(completed_total_count, completed_page * completed_page_size)
link_class = (
"relative inline-flex items-center px-4 py-2 text-sm font-semibold text-slate-700 "
"ring-1 ring-inset ring-slate-200 hover:bg-stone-50"
)
return h.div(
class_="flex items-center justify-between border-t border-slate-200 bg-white px-4 py-3 sm:px-6"
)[
h.div(class_="flex flex-1 justify-between sm:hidden")[
h.a(
href=_completed_page_href(max(1, completed_page - 1)),
class_="relative inline-flex items-center rounded-xl border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-stone-50",
)["Previous"],
h.a(
href=_completed_page_href(
min(completed_total_pages, completed_page + 1)
),
class_="relative ml-3 inline-flex items-center rounded-xl border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-stone-50",
)["Next"],
],
h.div(class_="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between")[
h.p(class_="text-sm text-slate-600")[
"Showing ",
h.span(class_="font-medium text-slate-950")[str(start_result)],
" to ",
h.span(class_="font-medium text-slate-950")[str(end_result)],
" of ",
h.span(class_="font-medium text-slate-950")[str(completed_total_count)],
" results",
],
h.nav(
aria_label="Completed execution pagination",
class_="isolate inline-flex -space-x-px rounded-xl shadow-xs",
)[
(
h.a(
href=_completed_page_href(page_number),
aria_current=(
"page" if page_number == completed_page else None
),
class_=(
"relative z-10 inline-flex items-center bg-amber-500 px-4 py-2 text-sm font-semibold text-slate-950"
if page_number == completed_page
else link_class
),
)[str(page_number)]
for page_number in range(1, completed_total_pages + 1)
)
],
],
]
def _completed_history_section(
*,
completed_rows: tuple[tuple[Node, ...], ...],
completed_page: int,
completed_page_size: int,
completed_total_count: int,
completed_total_pages: int,
) -> Renderable:
pagination = _completed_history_pagination(
completed_page=completed_page,
completed_page_size=completed_page_size,
completed_total_count=completed_total_count,
completed_total_pages=completed_total_pages,
)
return h.section[
table_section(
eyebrow="History",
title="Completed job executions",
empty_message="No job executions have completed yet.",
headers=(
"#",
"Source",
"Ended",
"State",
"Summary",
"Log",
),
rows=completed_rows,
first_header_class="w-px py-2.5 pr-2 pl-3 text-left text-xs font-semibold uppercase tracking-[0.18em] whitespace-nowrap text-slate-500 sm:pl-3",
first_cell_class="w-px py-3 pr-2 pl-3 text-sm font-medium text-slate-950 sm:pl-3",
actions=(
action_button(
label="Clear history",
tone="danger",
post_path="/actions/completed-executions/clear",
)
if completed_total_count > 0
else None
),
),
pagination,
]
def runs_page(
*,
running_executions: tuple[Mapping[str, object], ...] | None = None,
queued_executions: tuple[Mapping[str, object], ...] | None = None,
upcoming_jobs: tuple[Mapping[str, object], ...] | None = None,
completed_executions: tuple[Mapping[str, object], ...] | None = None,
completed_page: int = 1,
completed_page_size: int = 20,
completed_total_count: int | None = None,
completed_total_pages: int | None = None,
source_count: int = 0,
) -> Renderable:
running_items = running_executions or ()
@ -262,6 +379,12 @@ def runs_page(
)
upcoming_rows = tuple(_upcoming_row(job) for job in upcoming_items)
completed_rows = tuple(_completed_row(execution) for execution in completed_items)
resolved_completed_total_count = (
len(completed_items) if completed_total_count is None else completed_total_count
)
resolved_completed_total_pages = (
1 if completed_total_pages is None else completed_total_pages
)
return page_shell(
current_path="/runs",
@ -302,21 +425,12 @@ def runs_page(
),
rows=upcoming_rows,
),
table_section(
eyebrow="History",
title="Completed job executions",
empty_message="No job executions have completed yet.",
headers=(
"#",
"Source",
"Ended",
"State",
"Summary",
"Log",
),
rows=completed_rows,
first_header_class="w-px py-2.5 pr-2 pl-3 text-left text-xs font-semibold uppercase tracking-[0.18em] whitespace-nowrap text-slate-500 sm:pl-3",
first_cell_class="w-px py-3 pr-2 pl-3 text-sm font-medium text-slate-950 sm:pl-3",
_completed_history_section(
completed_rows=completed_rows,
completed_page=completed_page,
completed_page_size=completed_page_size,
completed_total_count=resolved_completed_total_count,
completed_total_pages=resolved_completed_total_pages,
),
h.script[
"""

View file

@ -75,13 +75,11 @@ def _source_row(source: Mapping[str, object]) -> tuple[Node, ...]:
label=str(source["state"]),
tone=str(source["state_tone"]),
),
h.p(class_="mt-2 text-xs text-slate-500")[str(source["last_run"])],
],
h.div(class_="flex flex-wrap items-center gap-2")[
h.div(class_="flex flex-nowrap items-center gap-3 whitespace-nowrap")[
inline_link(
href=f"/sources/{source['slug']}/edit", label="Edit", tone="amber"
),
inline_link(href="/runs", label="View runs"),
action_button(
label="Delete",
tone="danger",