""" Обработчики просмотра заявок. Навигация полностью через callback payload — FSM-состояния не нужны. "view_tickets" → список, страница 0 "tickets_page:{n}" → список, страница n "ticket_detail:{id}" → карточка заявки "main_menu" → в menu.py """ from maxapi.context import BaseContext from maxapi.dispatcher import Router from maxapi.filters import F from maxapi.types.updates.message_callback import MessageCallback from bot.filters import PayloadStartsWith from bot.keyboards import ticket_detail_kb, tickets_list_kb from database.queries.ticket_attachments import get_attachments from database.queries.tickets import ( PAGE_SIZE, count_tickets_for_org, get_ticket, get_tickets_for_org, ) router = Router(router_id="view_tickets") STATUS_LABELS = { "open": "🔴 Открыта", "in_progress": "🟡 В работе", "closed": "🟢 Закрыта", } async def _send_tickets_page( event: MessageCallback, page: int, bot_user: dict ) -> None: org_id = bot_user["org_id"] tickets = await get_tickets_for_org(org_id, page=page) total = await count_tickets_for_org(org_id) chat_id = event.message.recipient.chat_id if event.message else None if chat_id is None: return if not tickets: await event.bot.send_message( chat_id=chat_id, text="📭 У вашей организации пока нет заявок.", attachments=[ticket_detail_kb(page=0)], ) return await event.bot.send_message( chat_id=chat_id, text=f"📋 Заявки организации (стр. {page + 1}):", attachments=[tickets_list_kb(tickets, page=page, total=total)], ) @router.message_callback(F.callback.payload == "view_tickets") async def cb_view_tickets( event: MessageCallback, context: BaseContext, bot_user: dict ) -> None: await event.answer(notification="") await _send_tickets_page(event, page=0, bot_user=bot_user) @router.message_callback(PayloadStartsWith("tickets_page:")) async def cb_tickets_page( event: MessageCallback, context: BaseContext, bot_user: dict ) -> None: await event.answer(notification="") try: page = int(event.callback.payload.split(":")[1]) except (IndexError, ValueError): page = 0 await _send_tickets_page(event, page=page, bot_user=bot_user) @router.message_callback(PayloadStartsWith("ticket_detail:")) async def cb_ticket_detail( event: MessageCallback, context: BaseContext, bot_user: dict ) -> None: try: ticket_id = int(event.callback.payload.split(":")[1]) except (IndexError, ValueError): await event.answer(notification="Ошибка") return ticket = await get_ticket(ticket_id) chat_id = event.message.recipient.chat_id if event.message else None if chat_id is None: return # Проверка доступа: заявка должна принадлежать той же организации if ticket is None or ticket["org_id"] != bot_user["org_id"]: await event.answer(notification="Нет доступа") return await event.answer(notification="") status_label = STATUS_LABELS.get(ticket["status"], ticket["status"]) text = ( f"📌 Заявка #{ticket['id']}\n" f"Статус: {status_label}\n" f"Создана: {ticket['created_at'][:16]}\n\n" f"📄 Описание:\n{ticket['description']}" ) if ticket.get("last_comment"): text += f"\n\n💬 Последний комментарий:\n{ticket['last_comment']}" # Вложения attachments = await get_attachments(ticket_id) if attachments: att_lines = [] for a in attachments: icon = { "AttachmentType.IMAGE": "🖼", "AttachmentType.VIDEO": "🎬", "AttachmentType.AUDIO": "🎙", "AttachmentType.FILE": "📎", }.get(a["attachment_type"], "📎") line = f"{icon} {a['attachment_type'].split('.')[-1].capitalize()}" if a.get("attachment_url"): line += f": {a['attachment_url']}" att_lines.append(line) text += "\n\n📎 Вложения:\n" + "\n".join(att_lines) await event.bot.send_message( chat_id=chat_id, text=text, attachments=[ticket_detail_kb(page=0)], )