Add publisher dashboard routes
This commit is contained in:
parent
96551c2788
commit
e4a5246ab3
31 changed files with 1603 additions and 516 deletions
|
|
@ -9,7 +9,6 @@ from htpy import Node, Renderable
|
|||
from repub.components import (
|
||||
action_button,
|
||||
app_shell,
|
||||
header_action_link,
|
||||
inline_link,
|
||||
muted_action_link,
|
||||
stat_card,
|
||||
|
|
@ -19,7 +18,9 @@ from repub.components import (
|
|||
from repub.pages.runs import live_work_section, relative_time_formatter_script
|
||||
|
||||
|
||||
def dashboard_header() -> Renderable:
|
||||
def dashboard_header(
|
||||
*, path_prefix: str = "/admin", reader_app_url: str | None = None
|
||||
) -> Renderable:
|
||||
return h.section[
|
||||
h.div(
|
||||
class_="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between"
|
||||
|
|
@ -30,8 +31,20 @@ def dashboard_header() -> Renderable:
|
|||
],
|
||||
],
|
||||
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"),
|
||||
(
|
||||
muted_action_link(
|
||||
href=reader_app_url,
|
||||
label="Open AnyNews",
|
||||
target="_blank",
|
||||
rel="noopener noreferrer",
|
||||
)
|
||||
if reader_app_url is not None
|
||||
else None
|
||||
),
|
||||
muted_action_link(
|
||||
href=f"{path_prefix}/publisher",
|
||||
label="Publisher View",
|
||||
),
|
||||
],
|
||||
]
|
||||
]
|
||||
|
|
@ -124,9 +137,6 @@ def _source_feed_row(source_feed: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
),
|
||||
last_updated,
|
||||
next_run,
|
||||
h.p(class_="font-medium text-slate-900")[
|
||||
str(source_feed["artifact_footprint"])
|
||||
],
|
||||
action_button(
|
||||
label="Run now",
|
||||
disabled=bool(source_feed["run_disabled"]),
|
||||
|
|
@ -136,12 +146,15 @@ def _source_feed_row(source_feed: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
|
||||
|
||||
def published_feeds_table(
|
||||
*, source_feeds: tuple[Mapping[str, object], ...] | None = None
|
||||
*,
|
||||
source_feeds: tuple[Mapping[str, object], ...] | None = None,
|
||||
manage_sources_href: str | None = "/admin/sources",
|
||||
show_heading: bool = True,
|
||||
) -> Renderable:
|
||||
rows = tuple(_source_feed_row(source_feed) for source_feed in (source_feeds or ()))
|
||||
return table_section(
|
||||
eyebrow="Published feeds",
|
||||
title="Published feeds",
|
||||
eyebrow="Published feeds" if show_heading else None,
|
||||
title="Published feeds" if show_heading else None,
|
||||
empty_message="No feeds have been published yet.",
|
||||
headers=(
|
||||
"Source",
|
||||
|
|
@ -149,11 +162,14 @@ def published_feeds_table(
|
|||
"Status",
|
||||
"Last updated",
|
||||
"Next run",
|
||||
"Disk usage",
|
||||
"Actions",
|
||||
),
|
||||
rows=rows,
|
||||
actions=muted_action_link(href="/sources", label="Manage sources"),
|
||||
actions=(
|
||||
muted_action_link(href=manage_sources_href, label="Manage sources")
|
||||
if manage_sources_href is not None
|
||||
else None
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -163,26 +179,32 @@ def dashboard_page() -> Renderable:
|
|||
|
||||
def dashboard_page_with_data(
|
||||
*,
|
||||
current_path: str = "/admin",
|
||||
path_prefix: str = "/admin",
|
||||
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,
|
||||
reader_app_url: str | None = None,
|
||||
) -> Renderable:
|
||||
running_items = running_executions or ()
|
||||
queued_items = queued_executions or ()
|
||||
source_items = source_feeds or ()
|
||||
return app_shell(
|
||||
current_path="/",
|
||||
current_path=current_path,
|
||||
source_count=len(source_items),
|
||||
running_count=len(running_items),
|
||||
content=(
|
||||
dashboard_header(),
|
||||
dashboard_header(path_prefix=path_prefix, reader_app_url=reader_app_url),
|
||||
operational_snapshot(snapshot=snapshot),
|
||||
live_work_section(
|
||||
running_executions=running_items,
|
||||
queued_executions=queued_items,
|
||||
),
|
||||
published_feeds_table(source_feeds=source_items),
|
||||
published_feeds_table(
|
||||
source_feeds=source_items,
|
||||
manage_sources_href=f"{path_prefix}/sources",
|
||||
),
|
||||
relative_time_formatter_script(),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,57 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
|
||||
import htpy as h
|
||||
from htpy import Renderable
|
||||
|
||||
from repub.components import app_shell
|
||||
from repub.components import publisher_shell
|
||||
from repub.pages.dashboard import published_feeds_table
|
||||
from repub.pages.runs import live_work_section, relative_time_formatter_script
|
||||
|
||||
|
||||
def publisher_page(*, current_path: str) -> Renderable:
|
||||
return app_shell(
|
||||
def publisher_page(
|
||||
*,
|
||||
current_path: str,
|
||||
source_feeds: tuple[Mapping[str, object], ...] | None = None,
|
||||
running_executions: tuple[Mapping[str, object], ...] | None = None,
|
||||
queued_executions: tuple[Mapping[str, object], ...] | None = None,
|
||||
reader_app_url: str | None = None,
|
||||
) -> Renderable:
|
||||
return publisher_shell(
|
||||
current_path=current_path,
|
||||
content=(
|
||||
h.section[
|
||||
h.h1(class_="text-3xl font-semibold tracking-tight text-slate-950")[
|
||||
"Hello publishers"
|
||||
]
|
||||
h.div(
|
||||
class_="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between"
|
||||
)[
|
||||
h.div[
|
||||
h.p(
|
||||
class_="text-xs font-semibold uppercase tracking-[0.22em] text-amber-600"
|
||||
)["Republisher"],
|
||||
h.h1(
|
||||
class_="mt-1 text-3xl font-semibold tracking-tight text-slate-950"
|
||||
)["Published feeds"],
|
||||
],
|
||||
reader_app_url
|
||||
and h.a(
|
||||
href=reader_app_url,
|
||||
target="_blank",
|
||||
rel="noopener noreferrer",
|
||||
class_="inline-flex shrink-0 items-center justify-center rounded-full bg-amber-400 px-4 py-2.5 text-sm font-semibold text-slate-950 transition hover:bg-amber-300",
|
||||
)["Open AnyNews"],
|
||||
],
|
||||
],
|
||||
published_feeds_table(
|
||||
source_feeds=source_feeds,
|
||||
manage_sources_href=None,
|
||||
show_heading=False,
|
||||
),
|
||||
live_work_section(
|
||||
running_executions=running_executions,
|
||||
queued_executions=queued_executions,
|
||||
show_row_actions=False,
|
||||
),
|
||||
relative_time_formatter_script(),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -190,7 +190,9 @@ def _queue_row_attrs(execution: Mapping[str, object]) -> dict[str, str]:
|
|||
}
|
||||
|
||||
|
||||
def _running_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
||||
def _running_row(
|
||||
execution: Mapping[str, object], *, show_row_actions: bool = True
|
||||
) -> tuple[Node, ...]:
|
||||
started_at = _maybe_text(execution, "started_at_iso")
|
||||
started_at_label: Node = h.p(class_="truncate")[_text(execution, "started_at")]
|
||||
if started_at is not None:
|
||||
|
|
@ -203,7 +205,7 @@ def _running_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
class_="truncate",
|
||||
)[_text(execution, "started_at")]
|
||||
|
||||
return (
|
||||
cells = (
|
||||
_live_status_cell(
|
||||
execution_id=_text(execution, "execution_id"),
|
||||
status=_text(execution, "status"),
|
||||
|
|
@ -222,6 +224,11 @@ def _running_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
h.p(class_="font-medium text-slate-900")[_text(execution, "stats")],
|
||||
h.p(class_="mt-0.5 text-xs text-slate-500")[_text(execution, "worker")],
|
||||
],
|
||||
)
|
||||
if not show_row_actions:
|
||||
return cells
|
||||
return (
|
||||
*cells,
|
||||
h.div(class_="flex flex-wrap items-center gap-2")[
|
||||
inline_link(
|
||||
href=_text(execution, "log_href"),
|
||||
|
|
@ -237,7 +244,9 @@ def _running_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
)
|
||||
|
||||
|
||||
def _queued_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
||||
def _queued_row(
|
||||
execution: Mapping[str, object], *, show_row_actions: bool = True
|
||||
) -> tuple[Node, ...]:
|
||||
queued_at = _maybe_text(execution, "queued_at_iso")
|
||||
queued_label: Node = h.p(class_="truncate")[_text(execution, "queued_at")]
|
||||
if queued_at is not None:
|
||||
|
|
@ -250,7 +259,7 @@ def _queued_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
class_="truncate",
|
||||
)[_text(execution, "queued_at")]
|
||||
|
||||
return (
|
||||
cells = (
|
||||
_live_status_cell(
|
||||
execution_id=_text(execution, "execution_id"),
|
||||
status="Queued",
|
||||
|
|
@ -270,6 +279,11 @@ def _queued_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
],
|
||||
h.p(class_="mt-0.5 text-xs text-slate-500")["waiting for capacity"],
|
||||
],
|
||||
)
|
||||
if not show_row_actions:
|
||||
return cells
|
||||
return (
|
||||
*cells,
|
||||
h.div(class_="flex flex-wrap items-center gap-2")[
|
||||
action_button(
|
||||
label=_queue_icon("up"),
|
||||
|
|
@ -362,8 +376,8 @@ def _completed_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
)
|
||||
|
||||
|
||||
def _completed_page_action_path(page: int) -> str:
|
||||
return f"/actions/runs/completed-page/{page}"
|
||||
def _completed_page_action_path(page: int, *, path_prefix: str = "/admin") -> str:
|
||||
return f"{path_prefix}/actions/runs/completed-page/{page}"
|
||||
|
||||
|
||||
def _pagination_button(
|
||||
|
|
@ -372,9 +386,12 @@ def _pagination_button(
|
|||
page: int,
|
||||
current: bool = False,
|
||||
class_name: str,
|
||||
path_prefix: str = "/admin",
|
||||
) -> Renderable:
|
||||
attributes = {
|
||||
"data-on:pointerdown": f"@post('{_completed_page_action_path(page)}')",
|
||||
"data-on:pointerdown": (
|
||||
f"@post('{_completed_page_action_path(page, path_prefix=path_prefix)}')"
|
||||
),
|
||||
}
|
||||
if current:
|
||||
attributes["aria-current"] = "page"
|
||||
|
|
@ -391,6 +408,7 @@ def _completed_history_pagination(
|
|||
completed_page_size: int,
|
||||
completed_total_count: int,
|
||||
completed_total_pages: int,
|
||||
path_prefix: str = "/admin",
|
||||
) -> Renderable | None:
|
||||
if completed_total_count <= completed_page_size:
|
||||
return None
|
||||
|
|
@ -410,6 +428,7 @@ def _completed_history_pagination(
|
|||
_pagination_button(
|
||||
label="Previous",
|
||||
page=max(1, completed_page - 1),
|
||||
path_prefix=path_prefix,
|
||||
class_name=(
|
||||
"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"
|
||||
|
|
@ -418,6 +437,7 @@ def _completed_history_pagination(
|
|||
_pagination_button(
|
||||
label="Next",
|
||||
page=min(completed_total_pages, completed_page + 1),
|
||||
path_prefix=path_prefix,
|
||||
class_name=(
|
||||
"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"
|
||||
|
|
@ -443,6 +463,7 @@ def _completed_history_pagination(
|
|||
label=str(page_number),
|
||||
page=page_number,
|
||||
current=page_number == completed_page,
|
||||
path_prefix=path_prefix,
|
||||
class_name=(
|
||||
"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
|
||||
|
|
@ -463,12 +484,14 @@ def _completed_history_section(
|
|||
completed_page_size: int,
|
||||
completed_total_count: int,
|
||||
completed_total_pages: int,
|
||||
path_prefix: str = "/admin",
|
||||
) -> 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,
|
||||
path_prefix=path_prefix,
|
||||
)
|
||||
return h.section[
|
||||
table_section(
|
||||
|
|
@ -486,7 +509,7 @@ def _completed_history_section(
|
|||
action_button(
|
||||
label="Clear history",
|
||||
tone="danger",
|
||||
post_path="/actions/completed-executions/clear",
|
||||
post_path=f"{path_prefix}/actions/completed-executions/clear",
|
||||
)
|
||||
if completed_total_count > 0
|
||||
else None
|
||||
|
|
@ -501,11 +524,18 @@ def live_work_section(
|
|||
running_executions: tuple[Mapping[str, object], ...] | None = None,
|
||||
queued_executions: tuple[Mapping[str, object], ...] | None = None,
|
||||
actions: Node | None = None,
|
||||
show_row_actions: bool = True,
|
||||
) -> Renderable:
|
||||
running_items = running_executions or ()
|
||||
queued_items = queued_executions or ()
|
||||
running_rows = tuple(_running_row(execution) for execution in running_items)
|
||||
queued_rows = tuple(_queued_row(execution) for execution in queued_items)
|
||||
running_rows = tuple(
|
||||
_running_row(execution, show_row_actions=show_row_actions)
|
||||
for execution in running_items
|
||||
)
|
||||
queued_rows = tuple(
|
||||
_queued_row(execution, show_row_actions=show_row_actions)
|
||||
for execution in queued_items
|
||||
)
|
||||
live_rows = running_rows + queued_rows
|
||||
live_row_attrs = tuple(
|
||||
_queue_row_attrs(execution) for execution in running_items + queued_items
|
||||
|
|
@ -515,10 +545,9 @@ def live_work_section(
|
|||
title="Running jobs",
|
||||
empty_message="No jobs are running or queued.",
|
||||
headers=(
|
||||
"State",
|
||||
"Source",
|
||||
"Details",
|
||||
"Actions",
|
||||
("State", "Source", "Details", "Actions")
|
||||
if show_row_actions
|
||||
else ("State", "Source", "Details")
|
||||
),
|
||||
rows=live_rows,
|
||||
row_attrs=live_row_attrs,
|
||||
|
|
@ -585,6 +614,7 @@ def runs_page(
|
|||
completed_total_count: int | None = None,
|
||||
completed_total_pages: int | None = None,
|
||||
source_count: int = 0,
|
||||
path_prefix: str = "/admin",
|
||||
) -> Renderable:
|
||||
upcoming_items = upcoming_jobs or ()
|
||||
completed_items = completed_executions or ()
|
||||
|
|
@ -598,10 +628,13 @@ def runs_page(
|
|||
)
|
||||
|
||||
return page_shell(
|
||||
current_path="/runs",
|
||||
current_path=f"{path_prefix}/runs",
|
||||
eyebrow="Execution control",
|
||||
title="Runs",
|
||||
actions=muted_action_link(href="/sources", label="Back to sources"),
|
||||
actions=muted_action_link(
|
||||
href=f"{path_prefix}/sources",
|
||||
label="Back to sources",
|
||||
),
|
||||
source_count=source_count,
|
||||
running_count=len(running_executions or ()),
|
||||
content=(
|
||||
|
|
@ -629,6 +662,7 @@ def runs_page(
|
|||
completed_page_size=completed_page_size,
|
||||
completed_total_count=resolved_completed_total_count,
|
||||
completed_total_pages=resolved_completed_total_pages,
|
||||
path_prefix=path_prefix,
|
||||
),
|
||||
relative_time_formatter_script(),
|
||||
),
|
||||
|
|
@ -640,6 +674,7 @@ def execution_logs_page(
|
|||
job_id: int,
|
||||
execution_id: int,
|
||||
log_view: Mapping[str, object] | None = None,
|
||||
path_prefix: str = "/admin",
|
||||
) -> Renderable:
|
||||
if log_view is None:
|
||||
log_view = {
|
||||
|
|
@ -664,10 +699,10 @@ def execution_logs_page(
|
|||
)
|
||||
|
||||
return page_shell(
|
||||
current_path=f"/job/{job_id}/execution/{execution_id}/logs",
|
||||
current_path=f"{path_prefix}/job/{job_id}/execution/{execution_id}/logs",
|
||||
eyebrow="Execution log",
|
||||
title=_text(log_view, "title"),
|
||||
actions=muted_action_link(href="/runs", label="Back to runs"),
|
||||
actions=muted_action_link(href=f"{path_prefix}/runs", label="Back to runs"),
|
||||
content=(
|
||||
section_card(
|
||||
content=(
|
||||
|
|
@ -677,7 +712,7 @@ def execution_logs_page(
|
|||
class_="text-xs font-semibold uppercase tracking-[0.22em] text-amber-600"
|
||||
)["Route"],
|
||||
h.h2(class_="mt-2 text-xl font-semibold text-slate-950")[
|
||||
f"/job/{job_id}/execution/{execution_id}/logs"
|
||||
f"{path_prefix}/job/{job_id}/execution/{execution_id}/logs"
|
||||
],
|
||||
],
|
||||
status_badge(
|
||||
|
|
|
|||
|
|
@ -23,12 +23,13 @@ def _value(settings: Mapping[str, object] | None, key: str, default: str = "") -
|
|||
def settings_page(
|
||||
*,
|
||||
settings: Mapping[str, object] | None = None,
|
||||
action_path: str = "/actions/settings",
|
||||
action_path: str = "/admin/actions/settings",
|
||||
source_count: int = 0,
|
||||
running_count: int = 0,
|
||||
path_prefix: str = "/admin",
|
||||
) -> Renderable:
|
||||
return page_shell(
|
||||
current_path="/settings",
|
||||
current_path=f"{path_prefix}/settings",
|
||||
eyebrow="Configuration",
|
||||
title="Settings",
|
||||
description="Global runtime controls for the republisher.",
|
||||
|
|
@ -85,7 +86,10 @@ def settings_page(
|
|||
),
|
||||
],
|
||||
h.div(class_="flex flex-wrap justify-end gap-3 pt-2")[
|
||||
muted_action_link(href="/", label="Back to dashboard"),
|
||||
muted_action_link(
|
||||
href=path_prefix,
|
||||
label="Back to dashboard",
|
||||
),
|
||||
action_button(
|
||||
label="Save settings",
|
||||
tone="dark",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
import htpy as h
|
||||
from htpy import Node, Renderable
|
||||
|
||||
from repub.components import app_shell
|
||||
from repub.components import app_shell, publisher_shell
|
||||
|
||||
ON_LOAD_JS = (
|
||||
"@post(window.location.pathname + "
|
||||
|
|
@ -17,6 +17,7 @@ TAB_ID_JS = "self.crypto.randomUUID().substring(0,8)"
|
|||
def shim_page(
|
||||
*, datastar_src: str, current_path: str, head: Node | None = None
|
||||
) -> Renderable:
|
||||
shell = app_shell if current_path.startswith("/admin") else publisher_shell
|
||||
return h.html(lang="en")[
|
||||
h.head[
|
||||
h.meta(charset="UTF-8"),
|
||||
|
|
@ -33,7 +34,7 @@ def shim_page(
|
|||
}
|
||||
),
|
||||
h.noscript["Your browser does not support JavaScript!"],
|
||||
app_shell(
|
||||
shell(
|
||||
current_path=current_path,
|
||||
content=(
|
||||
h.section[
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@ def _checked(source: Mapping[str, object] | None, key: str, default: bool) -> bo
|
|||
return bool(value)
|
||||
|
||||
|
||||
def _source_row(source: Mapping[str, object]) -> tuple[Node, ...]:
|
||||
def _source_row(
|
||||
source: Mapping[str, object], *, path_prefix: str = "/admin"
|
||||
) -> tuple[Node, ...]:
|
||||
return (
|
||||
h.div[
|
||||
h.div(class_="font-semibold text-slate-950")[str(source["name"])],
|
||||
|
|
@ -78,28 +80,37 @@ def _source_row(source: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
],
|
||||
h.div(class_="flex flex-nowrap items-center gap-3 whitespace-nowrap")[
|
||||
inline_link(
|
||||
href=f"/sources/{source['slug']}/edit", label="Edit", tone="amber"
|
||||
href=f"{path_prefix}/sources/{source['slug']}/edit",
|
||||
label="Edit",
|
||||
tone="amber",
|
||||
),
|
||||
action_button(
|
||||
label="Delete",
|
||||
tone="danger",
|
||||
post_path=f"/actions/sources/{source['slug']}/delete",
|
||||
post_path=f"{path_prefix}/actions/sources/{source['slug']}/delete",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def sources_table(
|
||||
*, sources: tuple[Mapping[str, object], ...] | None = None
|
||||
*,
|
||||
sources: tuple[Mapping[str, object], ...] | None = None,
|
||||
path_prefix: str = "/admin",
|
||||
) -> Renderable:
|
||||
rows = tuple(_source_row(source) for source in (sources or ()))
|
||||
rows = tuple(
|
||||
_source_row(source, path_prefix=path_prefix) for source in (sources or ())
|
||||
)
|
||||
return table_section(
|
||||
eyebrow="Inventory",
|
||||
title="Sources",
|
||||
empty_message="No sources yet.",
|
||||
headers=("Source", "Type", "Upstream", "Schedule", "Job state", "Actions"),
|
||||
rows=rows,
|
||||
actions=header_action_link(href="/sources/create", label="Create source"),
|
||||
actions=header_action_link(
|
||||
href=f"{path_prefix}/sources/create",
|
||||
label="Create source",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -107,15 +118,16 @@ def sources_page(
|
|||
*,
|
||||
sources: tuple[Mapping[str, object], ...] | None = None,
|
||||
running_count: int = 0,
|
||||
path_prefix: str = "/admin",
|
||||
) -> Renderable:
|
||||
source_items = sources or ()
|
||||
return page_shell(
|
||||
current_path="/sources",
|
||||
current_path=f"{path_prefix}/sources",
|
||||
eyebrow="Source management",
|
||||
title="Sources",
|
||||
source_count=len(source_items),
|
||||
running_count=running_count,
|
||||
content=sources_table(sources=source_items),
|
||||
content=sources_table(sources=source_items, path_prefix=path_prefix),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -124,6 +136,7 @@ def source_form(
|
|||
mode: str,
|
||||
action_path: str,
|
||||
source: Mapping[str, object] | None = None,
|
||||
path_prefix: str = "/admin",
|
||||
) -> Renderable:
|
||||
source_type = _value(source, "source_type", "pangea")
|
||||
slug = _value(source, "slug")
|
||||
|
|
@ -397,7 +410,7 @@ def source_form(
|
|||
h.div(
|
||||
class_="flex flex-wrap justify-end gap-3 border-t border-slate-200 pt-6"
|
||||
)[
|
||||
muted_action_link(href="/sources", label="Cancel"),
|
||||
muted_action_link(href=f"{path_prefix}/sources", label="Cancel"),
|
||||
action_button(
|
||||
label=submit_label,
|
||||
tone="dark",
|
||||
|
|
@ -412,22 +425,27 @@ def source_form(
|
|||
|
||||
def create_source_page(
|
||||
*,
|
||||
action_path: str = "/actions/sources/create",
|
||||
action_path: str = "/admin/actions/sources/create",
|
||||
source_count: int = 0,
|
||||
running_count: int = 0,
|
||||
path_prefix: str = "/admin",
|
||||
) -> Renderable:
|
||||
actions = (
|
||||
muted_action_link(href="/sources", label="Back to sources"),
|
||||
header_action_link(href="/runs", label="View runs"),
|
||||
muted_action_link(href=f"{path_prefix}/sources", label="Back to sources"),
|
||||
header_action_link(href=f"{path_prefix}/runs", label="View runs"),
|
||||
)
|
||||
return page_shell(
|
||||
current_path="/sources/create",
|
||||
current_path=f"{path_prefix}/sources/create",
|
||||
eyebrow="Source creation",
|
||||
title="Create source",
|
||||
actions=actions,
|
||||
source_count=source_count,
|
||||
running_count=running_count,
|
||||
content=source_form(mode="create", action_path=action_path),
|
||||
content=source_form(
|
||||
mode="create",
|
||||
action_path=action_path,
|
||||
path_prefix=path_prefix,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -438,17 +456,23 @@ def edit_source_page(
|
|||
action_path: str,
|
||||
source_count: int = 0,
|
||||
running_count: int = 0,
|
||||
path_prefix: str = "/admin",
|
||||
) -> Renderable:
|
||||
actions = (
|
||||
muted_action_link(href="/sources", label="Back to sources"),
|
||||
header_action_link(href="/runs", label="View runs"),
|
||||
muted_action_link(href=f"{path_prefix}/sources", label="Back to sources"),
|
||||
header_action_link(href=f"{path_prefix}/runs", label="View runs"),
|
||||
)
|
||||
return page_shell(
|
||||
current_path=f"/sources/{slug}/edit",
|
||||
current_path=f"{path_prefix}/sources/{slug}/edit",
|
||||
eyebrow="Source editing",
|
||||
title="Edit source",
|
||||
actions=actions,
|
||||
source_count=source_count,
|
||||
running_count=running_count,
|
||||
content=source_form(mode="edit", action_path=action_path, source=source),
|
||||
content=source_form(
|
||||
mode="edit",
|
||||
action_path=action_path,
|
||||
source=source,
|
||||
path_prefix=path_prefix,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue