generated from john/python-template
UI style refresh: extract reusable components and refactor
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
from contextlib import contextmanager
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
|
||||
@contextmanager
|
||||
def section_header_row(*, classes: str = ""):
|
||||
"""Render a standardized section header row container."""
|
||||
base_classes = "w-full items-center justify-between pb-2 ui-header-divider"
|
||||
with ui.row().classes(f"{base_classes} {classes}".strip()) as row:
|
||||
yield row
|
||||
|
||||
|
||||
def render_empty_state(message: str, *, italic: bool = False, extra_classes: str = "") -> None:
|
||||
"""Render standardized empty-state helper text."""
|
||||
classes = "text-xs ui-text-muted"
|
||||
if italic:
|
||||
classes = f"{classes} italic"
|
||||
ui.label(message).classes(f"{classes} {extra_classes}".strip())
|
||||
@@ -21,6 +21,8 @@ from transcription.ui.components.cards import archival_card
|
||||
from transcription.ui.components.data_display import archival_badge
|
||||
from transcription.ui.components.data_display import metadata_row
|
||||
from transcription.ui.components.error_presenter import show_error
|
||||
from transcription.ui.components.primitives import render_empty_state
|
||||
from transcription.ui.components.primitives import section_header_row
|
||||
from transcription.ui.components.table.documents import DocumentTableRow
|
||||
from transcription.ui.components.table.documents import render_documents_table
|
||||
from transcription.ui.components.viewers import dark_room_viewer
|
||||
@@ -147,7 +149,7 @@ def register_page() -> None:
|
||||
render_navigation_header(current_path="/documents")
|
||||
|
||||
with ui.column().classes("w-full max-w-7xl mx-auto p-4 gap-4"):
|
||||
with ui.row().classes("w-full items-center justify-between pb-2 ui-header-divider"):
|
||||
with section_header_row():
|
||||
page_header("Archival Documents")
|
||||
ui.button(
|
||||
"Create new document",
|
||||
@@ -211,7 +213,7 @@ def register_page() -> None:
|
||||
# Main Bento Grid Wrapper
|
||||
with ui.column().classes("w-full max-w-[1800px] mx-auto p-4 gap-4"):
|
||||
# Header Bar
|
||||
with ui.row().classes("w-full justify-between items-center pb-2 ui-header-divider"):
|
||||
with section_header_row():
|
||||
page_header(document.name, subtitle=f"Type: {document.document_type or 'Unspecified'} | ID: {document.id}")
|
||||
|
||||
with ui.row().classes("items-center gap-2"):
|
||||
@@ -267,7 +269,7 @@ def register_page() -> None:
|
||||
with ui.column().classes("col-span-12 lg:col-span-3 gap-4"):
|
||||
with archival_card(title="Related People"):
|
||||
if not document.document_people:
|
||||
ui.label("No linked people yet.").classes("text-xs ui-text-muted italic")
|
||||
render_empty_state("No linked people yet.", italic=True)
|
||||
else:
|
||||
with ui.column().classes("w-full gap-2"):
|
||||
for link in document.document_people:
|
||||
@@ -316,7 +318,7 @@ def register_page() -> None:
|
||||
return
|
||||
|
||||
with ui.column().classes("w-full max-w-4xl mx-auto p-4 gap-4"):
|
||||
with ui.row().classes("w-full justify-between items-center pb-2 ui-header-divider"):
|
||||
with section_header_row():
|
||||
page_header(f"Jobs for {document.name}")
|
||||
with ui.row().classes("gap-2"):
|
||||
ui.button(
|
||||
@@ -332,7 +334,7 @@ def register_page() -> None:
|
||||
|
||||
if not document.jobs:
|
||||
with archival_card(extra_classes="p-6 text-center"):
|
||||
ui.label("No transcription processing jobs created yet.").classes("text-xs ui-text-muted")
|
||||
render_empty_state("No transcription processing jobs created yet.")
|
||||
return
|
||||
|
||||
for job in sorted(document.jobs, key=lambda item: item.date_created, reverse=True):
|
||||
|
||||
@@ -19,6 +19,8 @@ from transcription.ui.components.cards import archival_card
|
||||
from transcription.ui.components.data_display import archival_badge
|
||||
from transcription.ui.components.data_display import metadata_row
|
||||
from transcription.ui.components.error_presenter import show_error
|
||||
from transcription.ui.components.primitives import render_empty_state
|
||||
from transcription.ui.components.primitives import section_header_row
|
||||
from transcription.ui.components.table.jobs import render_jobs_table
|
||||
from transcription.ui.theme import apply_archival_theme
|
||||
from transcription.ui.theme import page_header
|
||||
@@ -38,7 +40,7 @@ def register_page() -> None: # noqa: PLR0915
|
||||
render_navigation_header(current_path="/jobs")
|
||||
|
||||
with ui.column().classes("w-full max-w-7xl mx-auto p-4 gap-4"):
|
||||
with ui.row().classes("w-full items-center justify-between pb-2 ui-header-divider"):
|
||||
with section_header_row():
|
||||
page_header("Transcription Pipeline Jobs")
|
||||
with ui.row().classes("items-center gap-2"):
|
||||
ui.button("Create job", on_click=lambda: ui.navigate.to("/jobs/new"), icon="add").classes(
|
||||
@@ -75,8 +77,9 @@ def register_page() -> None: # noqa: PLR0915
|
||||
documents = await documents_service.list_documents()
|
||||
if not documents:
|
||||
with archival_card(extra_classes="p-6 text-center"):
|
||||
ui.label("No documents available. Create a Document before creating a Job.").classes(
|
||||
"text-xs text-red-800 font-medium mb-4"
|
||||
render_empty_state(
|
||||
"No documents available. Create a Document before creating a Job.",
|
||||
extra_classes="text-red-800 font-medium mb-4",
|
||||
)
|
||||
with ui.row().classes("justify-center gap-2"):
|
||||
ui.button(
|
||||
@@ -111,7 +114,7 @@ def register_page() -> None: # noqa: PLR0915
|
||||
@ui.refreshable
|
||||
def render_upload_list() -> None:
|
||||
if not uploaded_files:
|
||||
ui.label("No files uploaded yet.").classes("text-xs ui-text-muted italic")
|
||||
render_empty_state("No files uploaded yet.", italic=True)
|
||||
return
|
||||
|
||||
def remove_file(index: int) -> None:
|
||||
@@ -217,7 +220,7 @@ def register_page() -> None: # noqa: PLR0915
|
||||
return
|
||||
|
||||
with ui.column().classes("w-full max-w-4xl mx-auto p-4 gap-4"):
|
||||
with ui.row().classes("w-full justify-between items-center pb-2 ui-header-divider"):
|
||||
with section_header_row(classes="justify-between items-center"):
|
||||
page_header(f"Job Record: {job.id}")
|
||||
with ui.row().classes("items-center gap-2"):
|
||||
archival_badge(job.status.value.upper())
|
||||
|
||||
@@ -22,6 +22,8 @@ from transcription.ui.components.app_shell import render_navigation_header
|
||||
from transcription.ui.components.cards import archival_card
|
||||
from transcription.ui.components.data_display import metadata_row
|
||||
from transcription.ui.components.error_presenter import show_error
|
||||
from transcription.ui.components.primitives import render_empty_state
|
||||
from transcription.ui.components.primitives import section_header_row
|
||||
from transcription.ui.components.table.people import PersonTableRow, render_people_table
|
||||
from transcription.ui.components.viewers import dark_room_viewer
|
||||
from transcription.ui.theme import apply_archival_theme
|
||||
@@ -106,7 +108,7 @@ def register_page() -> None: # noqa: PLR0915
|
||||
render_navigation_header(current_path="/people")
|
||||
|
||||
with ui.column().classes("w-full max-w-7xl mx-auto p-4 gap-4"):
|
||||
with ui.row().classes("w-full items-center justify-between pb-2 ui-header-divider"):
|
||||
with section_header_row():
|
||||
page_header("Archival Entities: People")
|
||||
ui.button(
|
||||
"Create new person",
|
||||
@@ -230,7 +232,7 @@ def register_page() -> None: # noqa: PLR0915
|
||||
portrait_src = _resolve_portrait_src(person.portrait_path)
|
||||
|
||||
with ui.column().classes("w-full max-w-[1800px] mx-auto p-4 gap-4"):
|
||||
with ui.row().classes("w-full justify-between items-center pb-2 ui-header-divider"):
|
||||
with section_header_row():
|
||||
page_header(person.full_name, subtitle=f"Person ID: {person.id}")
|
||||
|
||||
with ui.row().classes("items-center gap-2"):
|
||||
@@ -273,8 +275,8 @@ def register_page() -> None: # noqa: PLR0915
|
||||
|
||||
with archival_card(title="Linked Documents"):
|
||||
if not person.document_people:
|
||||
ui.label("No linked documents yet.").classes("text-xs ui-text-muted italic")
|
||||
ui.label("Link this person from a Document workflow.").classes("text-xs ui-text-muted")
|
||||
render_empty_state("No linked documents yet.", italic=True)
|
||||
render_empty_state("Link this person from a Document workflow.")
|
||||
else:
|
||||
with ui.column().classes("w-full gap-2"):
|
||||
for link in person.document_people:
|
||||
|
||||
@@ -21,6 +21,7 @@ from transcription.ui.components.cards import archival_card
|
||||
from transcription.ui.components.data_display import metadata_row
|
||||
from transcription.ui.components.document_panzoom import render_document_panzoom
|
||||
from transcription.ui.components.error_presenter import show_error
|
||||
from transcription.ui.components.primitives import section_header_row
|
||||
from transcription.ui.components.table.sources import SourceTableRow, render_sources_table
|
||||
from transcription.ui.theme import apply_archival_theme
|
||||
from transcription.ui.theme import page_header
|
||||
@@ -76,7 +77,7 @@ def register_page() -> None:
|
||||
return
|
||||
|
||||
with ui.column().classes("w-full max-w-7xl mx-auto p-4 gap-4"):
|
||||
with ui.row().classes("w-full items-center justify-between pb-2 ui-header-divider"):
|
||||
with section_header_row():
|
||||
if document_name is not None:
|
||||
header_title = f"Sources: {document_name}"
|
||||
elif job_label is not None:
|
||||
@@ -129,7 +130,7 @@ def register_page() -> None:
|
||||
back_path = _back_path_from_query(request.query_params)
|
||||
|
||||
with ui.column().classes("w-full max-w-[1800px] mx-auto p-4 gap-4"):
|
||||
with ui.row().classes("w-full justify-between items-center pb-2 ui-header-divider"):
|
||||
with section_header_row():
|
||||
page_header(f"Source Page {source.page_number}: {source.upload_name}", subtitle=f"Source ID: {source.id}")
|
||||
|
||||
if back_path is not None:
|
||||
|
||||
Reference in New Issue
Block a user