136 lines
5.1 KiB
Python
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}"
|