Minor UI refinement
Quality Gate / gate (push) Failing after 49s

This commit is contained in:
Jim Lancaster
2026-08-24 12:58:34 -05:00
parent 9a7970c533
commit 96af7b2dc0
8 changed files with 85 additions and 16 deletions
+1 -1
View File
@@ -71,7 +71,7 @@ Rules:
- The heading shows name, type, and internal ID.
- The header includes a contextual back action: **Back to Documents** by default, **Back to Person** when opened from Person Detail, and **Back to Job** when opened from Job Detail.
- The first Source, when present, appears in the dark-room viewer.
- Archival Metadata shows authors, Document Type, tags, Document date (`MM-DD-YYYY` for exact dates), location, and archive identifier. Notes appear in a separate archival-notes block within the same card.
- Archival Metadata shows authors, Document Type, tags, Document date (`MM-DD-YYYY` for exact dates), location (linked to Google Maps when present), and archive identifier. Notes appear in a separate archival-notes block within the same card.
- System Logistics shows created and updated timestamps.
- Related People are grouped by role and link to Person Detail.
- **Sources & Pipeline Jobs** shows counts and actions for filtered Sources, Document Jobs, and adding a Job.
+1 -1
View File
@@ -20,7 +20,7 @@ The list accepts optional `document_id` and `job_id` query parameters. Document
- Global context provides **Create Job**.
- Filtered context provides **Back to Document** or **Back to Job**.
- Rows are ordered by page number and then upload name.
- Columns are Document Name, Page Number, Upload Title, Status, and Error Detail.
- Columns are Upload Title, Page Number, Document Name, Status, and Error Detail.
- Document Name, Upload Title, and Error Detail are left-aligned; Status is centered.
- Status labels are presented in uppercase for consistency with Jobs.
- Stored Filename is intentionally absent from the list.
@@ -53,9 +53,9 @@ def render_sources_table(rows: Sequence[SourceTableRow]) -> None:
rows=_serialize_rows(rows),
columns=[
{
"name": "document_name",
"label": "Document Name",
"field": "document_name",
"name": "upload_name",
"label": "Upload Title",
"field": "upload_name",
"sortable": True,
"classes": "font-serif text-left ui-table-cell-wrap",
"align": "left",
@@ -69,9 +69,9 @@ def render_sources_table(rows: Sequence[SourceTableRow]) -> None:
"style": "width: 10%;",
},
{
"name": "upload_name",
"label": "Upload Title",
"field": "upload_name",
"name": "document_name",
"label": "Document Name",
"field": "document_name",
"sortable": True,
"classes": "font-serif text-left ui-table-cell-wrap",
"align": "left",
+10 -1
View File
@@ -27,10 +27,12 @@ from transcription.ui.components.confirm_delete import dependency_summary
from transcription.ui.components.confirm_delete import render_delete_actions
from transcription.ui.components.confirm_delete import render_delete_blocked_notice
from transcription.ui.components.data_display import archival_badge
from transcription.ui.components.data_display import metadata_link_row
from transcription.ui.components.data_display import metadata_row
from transcription.ui.components.error_presenter import run_ui_action
from transcription.ui.components.error_presenter import show_error
from transcription.ui.components.formatters import compact_date
from transcription.ui.components.formatters import google_maps_search_url
from transcription.ui.components.formatters import parse_iso_date
from transcription.ui.components.formatters import parse_uuid
from transcription.ui.components.guards import parsed_record_id
@@ -560,7 +562,14 @@ def _render_bento_metadata_zone(document: Document) -> None:
)
metadata_row("Tags:", ", ".join(tags) if tags else "Not set")
metadata_row("Document Date:", _detail_document_date(document.document_date, document.document_date_raw))
metadata_row("Location Created:", document.location_created or "Not set")
if document.location_created:
metadata_link_row(
"Location Created:",
document.location_created,
google_maps_search_url(document.location_created),
)
else:
metadata_row("Location Created:", "Not set")
metadata_row("Archive Identifier:", document.archive_identifier or "Not set")
with ui.column().classes("w-full mt-2"):
@@ -655,14 +655,23 @@ async def _render_person_photo_zone(
active_index = [0]
with ui.column().classes("col-span-12 lg:col-span-4"), archival_card(title="Photos", extra_classes="gap-3"):
@ui.refreshable
def render_photo_viewer() -> None:
def refresh_photo_viewer() -> None:
render_photo_viewer.refresh()
_render_photo_viewer_with_navigation(
photos=photos,
active_index=active_index,
settings=settings,
request=request,
empty_message="No portrait photo uploaded yet.",
on_change=refresh_photo_viewer,
)
render_photo_viewer()
def _shift_gallery_index(*, photos: list, active_index: list[int], step: int) -> None:
if len(photos) < 2:
+2
View File
@@ -37,6 +37,7 @@ async def seed_person_and_document():
document_type_id=letter_type.id,
archive_identifier="ZC-1924-001",
document_date=date(1924, 7, 4),
location_created="Salt Lake City, Utah",
)
session.add(doc)
await session.flush()
@@ -151,6 +152,7 @@ class TestDocumentsPageRendering:
assert "Letter" in response.text
assert "07-04-1924" in response.text
assert "1924-07-04" not in response.text
assert "google.com/maps/search/?api=1&amp;query=Salt+Lake+City%2C+Utah" in response.text
assert "PIPELINE JOBS" in response.text.upper()
assert "Edit Document" in response.text
assert "Back to Documents" in response.text
+39
View File
@@ -186,6 +186,45 @@ class TestPeoplePageRendering:
assert response.text.count("Edit Photo(s)") == 1
assert "Upload photo(s)" not in response.text
@pytest.mark.asyncio
async def test_person_detail_page_shows_gallery_navigation_for_multiple_photos(self, app_client):
app, client = app_client
upload_dirs = {app.state.settings.upload_dir, get_settings().upload_dir}
for upload_dir in upload_dirs:
primary_file = upload_dir / "photos" / "portrait-primary.png"
secondary_file = upload_dir / "photos" / "portrait-secondary.png"
primary_file.parent.mkdir(parents=True, exist_ok=True)
primary_file.write_bytes(b"portrait-primary")
secondary_file.write_bytes(b"portrait-secondary")
async with session_scope() as session:
person = Person(given_names="Gallery", last_name="Navigation")
session.add(person)
await session.flush()
session.add_all(
[
Photo(
person_id=person.id,
path="photos/portrait-primary.png",
is_primary=True,
),
Photo(
person_id=person.id,
path="photos/portrait-secondary.png",
is_primary=False,
),
]
)
await session.commit()
person_id = str(person.id)
response = client.get(f"/ui/people/{person_id}")
assert response.status_code == 200
assert "Previous" in response.text
assert "Next" in response.text
assert "1 of 2" in response.text
@pytest.mark.asyncio
async def test_person_photos_page_renders_photo_management_controls(self, app_client):
app, client = app_client
+10
View File
@@ -1,5 +1,6 @@
"""Tests for the sources page routes and Source model properties."""
import re
from datetime import UTC
from datetime import datetime
from pathlib import Path
@@ -198,6 +199,15 @@ class TestSourcesPageRendering:
assert "page_one.png" in response.text
assert "Source Document" in response.text
assert "Stored Filename" not in response.text
assert re.search(
r'"name":"upload_name","label":"Upload Title".*'
r'"name":"page_number","label":"Page Number".*'
r'"name":"document_name","label":"Document Name".*'
r'"name":"job_source_status","label":"Status".*'
r'"name":"job_source_error_detail","label":"Error Detail"',
response.text,
re.DOTALL,
)
@pytest.mark.asyncio
async def test_sources_page_filters_to_document_context(self, app_client):