from __future__ import annotations from collections.abc import Mapping import htpy as h from htpy import Node, Renderable from repub.components import ( app_shell, header_action_link, inline_link, muted_action_link, stat_card, status_badge, table_section, ) from repub.pages.runs import live_work_section, relative_time_formatter_script def dashboard_header() -> Renderable: return h.section[ h.div( class_="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between" )[ h.div[ h.h1(class_="text-3xl font-semibold tracking-tight text-slate-950")[ "Republisher" ], ], h.div(class_="flex flex-wrap gap-2")[ header_action_link(href="/sources/create", label="Create source"), muted_action_link(href="/sources", label="View sources"), ], ] ] def operational_snapshot(*, snapshot: Mapping[str, str] | None = None) -> Renderable: values = snapshot or { "running_now": "0", "upcoming_today": "0", "failures_24h": "0", "artifact_footprint": "0 B", } return h.section[ h.div(class_="mb-3 flex items-end justify-between gap-4")[ h.div[ h.p( class_="text-xs font-semibold uppercase tracking-[0.22em] text-slate-500" )["Overview"], h.h2(class_="mt-1 text-xl font-semibold tracking-tight text-slate-950")[ "Operational snapshot" ], ], ], h.dl(class_="grid gap-3 md:grid-cols-2 xl:grid-cols-4")[ stat_card( label="Running now", value=values["running_now"], detail="Currently active job executions.", ), stat_card( label="Upcoming today", value=values["upcoming_today"], detail="Enabled jobs that are ready for their next run.", ), stat_card( label="Failures in 24h", value=values["failures_24h"], detail="Recent failed executions recorded by the scheduler.", ), stat_card( label="Artifact footprint", value=values["artifact_footprint"], detail="Current artifact size under the output path.", ), ], ] def _source_feed_row(source_feed: Mapping[str, object]) -> tuple[Node, ...]: last_updated_iso = source_feed.get("last_updated_iso") last_updated = ( h.time( datetime=str(last_updated_iso), title=str(last_updated_iso), class_="font-medium text-slate-900", )[str(source_feed["last_updated"])] if last_updated_iso is not None else h.p(class_="font-medium text-slate-900")[str(source_feed["last_updated"])] ) return ( h.div[ h.div(class_="font-semibold text-slate-950")[str(source_feed["source"])], h.p(class_="mt-0.5 font-mono text-[11px] text-slate-500")[ str(source_feed["slug"]) ], ], h.div(class_="min-w-64")[ inline_link( href=str(source_feed["feed_href"]), label=str(source_feed["feed_href"]), tone="amber", ) ], status_badge( label=str(source_feed["feed_status_label"]), tone=str(source_feed["feed_status_tone"]), ), last_updated, h.p(class_="font-medium text-slate-900")[ str(source_feed["artifact_footprint"]) ], ) def published_feeds_table( *, source_feeds: tuple[Mapping[str, object], ...] | None = None ) -> Renderable: rows = tuple(_source_feed_row(source_feed) for source_feed in (source_feeds or ())) return table_section( eyebrow="Published feeds", title="Published feeds", empty_message="No feeds have been published yet.", headers=("Source", "Feed URL", "Status", "Last updated", "Disk usage"), rows=rows, actions=muted_action_link(href="/sources", label="Manage sources"), ) def dashboard_page() -> Renderable: return dashboard_page_with_data() def dashboard_page_with_data( *, snapshot: Mapping[str, str] | None = None, running_executions: tuple[Mapping[str, object], ...] | None = None, queued_executions: tuple[Mapping[str, object], ...] | None = None, source_feeds: tuple[Mapping[str, object], ...] | None = None, ) -> Renderable: running_items = running_executions or () queued_items = queued_executions or () source_items = source_feeds or () return app_shell( current_path="/", source_count=len(source_items), running_count=len(running_items), content=( dashboard_header(), operational_snapshot(snapshot=snapshot), live_work_section( running_executions=running_items, queued_executions=queued_items, ), published_feeds_table(source_feeds=source_items), relative_time_formatter_script(), ), )