# -*- coding: utf-8 -*- # hub/ticket/ui/pages/document_browser_page.py """Общая страница просмотра документов Ticket.""" from __future__ import annotations from gui.components import Button, Label, TextInput from gui.containers import HContainer, SContainer, VContainer from application import TaskApplicationService from domain import TicketDocumentSnapshot from ui.ticket_selection_list import TicketSelectionEntry, TicketSelectionList from .report_viewer import ReportViewer class DocumentBrowserPage(SContainer): """Базовая страница списка документов и предпросмотра.""" def __init__( self, application: TaskApplicationService, title: str, empty_message: str, document_type: str, parent=None, ): super().__init__(width_percent=100, height_percent=100, parent=parent) self._application = application self._title = title self._empty_message = empty_message self._document_type = document_type self._documents: dict[str, TicketDocumentSnapshot] = {} self._list_widget: TicketSelectionList | None = None self._preview: TextInput | None = None self._open_button: Button | None = None self._setup_ui() self._connect_signals() self._reload_documents() def _setup_ui(self) -> None: # Root-контейнер страницы документов: заголовок плюс двухпанельная область. main_container = VContainer(margin=12, spacing=10, parent=self) title_label = Label(self._title, alignment="left", style="TICKET_LIST_HEADER") # Content-row документов: раскладывает список документов и их предпросмотр. content_row = HContainer(spacing=10, parent=main_container) # Левая панель списка документов list_panel = SContainer(style="TICKET_LIST_CONTAINER") # List-layout: внутренний контейнер заголовка и списка документов. list_layout = VContainer(margin=12, spacing=8, parent=list_panel) list_title_label = Label("Список документов", margin=[0, 0, 0, 8], alignment="left", style="TICKET_LIST_TITLE") self._list_widget = TicketSelectionList() list_layout.add_widget(list_title_label) list_layout.add_widget(self._list_widget) # Правая панель предпросмотра preview_panel = SContainer(style="TICKET_LIST_CONTAINER") # Preview-layout: внутренний контейнер заголовка, preview-поля и кнопки открытия. preview_layout = VContainer(margin=12, spacing=8, parent=preview_panel) preview_title_label = Label("Предпросмотр", alignment="left", style="TICKET_LIST_TITLE") self._preview = TextInput(style="TICKET_PREVIEW_AREA", multiline=True) self._preview.set_read_only(True) self._open_button = Button("Открыть документ", style="FILTER_BUTTON", content_fit=True) self._open_button.set_enabled(False) preview_layout.add_widget(preview_title_label) preview_layout.add_widget(self._preview) preview_layout.add_widget(self._open_button) content_row.add_widget_with_stretch(list_panel, 4) content_row.add_widget_with_stretch(preview_panel, 7) main_container.add_widget(title_label) main_container.add_widget(content_row) def _connect_signals(self) -> None: # Application-состояние self._application.task_updated.connect(self._reload_documents) self._application.state_loaded.connect(self._reload_documents) # UI-события страницы if self._list_widget is not None: self._list_widget.selection_changed.connect(self._update_preview) self._list_widget.item_activated.connect(self._open_current_document) if self._open_button is not None: self._open_button.clicked.connect(self._open_current_document) def _reload_documents(self, *_args) -> None: if self._list_widget is None or self._preview is None: return current_document_id = self._current_document_id() self._documents = { document.document_id: document for document in self._application.list_documents(self._document_type) } entries = [ TicketSelectionEntry( entry_id=document.document_id, title=document.title, subtitle=document.created_at.strftime("%d.%m.%Y %H:%M"), ) for document in self._documents.values() ] self._list_widget.set_entries(entries) if not self._documents: self._preview.set_text(self._empty_message) if self._open_button is not None: self._open_button.set_enabled(False) return self._restore_selection(current_document_id) self._update_preview() def _restore_selection(self, document_id: str | None) -> None: if self._list_widget is None: return self._list_widget.set_current_entry(document_id) def _update_preview(self, *_args) -> None: if self._preview is None: return document = self._current_document() if document is None: self._preview.set_text(self._empty_message) if self._open_button is not None: self._open_button.set_enabled(False) return self._preview.set_text(document.content or document.summary or document.title) if self._open_button is not None: self._open_button.set_enabled(True) def _current_document(self) -> TicketDocumentSnapshot | None: document_id = self._current_document_id() if document_id is None: return None return self._documents.get(document_id) def _current_document_id(self) -> str | None: if self._list_widget is None: return None entry_id = self._list_widget.current_entry_id() return str(entry_id) if entry_id is not None else None def _open_current_document(self, *_args) -> None: document = self._current_document() if document is not None: ReportViewer(document, parent=self).exec() def showEvent(self, event) -> None: super().showEvent(event) self._reload_documents()