Files
DutyLog/src/dutylog/application/bot/admin_dialogs/residents_management.py
T
2026-04-04 00:44:09 +03:00

928 lines
29 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from aiogram.types import Message, CallbackQuery
from aiogram.utils.markdown import html_decoration as hd
from aiogram_dialog import Window, DialogManager
from aiogram_dialog.widgets.text import Format, Const
from aiogram_dialog.widgets.kbd import Row, SwitchTo, Button, ScrollingGroup, Select, Group
from aiogram_dialog.widgets.input import MessageInput
from magic_filter import F
from dishka import FromDishka
from dishka.integrations.aiogram_dialog import inject
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
from dutylog.infrastructure.database.repositories.residents_repository import (
ResidentsRepository,
)
from dutylog.infrastructure.database.repositories.rooms_repository import (
RoomsRepository,
)
from dutylog.infrastructure.database.repositories.floors_repository import (
FloorsRepository,
)
from dutylog.infrastructure.database.repositories.users_repository import (
UsersRepository,
)
from dutylog.infrastructure.database.repositories.hours_transactions_repository import (
HoursTransactionsRepository,
)
from dutylog.infrastructure.utils.datetime import msk_now
@inject
async def get_residents_list_data(
residents_repository: FromDishka[ResidentsRepository],
rooms_repository: FromDishka[RoomsRepository],
floors_repository: FromDishka[FloorsRepository],
users_repository: FromDishka[UsersRepository],
**kwargs,
):
all_residents = await residents_repository.get_all_residents()
residents_with_rooms = []
for resident in all_residents:
room = await rooms_repository.get_room_by_id(resident.room)
if room:
floor = await floors_repository.get_floor_by_id(room.on_floor)
floor_number = floor.number if floor else 999999
room_number = room.number
else:
floor_number = 999999
room_number = 999999
is_admin = False
if resident.user_entity:
user = await users_repository.get_user_by_id(resident.user_entity)
if user and user.is_admin:
is_admin = True
residents_with_rooms.append((resident, floor_number, room_number, is_admin))
residents_with_rooms.sort(key=lambda x: (x[1], x[2]))
residents_data = []
for resident, floor_number, room_number, is_admin in residents_with_rooms:
status = "🟢" if resident.is_busy else "⚪️"
name = resident.real_name if resident.real_name else "Без имени"
admin_badge = " 👑" if is_admin else ""
residents_data.append(
(f"{name} | Комната {room_number} | {status}{admin_badge}", resident.id)
)
content = f"""
<blockquote>🏠 <b>Резиденты</b></blockquote>
<b>Всего резидентов:</b> <code>{len(all_residents)}</code>
Выберите резидента для просмотра информации:
"""
return {
"content": content,
"residents": residents_data,
}
@inject
async def get_resident_info_data(
dialog_manager: DialogManager,
residents_repository: FromDishka[ResidentsRepository],
rooms_repository: FromDishka[RoomsRepository],
users_repository: FromDishka[UsersRepository],
**kwargs,
):
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
if not resident_id:
return {"info_content": "Ошибка: резидент не выбран", "is_busy": False, "is_admin": False, "from_search": False, "from_filter": False}
resident = await residents_repository.get_resident_by_id(resident_id)
if not resident:
return {"info_content": "Ошибка: резидент не найден", "is_busy": False, "is_admin": False, "from_search": False, "from_filter": False}
room = await rooms_repository.get_room_by_id(resident.room)
room_number = room.number if room else "???"
name = hd.quote(resident.real_name) if resident.real_name else "Без имени"
status = "🟢 Занят" if resident.is_busy else "⚪️ Свободен"
is_admin = False
user_info = "Не привязан"
if resident.user_entity:
user = await users_repository.get_user_by_id(resident.user_entity)
if user:
if user.username:
username = f"@{hd.quote(user.username)}"
else:
username = f"ID: {user.id}"
first_name = hd.quote(user.first_name) if user.first_name else "Без имени"
user_info = f"{first_name} ({username})"
is_admin = user.is_admin
admin_badge = " 👑" if is_admin else ""
hours_info = ""
if not is_admin:
hours_info = f"""
🟢 <b>Отработанные часы:</b> <code>{resident.inactive_hours}</code> ч
🔴 <b>Неотработанные часы:</b> <code>{resident.active_hours}</code> ч
"""
else:
hours_info = "\n<blockquote>👑 <b>Это администратор</b></blockquote>"
info_content = f"""
<blockquote>👤 <b>Информация о резиденте{admin_badge}</b></blockquote>
<b>ID:</b> <code>{resident.id}</code>
<b>Имя:</b> {name}
<b>Комната:</b> <code>{room_number}</code>
<b>Статус:</b> {status}
<b>Пользователь:</b> {user_info}
{hours_info}"""
from_search = dialog_manager.dialog_data.get("from_search", False)
from_filter = dialog_manager.dialog_data.get("from_filter", False)
return {
"info_content": info_content,
"is_busy": resident.is_busy,
"is_admin": is_admin,
"from_search": from_search,
"from_filter": from_filter,
}
async def on_resident_selected(
callback: CallbackQuery,
widget: Select,
dialog_manager: DialogManager,
item_id: str,
):
dialog_manager.dialog_data["selected_resident_id"] = int(item_id)
dialog_manager.dialog_data["from_search"] = False
dialog_manager.dialog_data["from_filter"] = False
await dialog_manager.switch_to(AdminMenuSG.resident_info)
async def on_add_resident(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
):
await dialog_manager.switch_to(AdminMenuSG.create_resident_name)
async def on_filter_residents(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
):
await dialog_manager.switch_to(AdminMenuSG.residents_filter_select)
async def on_search_residents(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
):
await dialog_manager.switch_to(AdminMenuSG.residents_search_input)
@inject
async def on_logout_resident_confirm(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
residents_repository: FromDishka[ResidentsRepository],
):
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
if resident_id:
await residents_repository.unbind_user_from_resident(resident_id)
await dialog_manager.switch_to(AdminMenuSG.resident_info)
async def on_logout_resident_cancel(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
):
await dialog_manager.switch_to(AdminMenuSG.resident_info)
@inject
async def on_delete_resident_confirm(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
residents_repository: FromDishka[ResidentsRepository],
):
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
if resident_id:
await residents_repository.delete_resident(resident_id)
await callback.answer("✅ Резидент удален!")
await dialog_manager.switch_to(AdminMenuSG.residents)
async def on_delete_resident_cancel(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
):
await dialog_manager.switch_to(AdminMenuSG.resident_info)
async def on_rebind_resident(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
):
await dialog_manager.switch_to(AdminMenuSG.resident_rebind_floor)
@inject
async def get_rebind_floors_data(
floors_repository: FromDishka[FloorsRepository],
**kwargs,
):
all_floors = await floors_repository.get_all_floors()
all_floors.sort(key=lambda f: f.number)
return {
"floors": [(f.id, f"Этаж {f.number}") for f in all_floors],
}
async def on_rebind_floor_selected(
callback: CallbackQuery,
widget: Select,
dialog_manager: DialogManager,
item_id: str,
):
dialog_manager.dialog_data["rebind_floor_id"] = int(item_id)
await dialog_manager.switch_to(AdminMenuSG.resident_rebind_room)
@inject
async def get_rebind_rooms_data(
dialog_manager: DialogManager,
rooms_repository: FromDishka[RoomsRepository],
**kwargs,
):
floor_id = dialog_manager.dialog_data.get("rebind_floor_id")
if not floor_id:
return {"rooms": []}
rooms = await rooms_repository.get_rooms_by_floor(floor_id)
rooms.sort(key=lambda r: r.number)
return {
"rooms": [(r.id, str(r.number)) for r in rooms],
}
async def on_rebind_room_selected(
callback: CallbackQuery,
widget: Select,
dialog_manager: DialogManager,
item_id: str,
):
dialog_manager.dialog_data["rebind_room_id"] = int(item_id)
await dialog_manager.switch_to(AdminMenuSG.resident_rebind_confirm)
@inject
async def get_rebind_confirm_data(
dialog_manager: DialogManager,
residents_repository: FromDishka[ResidentsRepository],
rooms_repository: FromDishka[RoomsRepository],
**kwargs,
):
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
new_room_id = dialog_manager.dialog_data.get("rebind_room_id")
resident = await residents_repository.get_resident_by_id(resident_id) if resident_id else None
new_room = await rooms_repository.get_room_by_id(new_room_id) if new_room_id else None
old_room = await rooms_repository.get_room_by_id(resident.room) if resident else None
resident_name = resident.real_name if resident and resident.real_name else "???"
old_room_number = old_room.number if old_room else "???"
new_room_number = new_room.number if new_room else "???"
return {
"resident_name": resident_name,
"old_room_number": old_room_number,
"new_room_number": new_room_number,
}
@inject
async def on_rebind_confirm(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
residents_repository: FromDishka[ResidentsRepository],
):
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
new_room_id = dialog_manager.dialog_data.get("rebind_room_id")
if resident_id and new_room_id:
resident = await residents_repository.get_resident_by_id(resident_id)
if resident:
await residents_repository.update_resident_room(resident_id, new_room_id)
await callback.answer("✅ Резидент перепривязан к новой комнате!")
await dialog_manager.switch_to(AdminMenuSG.resident_info)
async def on_rebind_cancel(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
):
await dialog_manager.switch_to(AdminMenuSG.resident_info)
async def on_resident_name_input(
message: Message,
widget: MessageInput,
dialog_manager: DialogManager,
):
if not message.text or len(message.text.strip()) < 2:
await message.answer("⚠️ Пожалуйста, введите корректное имя и фамилию")
return
dialog_manager.dialog_data["new_resident_name"] = message.text.strip()
await dialog_manager.switch_to(AdminMenuSG.create_resident_floor)
@inject
async def get_create_resident_floors_data(
floors_repository: FromDishka[FloorsRepository],
**kwargs,
):
all_floors = await floors_repository.get_all_floors()
all_floors.sort(key=lambda f: f.number)
return {
"floors": [(f.id, f"Этаж {f.number}") for f in all_floors],
}
async def on_create_resident_floor_selected(
callback: CallbackQuery,
widget: Select,
dialog_manager: DialogManager,
item_id: str,
):
dialog_manager.dialog_data["new_resident_floor_id"] = int(item_id)
await dialog_manager.switch_to(AdminMenuSG.create_resident_room)
@inject
async def get_create_resident_rooms_data(
dialog_manager: DialogManager,
rooms_repository: FromDishka[RoomsRepository],
**kwargs,
):
floor_id = dialog_manager.dialog_data.get("new_resident_floor_id")
if not floor_id:
return {"rooms": []}
rooms = await rooms_repository.get_rooms_by_floor(floor_id)
rooms.sort(key=lambda r: r.number)
return {
"rooms": [(r.id, str(r.number)) for r in rooms],
}
async def on_create_resident_room_selected(
callback: CallbackQuery,
widget: Select,
dialog_manager: DialogManager,
item_id: str,
):
dialog_manager.dialog_data["new_resident_room_id"] = int(item_id)
await dialog_manager.switch_to(AdminMenuSG.create_resident_confirm)
async def get_create_resident_confirm_data(
dialog_manager: DialogManager,
**kwargs,
):
name = dialog_manager.dialog_data.get("new_resident_name", "???")
return {"resident_name": name}
@inject
async def on_create_resident_confirm(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
residents_repository: FromDishka[ResidentsRepository],
):
name = dialog_manager.dialog_data.get("new_resident_name")
room_id = dialog_manager.dialog_data.get("new_resident_room_id")
if name and room_id:
await residents_repository.create_resident(
room_id=room_id,
real_name=name,
)
await callback.answer("✅ Резидент создан!")
await dialog_manager.switch_to(AdminMenuSG.residents)
async def on_create_resident_cancel(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
):
await dialog_manager.switch_to(AdminMenuSG.residents)
async def on_search_input(
message: Message,
widget: MessageInput,
dialog_manager: DialogManager,
):
if not message.text or len(message.text.strip()) < 1:
await message.answer("⚠️ Пожалуйста, введите поисковый запрос")
return
query = message.text.strip()
if query.startswith("@"):
query = query[1:]
dialog_manager.dialog_data["search_query"] = query
dialog_manager.dialog_data["is_search_active"] = True
await dialog_manager.switch_to(AdminMenuSG.residents_search_results)
@inject
async def get_search_results_data(
dialog_manager: DialogManager,
residents_repository: FromDishka[ResidentsRepository],
rooms_repository: FromDishka[RoomsRepository],
floors_repository: FromDishka[FloorsRepository],
users_repository: FromDishka[UsersRepository],
**kwargs,
):
query = dialog_manager.dialog_data.get("search_query", "")
residents, search_type = await residents_repository.search_residents(
query, users_repository
)
if not residents:
return {
"content": f"""
<blockquote>🔍 <b>Результаты поиска</b></blockquote>
<b>Запрос:</b> <code>{query}</code>
❌ Ничего не найдено
""",
"residents": [],
"has_results": False,
}
residents_with_rooms = []
for resident in residents:
room = await rooms_repository.get_room_by_id(resident.room)
if room:
floor = await floors_repository.get_floor_by_id(room.on_floor)
floor_number = floor.number if floor else 999999
room_number = room.number
else:
floor_number = 999999
room_number = 999999
is_admin = False
if resident.user_entity:
user = await users_repository.get_user_by_id(resident.user_entity)
if user and user.is_admin:
is_admin = True
residents_with_rooms.append((resident, floor_number, room_number, is_admin))
residents_with_rooms.sort(key=lambda x: (x[1], x[2]))
residents_data = []
for resident, floor_number, room_number, is_admin in residents_with_rooms:
status = "🟢" if resident.is_busy else "⚪️"
name = resident.real_name if resident.real_name else "Без имени"
admin_badge = " 👑" if is_admin else ""
residents_data.append(
(f"{name} | Комната {room_number} | {status}{admin_badge}", resident.id)
)
content = f"""
<blockquote>🔍 <b>Результаты поиска по {search_type}</b></blockquote>
<b>Найдено резидентов:</b> <code>{len(residents)}</code>
Выберите резидента для просмотра информации:
"""
return {
"content": content,
"residents": residents_data,
"has_results": True,
}
async def on_search_resident_selected(
callback: CallbackQuery,
widget: Select,
dialog_manager: DialogManager,
item_id: str,
):
dialog_manager.dialog_data["selected_resident_id"] = int(item_id)
dialog_manager.dialog_data["from_search"] = True
dialog_manager.dialog_data["from_filter"] = False
await dialog_manager.switch_to(AdminMenuSG.resident_info)
@inject
async def get_resident_history_data(
dialog_manager: DialogManager,
residents_repository: FromDishka[ResidentsRepository],
transactions_repository: FromDishka[HoursTransactionsRepository],
**kwargs,
):
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
if not resident_id:
return {"history_content": "Ошибка: резидент не выбран"}
resident = await residents_repository.get_resident_by_id(resident_id)
if not resident:
return {"history_content": "Ошибка: резидент не найден"}
transactions = await transactions_repository.get_resident_history(resident_id)
transactions_sorted = sorted(transactions, key=lambda x: x.created_at)
last_10 = transactions_sorted[-10:]
resident_name = resident.real_name if resident.real_name else "Без имени"
if not last_10:
history_text = f"""
<blockquote>📜 <b>История операций</b></blockquote>
<b>Резидент:</b> {resident_name}
<i>История операций пуста</i>
"""
else:
history_text = f"""
<blockquote>📜 <b>История операций</b></blockquote>
<b>Резидент:</b> {resident_name}
"""
for tx in last_10:
operation = "Начислено" if tx.transaction_type == "increase" else "Списано"
emoji = "+" if tx.transaction_type == "increase" else ""
msk_time = tx.created_at.astimezone(msk_now().tzinfo).replace(tzinfo=None)
date_str = msk_time.strftime("%d.%m.%Y %H:%M")
remark_text = f"\n💬 <i>{tx.remark}</i>" if tx.remark else ""
history_text += f"<blockquote><b>{operation}</b> {emoji}<code>{tx.amount}</code> ч\n📅 {date_str}{remark_text}</blockquote>\n"
return {"history_content": history_text}
residents_list_window = Window(
Format("{content}"),
Row(
Button(
Const("🔍 Поиск"),
id="search_residents_btn",
on_click=on_search_residents,
),
Button(
Const("🔽 Фильтр"),
id="filter_residents_btn",
on_click=on_filter_residents,
),
),
ScrollingGroup(
Select(
Format("{item[0]}"),
id="residents_select",
item_id_getter=lambda x: x[1],
items="residents",
on_click=on_resident_selected,
),
id="residents_scroll",
width=1,
height=7,
),
Button(
Const("➕ Добавить резидента"),
id="add_resident_btn",
on_click=on_add_resident,
),
SwitchTo(
Const("◀️ Назад"),
id="back_to_admin_menu",
state=AdminMenuSG.main,
),
state=AdminMenuSG.residents,
getter=get_residents_list_data,
)
resident_info_window = Window(
Format("{info_content}"),
Row(
Button(
Const("Добавить часы"),
id="add_hours_btn",
on_click=lambda c, b, m: m.switch_to(AdminMenuSG.add_hours_select),
when=~F["is_admin"],
),
Button(
Const("Отнять часы"),
id="remove_hours_btn",
on_click=lambda c, b, m: m.switch_to(AdminMenuSG.remove_hours_select),
when=~F["is_admin"],
),
),
Button(
Const("📜 История"),
id="resident_history_btn",
on_click=lambda c, b, m: m.switch_to(AdminMenuSG.resident_history),
when=~F["is_admin"],
),
Button(
Const("🔄 Перепривязать к комнате"),
id="rebind_resident_btn",
on_click=on_rebind_resident,
when=~F["is_admin"],
),
Button(
Const("🚪 Разлогинить"),
id="logout_resident_btn",
on_click=lambda c, b, m: m.switch_to(AdminMenuSG.resident_logout_confirm),
when=F["is_busy"] & ~F["is_admin"],
),
Button(
Const("🗑 Удалить резидента"),
id="delete_resident_btn",
on_click=lambda c, b, m: m.switch_to(AdminMenuSG.resident_delete_confirm),
when=~F["is_admin"],
),
SwitchTo(
Const("◀️ Назад к результатам поиска"),
id="back_to_search_results",
state=AdminMenuSG.residents_search_results,
when="from_search",
),
SwitchTo(
Const("◀️ Назад к результатам фильтра"),
id="back_to_filter_results",
state=AdminMenuSG.residents_filtered_results,
when="from_filter",
),
SwitchTo(
Const("◀️ Назад к списку"),
id="back_to_residents_list",
state=AdminMenuSG.residents,
when=~F["from_search"] & ~F["from_filter"],
),
state=AdminMenuSG.resident_info,
getter=get_resident_info_data,
)
resident_logout_confirm_window = Window(
Const(
"<blockquote>⚠️ <b>Подтверждение</b></blockquote>\n\nВы уверены, что хотите разлогинить этого резидента?"
),
Row(
Button(
Const("✅ Да"),
id="confirm_logout",
on_click=on_logout_resident_confirm,
),
Button(
Const("❌ Нет"),
id="cancel_logout",
on_click=on_logout_resident_cancel,
),
),
state=AdminMenuSG.resident_logout_confirm,
)
create_resident_name_window = Window(
Const("<blockquote> <b>Создание резидента</b></blockquote>\n\nВведите имя и фамилию резидента:"),
MessageInput(on_resident_name_input),
SwitchTo(
Const("◀️ Отмена"),
id="cancel_create_resident_name",
state=AdminMenuSG.residents,
),
state=AdminMenuSG.create_resident_name,
)
create_resident_floor_window = Window(
Const("<blockquote>🏢 <b>Выбор этажа</b></blockquote>\n\nВыберите этаж для нового резидента:"),
Group(
Select(
Format("{item[1]}"),
id="create_resident_floor_select",
item_id_getter=lambda x: x[0],
items="floors",
on_click=on_create_resident_floor_selected,
),
width=2,
),
SwitchTo(
Const("◀️ Назад"),
id="back_to_create_name",
state=AdminMenuSG.create_resident_name,
),
state=AdminMenuSG.create_resident_floor,
getter=get_create_resident_floors_data,
)
create_resident_room_window = Window(
Const("<blockquote>🚪 <b>Выбор комнаты</b></blockquote>\n\nВыберите комнату для нового резидента:"),
Group(
Select(
Format("{item[1]}"),
id="create_resident_room_select",
item_id_getter=lambda x: x[0],
items="rooms",
on_click=on_create_resident_room_selected,
),
width=3,
),
SwitchTo(
Const("◀️ Назад"),
id="back_to_create_floor",
state=AdminMenuSG.create_resident_floor,
),
state=AdminMenuSG.create_resident_room,
getter=get_create_resident_rooms_data,
)
create_resident_confirm_window = Window(
Format("<blockquote>✅ <b>Подтверждение</b></blockquote>\n\nСоздать резидента <b>{resident_name}</b>?"),
Row(
Button(
Const("✅ Да"),
id="confirm_create_resident",
on_click=on_create_resident_confirm,
),
Button(
Const("❌ Нет"),
id="cancel_create_resident",
on_click=on_create_resident_cancel,
),
),
state=AdminMenuSG.create_resident_confirm,
getter=get_create_resident_confirm_data,
)
search_input_window = Window(
Const("<blockquote>🔍 <b>Поиск резидентов</b></blockquote>\n\n<blockquote>Введите номер комнаты, имя резидента или username пользователя. Можно указать только часть имени или username для поиска.</blockquote>"),
MessageInput(on_search_input),
SwitchTo(
Const("◀️ Отмена"),
id="cancel_search",
state=AdminMenuSG.residents,
),
state=AdminMenuSG.residents_search_input,
)
search_results_window = Window(
Format("{content}"),
ScrollingGroup(
Select(
Format("{item[0]}"),
id="search_residents_select",
item_id_getter=lambda x: x[1],
items="residents",
on_click=on_search_resident_selected,
),
id="search_residents_scroll",
width=1,
height=7,
when="has_results",
),
SwitchTo(
Const("🔍 Новый поиск"),
id="new_search",
state=AdminMenuSG.residents_search_input,
),
SwitchTo(
Const("◀️ К списку резидентов"),
id="back_to_residents_from_search",
state=AdminMenuSG.residents,
),
state=AdminMenuSG.residents_search_results,
getter=get_search_results_data,
)
resident_delete_confirm_window = Window(
Const(
"<blockquote>⚠️ <b>Подтверждение удаления</b></blockquote>\n\nВы уверены, что хотите удалить этого резидента? Это действие необратимо!"
),
Row(
Button(
Const("✅ Да, удалить"),
id="confirm_delete",
on_click=on_delete_resident_confirm,
),
Button(
Const("❌ Отмена"),
id="cancel_delete",
on_click=on_delete_resident_cancel,
),
),
state=AdminMenuSG.resident_delete_confirm,
)
resident_rebind_floor_window = Window(
Const("<blockquote>🏢 <b>Перепривязка резидента</b></blockquote>\n\nВыберите новый этаж:"),
Group(
Select(
Format("{item[1]}"),
id="rebind_floor_select",
item_id_getter=lambda x: x[0],
items="floors",
on_click=on_rebind_floor_selected,
),
width=2,
),
SwitchTo(
Const("◀️ Отмена"),
id="cancel_rebind_floor",
state=AdminMenuSG.resident_info,
),
state=AdminMenuSG.resident_rebind_floor,
getter=get_rebind_floors_data,
)
resident_rebind_room_window = Window(
Const("<blockquote>🚪 <b>Перепривязка резидента</b></blockquote>\n\nВыберите новую комнату:"),
Group(
Select(
Format("{item[1]}"),
id="rebind_room_select",
item_id_getter=lambda x: x[0],
items="rooms",
on_click=on_rebind_room_selected,
),
width=3,
),
SwitchTo(
Const("◀️ Назад"),
id="back_to_rebind_floor",
state=AdminMenuSG.resident_rebind_floor,
),
state=AdminMenuSG.resident_rebind_room,
getter=get_rebind_rooms_data,
)
resident_rebind_confirm_window = Window(
Format("<blockquote>✅ <b>Подтверждение перепривязки</b></blockquote>\n\nПерепривязать резидента <b>{resident_name}</b> из комнаты <code>{old_room_number}</code> в комнату <code>{new_room_number}</code>?"),
Row(
Button(
Const("✅ Да"),
id="confirm_rebind",
on_click=on_rebind_confirm,
),
Button(
Const("❌ Нет"),
id="cancel_rebind",
on_click=on_rebind_cancel,
),
),
state=AdminMenuSG.resident_rebind_confirm,
getter=get_rebind_confirm_data,
)
resident_history_window = Window(
Format("{history_content}"),
SwitchTo(
Const("◀️ Назад"),
id="back_to_resident_info",
state=AdminMenuSG.resident_info,
),
state=AdminMenuSG.resident_history,
getter=get_resident_history_data,
)