Files
Dispatch/Dispatch_V0.1.1/ui/dialogs/task_refusal_dialog.py
2026-04-29 08:18:54 +04:00

136 lines
5.1 KiB
Python

# -*- coding: utf-8 -*-
# hub/ticket/ui/dialogs/task_refusal_dialog.py
"""Диалог подтверждения отказа задачи Ticket."""
from __future__ import annotations
from gui.components import Button, Dialog, Label, TextInput
from gui.containers import HContainer, VContainer
from domain import TicketTaskSnapshot, parse_location_parts
class TaskRefusalDialog(Dialog):
"""Диалог ввода обязательной причины отказа по задаче."""
def __init__(self, task: TicketTaskSnapshot, parent=None):
self._task = task
self._reason_input: TextInput | None = None
self._cancel_button: Button | None = None
self._submit_button: Button | None = None
super().__init__(
title="Отказ в обслуживании",
width=500,
height=460,
modal=True,
parent=parent,
)
self._setup_ui()
self._connect_signals()
self._refresh_submit_state()
@property
def refusal_reason(self) -> str:
if self._reason_input is None:
return ""
return self._reason_input.get_text().strip()
def _setup_ui(self) -> None:
# Root-контейнер окна отказа: предупреждение, контекст задачи, поле причины и actions.
main_container = VContainer(margin=[24, 20, 24, 20], spacing=16)
self.add_widget(main_container)
main_container.add_widget(
Label(
"Вы уверены, что хотите отказать в обслуживании?",
alignment="left",
style="TICKET_REFUSAL_HEADING",
)
)
main_container.add_widget(self._build_location_row())
main_container.add_widget(
Label(
'Задача будет перемещена в колонку "Отказ"',
alignment="left",
style="TICKET_REFUSAL_WARNING",
)
)
main_container.add_widget(
Label(
"Причина отказа",
alignment="left",
style="TICKET_REFUSAL_HEADING",
)
)
main_container.add_widget_with_stretch(self._build_reason_input(), 1)
main_container.add_widget(self._build_actions())
def _build_location_row(self) -> HContainer:
# Location-row: показывает локацию задачи, чтобы отказ происходил в явном контексте.
row = HContainer(spacing=10, content_fit=True)
row.add_widget(
Label(
"Локация и кабинет:",
alignment="left",
style="TICKET_REFUSAL_LOCATION_TITLE",
)
)
value_label = Label(
self._build_location_text(),
alignment="left",
style="TICKET_REFUSAL_LOCATION_VALUE",
)
value_label.set_tooltip(self._build_location_text())
row.add_widget_with_stretch(value_label, 1)
return row
def _build_reason_input(self) -> TextInput:
self._reason_input = TextInput(
placeholder="Укажите причину отказа",
style="TICKET_DOCUMENT_TEXTAREA",
multiline=True,
)
self._reason_input.set_min_height(170)
return self._reason_input
def _build_actions(self) -> HContainer:
# Actions-row окна отказа: собирает кнопки отмены и финального подтверждения.
actions = HContainer(spacing=18, content_fit=True)
actions.add_stretch()
self._cancel_button = Button(
"Отмена",
style="TICKET_DOCUMENT_CANCEL_BUTTON",
content_fit=True,
)
self._submit_button = Button(
"Подтвердить отказ",
style="TICKET_DOCUMENT_SUBMIT_BUTTON",
content_fit=True,
)
actions.add_widget(self._cancel_button)
actions.add_widget(self._submit_button)
return actions
def _connect_signals(self) -> None:
if self._reason_input is not None:
self._reason_input.text_changed.connect(self._refresh_submit_state)
if self._cancel_button is not None:
self._cancel_button.clicked.connect(self.reject)
if self._submit_button is not None:
self._submit_button.clicked.connect(self._handle_accept)
def _refresh_submit_state(self) -> None:
if self._submit_button is not None:
self._submit_button.set_enabled(bool(self.refusal_reason))
def _handle_accept(self) -> None:
if not self.refusal_reason:
return
self.accept()
def _build_location_text(self) -> str:
institution, room, _ = parse_location_parts(self._task.location or "")
normalized_institution = institution or "Локация не указана"
normalized_room = room or "Кабинет не указан"
return f"{normalized_institution}, {normalized_room}"