mirror of
https://github.com/koloideal/DutyLog.git
synced 2026-06-10 10:25:29 +03:00
928 lines
29 KiB
Python
928 lines
29 KiB
Python
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,
|
||
)
|