generated from john/python-template
UI style refresh: Final cleanup
This commit is contained in:
@@ -4,10 +4,16 @@ from transcription.ui.components.app_shell import NAV_ITEMS
|
|||||||
from transcription.ui.components.app_shell import render_app_shell
|
from transcription.ui.components.app_shell import render_app_shell
|
||||||
from transcription.ui.components.app_shell import render_navigation_header
|
from transcription.ui.components.app_shell import render_navigation_header
|
||||||
from transcription.ui.components.document_panzoom import render_document_panzoom
|
from transcription.ui.components.document_panzoom import render_document_panzoom
|
||||||
|
from transcription.ui.components.primitives import destructive_button
|
||||||
|
from transcription.ui.components.primitives import render_empty_state
|
||||||
|
from transcription.ui.components.primitives import section_header_row
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"NAV_ITEMS",
|
"NAV_ITEMS",
|
||||||
|
"destructive_button",
|
||||||
"render_app_shell",
|
"render_app_shell",
|
||||||
"render_document_panzoom",
|
"render_document_panzoom",
|
||||||
|
"render_empty_state",
|
||||||
"render_navigation_header",
|
"render_navigation_header",
|
||||||
|
"section_header_row",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -17,3 +17,22 @@ def render_empty_state(message: str, *, italic: bool = False, extra_classes: str
|
|||||||
if italic:
|
if italic:
|
||||||
classes = f"{classes} italic"
|
classes = f"{classes} italic"
|
||||||
ui.label(message).classes(f"{classes} {extra_classes}".strip())
|
ui.label(message).classes(f"{classes} {extra_classes}".strip())
|
||||||
|
|
||||||
|
|
||||||
|
def destructive_button(
|
||||||
|
label: str,
|
||||||
|
*,
|
||||||
|
on_click,
|
||||||
|
icon: str,
|
||||||
|
variant: str = "outlined",
|
||||||
|
extra_classes: str = "",
|
||||||
|
):
|
||||||
|
"""Render a standardized destructive action button."""
|
||||||
|
button = ui.button(label, on_click=on_click, icon=icon)
|
||||||
|
if variant == "solid":
|
||||||
|
button.props("unelevated color=negative")
|
||||||
|
else:
|
||||||
|
button.props("outlined color=negative")
|
||||||
|
if extra_classes:
|
||||||
|
button.classes(extra_classes)
|
||||||
|
return button
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from uuid import UUID
|
|||||||
from nicegui import ui
|
from nicegui import ui
|
||||||
|
|
||||||
from transcription.ui.components.cards import archival_card
|
from transcription.ui.components.cards import archival_card
|
||||||
|
from transcription.ui.components.primitives import render_empty_state
|
||||||
from transcription.ui.components.table.common import build_table
|
from transcription.ui.components.table.common import build_table
|
||||||
|
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ def render_documents_table(rows: Sequence[DocumentTableRow]) -> None:
|
|||||||
"""Render documents table and open detail page when clicking a row."""
|
"""Render documents table and open detail page when clicking a row."""
|
||||||
if not rows:
|
if not rows:
|
||||||
with archival_card(extra_classes="p-8 text-center"):
|
with archival_card(extra_classes="p-8 text-center"):
|
||||||
ui.label("No documents in repository yet.").classes("text-xs ui-text-muted")
|
render_empty_state("No documents in repository yet.")
|
||||||
return
|
return
|
||||||
|
|
||||||
build_table(
|
build_table(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from uuid import UUID
|
|||||||
from nicegui import ui
|
from nicegui import ui
|
||||||
|
|
||||||
from transcription.ui.components.cards import archival_card
|
from transcription.ui.components.cards import archival_card
|
||||||
|
from transcription.ui.components.primitives import render_empty_state
|
||||||
from .common import build_table
|
from .common import build_table
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ def render_jobs_table(rows: Sequence[JobTableRow]) -> None:
|
|||||||
"""Render jobs table and open a detail page when clicking a row."""
|
"""Render jobs table and open a detail page when clicking a row."""
|
||||||
if not rows:
|
if not rows:
|
||||||
with archival_card(extra_classes="p-8 text-center"):
|
with archival_card(extra_classes="p-8 text-center"):
|
||||||
ui.label("No active or historical processing jobs found.").classes("text-xs ui-text-muted")
|
render_empty_state("No active or historical processing jobs found.")
|
||||||
return
|
return
|
||||||
|
|
||||||
build_table(
|
build_table(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from uuid import UUID
|
|||||||
from nicegui import ui
|
from nicegui import ui
|
||||||
|
|
||||||
from transcription.ui.components.cards import archival_card
|
from transcription.ui.components.cards import archival_card
|
||||||
|
from transcription.ui.components.primitives import render_empty_state
|
||||||
from transcription.ui.components.table.common import build_table
|
from transcription.ui.components.table.common import build_table
|
||||||
|
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ def render_people_table(rows: Sequence[PersonTableRow]) -> None:
|
|||||||
"""Render people table and open detail page when clicking a row."""
|
"""Render people table and open detail page when clicking a row."""
|
||||||
if not rows:
|
if not rows:
|
||||||
with archival_card(extra_classes="p-8 text-center"):
|
with archival_card(extra_classes="p-8 text-center"):
|
||||||
ui.label("No person records found in repository.").classes("text-xs ui-text-muted")
|
render_empty_state("No person records found in repository.")
|
||||||
return
|
return
|
||||||
|
|
||||||
build_table(
|
build_table(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from uuid import UUID
|
|||||||
from nicegui import ui
|
from nicegui import ui
|
||||||
|
|
||||||
from transcription.ui.components.cards import archival_card
|
from transcription.ui.components.cards import archival_card
|
||||||
|
from transcription.ui.components.primitives import render_empty_state
|
||||||
from transcription.ui.components.table.common import build_table
|
from transcription.ui.components.table.common import build_table
|
||||||
|
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ def render_sources_table(rows: Sequence[SourceTableRow]) -> None:
|
|||||||
"""Render sources table and open detail page when clicking a row."""
|
"""Render sources table and open detail page when clicking a row."""
|
||||||
if not rows:
|
if not rows:
|
||||||
with archival_card(extra_classes="p-8 text-center"):
|
with archival_card(extra_classes="p-8 text-center"):
|
||||||
ui.label("No source file records found.").classes("text-xs ui-text-muted")
|
render_empty_state("No source file records found.")
|
||||||
return
|
return
|
||||||
|
|
||||||
build_table(
|
build_table(
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from transcription.ui.components.cards import archival_card
|
|||||||
from transcription.ui.components.data_display import archival_badge
|
from transcription.ui.components.data_display import archival_badge
|
||||||
from transcription.ui.components.data_display import metadata_row
|
from transcription.ui.components.data_display import metadata_row
|
||||||
from transcription.ui.components.error_presenter import show_error
|
from transcription.ui.components.error_presenter import show_error
|
||||||
|
from transcription.ui.components.primitives import destructive_button
|
||||||
from transcription.ui.components.primitives import render_empty_state
|
from transcription.ui.components.primitives import render_empty_state
|
||||||
from transcription.ui.components.primitives import section_header_row
|
from transcription.ui.components.primitives import section_header_row
|
||||||
from transcription.ui.components.table.documents import DocumentTableRow
|
from transcription.ui.components.table.documents import DocumentTableRow
|
||||||
@@ -222,11 +223,12 @@ def register_page() -> None:
|
|||||||
on_click=lambda: ui.navigate.to(f"/documents/{document.id}/edit"),
|
on_click=lambda: ui.navigate.to(f"/documents/{document.id}/edit"),
|
||||||
icon="edit",
|
icon="edit",
|
||||||
).classes("ui-btn-primary text-xs")
|
).classes("ui-btn-primary text-xs")
|
||||||
ui.button(
|
destructive_button(
|
||||||
"Delete",
|
"Delete",
|
||||||
on_click=lambda: ui.navigate.to(f"/documents/{document.id}/delete"),
|
on_click=lambda: ui.navigate.to(f"/documents/{document.id}/delete"),
|
||||||
icon="delete",
|
icon="delete",
|
||||||
).props("outlined color=negative text-xs")
|
extra_classes="text-xs",
|
||||||
|
)
|
||||||
|
|
||||||
# High-Density Bento Grid Layout
|
# High-Density Bento Grid Layout
|
||||||
with ui.grid().classes("w-full grid-cols-12 gap-4"):
|
with ui.grid().classes("w-full grid-cols-12 gap-4"):
|
||||||
@@ -587,11 +589,12 @@ def register_page() -> None:
|
|||||||
ui.navigate.to("/documents")
|
ui.navigate.to("/documents")
|
||||||
|
|
||||||
with ui.row().classes("w-full items-center gap-2 mt-2"):
|
with ui.row().classes("w-full items-center gap-2 mt-2"):
|
||||||
ui.button(
|
destructive_button(
|
||||||
"Delete document permanently",
|
"Delete document permanently",
|
||||||
on_click=submit_delete,
|
on_click=submit_delete,
|
||||||
icon="delete_forever",
|
icon="delete_forever",
|
||||||
).props("unelevated color=negative")
|
variant="solid",
|
||||||
|
)
|
||||||
ui.button("Cancel", on_click=lambda: ui.navigate.to(f"/documents/{document.id}"), icon="arrow_back").props(
|
ui.button("Cancel", on_click=lambda: ui.navigate.to(f"/documents/{document.id}"), icon="arrow_back").props(
|
||||||
"flat"
|
"flat"
|
||||||
)
|
)
|
||||||
@@ -19,6 +19,7 @@ from transcription.ui.components.cards import archival_card
|
|||||||
from transcription.ui.components.data_display import archival_badge
|
from transcription.ui.components.data_display import archival_badge
|
||||||
from transcription.ui.components.data_display import metadata_row
|
from transcription.ui.components.data_display import metadata_row
|
||||||
from transcription.ui.components.error_presenter import show_error
|
from transcription.ui.components.error_presenter import show_error
|
||||||
|
from transcription.ui.components.primitives import destructive_button
|
||||||
from transcription.ui.components.primitives import render_empty_state
|
from transcription.ui.components.primitives import render_empty_state
|
||||||
from transcription.ui.components.primitives import section_header_row
|
from transcription.ui.components.primitives import section_header_row
|
||||||
from transcription.ui.components.table.jobs import render_jobs_table
|
from transcription.ui.components.table.jobs import render_jobs_table
|
||||||
@@ -224,11 +225,12 @@ def register_page() -> None: # noqa: PLR0915
|
|||||||
page_header(f"Job Record: {job.id}")
|
page_header(f"Job Record: {job.id}")
|
||||||
with ui.row().classes("items-center gap-2"):
|
with ui.row().classes("items-center gap-2"):
|
||||||
archival_badge(job.status.value.upper())
|
archival_badge(job.status.value.upper())
|
||||||
ui.button(
|
destructive_button(
|
||||||
"Delete Job",
|
"Delete Job",
|
||||||
on_click=lambda: ui.navigate.to(f"/jobs/{job.id}/delete"),
|
on_click=lambda: ui.navigate.to(f"/jobs/{job.id}/delete"),
|
||||||
icon="delete",
|
icon="delete",
|
||||||
).props("outlined color=negative text-xs")
|
extra_classes="text-xs",
|
||||||
|
)
|
||||||
|
|
||||||
with ui.grid().classes("w-full grid-cols-1 md:grid-cols-2 gap-4"):
|
with ui.grid().classes("w-full grid-cols-1 md:grid-cols-2 gap-4"):
|
||||||
with archival_card(title="Execution Logistics"):
|
with archival_card(title="Execution Logistics"):
|
||||||
@@ -314,7 +316,10 @@ def register_page() -> None: # noqa: PLR0915
|
|||||||
ui.navigate.to("/jobs")
|
ui.navigate.to("/jobs")
|
||||||
|
|
||||||
with ui.row().classes("w-full items-center gap-2 mt-2"):
|
with ui.row().classes("w-full items-center gap-2 mt-2"):
|
||||||
ui.button("Delete job permanently", on_click=submit_delete, icon="delete_forever").props(
|
destructive_button(
|
||||||
"unelevated color=negative"
|
"Delete job permanently",
|
||||||
|
on_click=submit_delete,
|
||||||
|
icon="delete_forever",
|
||||||
|
variant="solid",
|
||||||
)
|
)
|
||||||
ui.button("Cancel", on_click=lambda: ui.navigate.to(f"/jobs/{job.id}"), icon="arrow_back").props("flat")
|
ui.button("Cancel", on_click=lambda: ui.navigate.to(f"/jobs/{job.id}"), icon="arrow_back").props("flat")
|
||||||
@@ -22,6 +22,7 @@ from transcription.ui.components.app_shell import render_navigation_header
|
|||||||
from transcription.ui.components.cards import archival_card
|
from transcription.ui.components.cards import archival_card
|
||||||
from transcription.ui.components.data_display import metadata_row
|
from transcription.ui.components.data_display import metadata_row
|
||||||
from transcription.ui.components.error_presenter import show_error
|
from transcription.ui.components.error_presenter import show_error
|
||||||
|
from transcription.ui.components.primitives import destructive_button
|
||||||
from transcription.ui.components.primitives import render_empty_state
|
from transcription.ui.components.primitives import render_empty_state
|
||||||
from transcription.ui.components.primitives import section_header_row
|
from transcription.ui.components.primitives import section_header_row
|
||||||
from transcription.ui.components.table.people import PersonTableRow, render_people_table
|
from transcription.ui.components.table.people import PersonTableRow, render_people_table
|
||||||
@@ -241,11 +242,12 @@ def register_page() -> None: # noqa: PLR0915
|
|||||||
on_click=lambda: ui.navigate.to(f"/people/{person.id}/edit"),
|
on_click=lambda: ui.navigate.to(f"/people/{person.id}/edit"),
|
||||||
icon="edit",
|
icon="edit",
|
||||||
).classes("ui-btn-primary text-xs")
|
).classes("ui-btn-primary text-xs")
|
||||||
ui.button(
|
destructive_button(
|
||||||
"Delete",
|
"Delete",
|
||||||
on_click=lambda: ui.navigate.to(f"/people/{person.id}/delete"),
|
on_click=lambda: ui.navigate.to(f"/people/{person.id}/delete"),
|
||||||
icon="delete",
|
icon="delete",
|
||||||
).props("outlined color=negative text-xs")
|
extra_classes="text-xs",
|
||||||
|
)
|
||||||
|
|
||||||
with ui.grid().classes("w-full grid-cols-12 gap-4"):
|
with ui.grid().classes("w-full grid-cols-12 gap-4"):
|
||||||
with ui.column().classes("col-span-12 lg:col-span-4"):
|
with ui.column().classes("col-span-12 lg:col-span-4"):
|
||||||
@@ -467,9 +469,10 @@ def register_page() -> None: # noqa: PLR0915
|
|||||||
ui.navigate.to("/people")
|
ui.navigate.to("/people")
|
||||||
|
|
||||||
with ui.row().classes("w-full items-center gap-2 mt-2"):
|
with ui.row().classes("w-full items-center gap-2 mt-2"):
|
||||||
ui.button(
|
destructive_button(
|
||||||
"Delete person permanently",
|
"Delete person permanently",
|
||||||
on_click=submit_delete,
|
on_click=submit_delete,
|
||||||
icon="delete_forever",
|
icon="delete_forever",
|
||||||
).props("unelevated color=negative")
|
variant="solid",
|
||||||
|
)
|
||||||
ui.button("Cancel", on_click=lambda: ui.navigate.to(f"/people/{person.id}"), icon="arrow_back").props("flat")
|
ui.button("Cancel", on_click=lambda: ui.navigate.to(f"/people/{person.id}"), icon="arrow_back").props("flat")
|
||||||
@@ -209,6 +209,26 @@ input:focus-visible,
|
|||||||
color: var(--theme-secondary);
|
color: var(--theme-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-page-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-page-title {
|
||||||
|
color: var(--theme-text);
|
||||||
|
font-family: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Palatino, serif;
|
||||||
|
font-size: 1.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-page-subtitle {
|
||||||
|
color: var(--theme-text-muted);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-table {
|
.ui-table {
|
||||||
border: 1px solid var(--theme-border);
|
border: 1px solid var(--theme-border);
|
||||||
color: var(--theme-text);
|
color: var(--theme-text);
|
||||||
|
|||||||
@@ -1,35 +1,33 @@
|
|||||||
from nicegui import ui
|
from nicegui import ui
|
||||||
|
|
||||||
# Exact Palette & Typography Constants from UI Design Specification
|
# Runtime bridge for Quasar color slots. Visual ownership remains in theme.css tokens/classes.
|
||||||
THEME_COLORS = {
|
THEME_COLORS = {
|
||||||
"primary": "#2D5A4C", # Library Green
|
"primary": "#5e6572",
|
||||||
"secondary": "#E2C7A8", # Aged Sepia
|
"secondary": "#7d98a1",
|
||||||
"positive": "#2D5A4C",
|
"accent": "#a9b4c2",
|
||||||
"accent": "#E2C7A8",
|
"dark": "#1c2321",
|
||||||
"dark": "#2B2D2C", # Matte Slate
|
"positive": "#7d98a1",
|
||||||
"negative": "#A83232",
|
"negative": "#1c2321",
|
||||||
|
"info": "#7d98a1",
|
||||||
|
"warning": "#a9b4c2",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Typography Macros
|
_THEME_APPLIED = False
|
||||||
STYLE_SERIF_HEADER = "font-family: 'Georgia', 'Times New Roman', serif;"
|
|
||||||
STYLE_SANS_BODY = "font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;"
|
|
||||||
|
|
||||||
|
|
||||||
def apply_archival_theme() -> None:
|
def apply_archival_theme() -> None:
|
||||||
"""Configures global NiceGUI theme variables and injects custom global styles."""
|
"""Apply runtime color slots once; visual styling is defined in theme.css."""
|
||||||
ui.colors(**THEME_COLORS)
|
global _THEME_APPLIED
|
||||||
|
if _THEME_APPLIED:
|
||||||
|
return
|
||||||
|
|
||||||
# Global body rules: Archival Cream (#FAF9F6) background, Iron Ink (#333333) text
|
ui.colors(**THEME_COLORS)
|
||||||
ui.query("body").style(
|
_THEME_APPLIED = True
|
||||||
f"background-color: #FAF9F6; color: #333333; {STYLE_SANS_BODY}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def page_header(title: str, subtitle: str | None = None) -> None:
|
def page_header(title: str, subtitle: str | None = None) -> None:
|
||||||
"""Standardized page title component with Archival Serif styling."""
|
"""Standardized page title component using semantic classes."""
|
||||||
with ui.column().classes("gap-0 pb-2 border-b border-[#6B6A65]/30 w-full"):
|
with ui.column().classes("ui-page-header"):
|
||||||
ui.label(title).style(
|
ui.label(title).classes("ui-page-title")
|
||||||
f"{STYLE_SERIF_HEADER} font-size: 1.75rem; font-weight: 700; color: #333333;"
|
|
||||||
)
|
|
||||||
if subtitle:
|
if subtitle:
|
||||||
ui.label(subtitle).classes("text-xs text-[#6B6A65]")
|
ui.label(subtitle).classes("ui-page-subtitle")
|
||||||
Reference in New Issue
Block a user