From 73bf682f9779d926a01f28bb9b3ed05603f31f50 Mon Sep 17 00:00:00 2001 From: hteppl Date: Thu, 9 Jan 2025 15:48:32 +0300 Subject: [PATCH] Update admin coupons handler --- handlers/admin/admin_coupons.py | 228 +++++++++++++++++--------------- keyboards/admin/coupons_kb.py | 43 ++++++ keyboards/admin/panel_kb.py | 10 ++ 3 files changed, 176 insertions(+), 105 deletions(-) create mode 100644 keyboards/admin/coupons_kb.py diff --git a/handlers/admin/admin_coupons.py b/handlers/admin/admin_coupons.py index 27e3071e..ddcbf67b 100644 --- a/handlers/admin/admin_coupons.py +++ b/handlers/admin/admin_coupons.py @@ -3,56 +3,141 @@ from typing import Any from aiogram import F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from aiogram.types import InlineKeyboardButton -from aiogram.utils.keyboard import InlineKeyboardBuilder -from database import create_coupon, delete_coupon_from_db, get_all_coupons +from database import delete_coupon_from_db, get_all_coupons, create_coupon from filters.admin import IsAdminFilter +from keyboards.admin.coupons_kb import build_coupons_kb, build_coupons_list_kb, AdminCouponDeleteCallback +from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_kb from logger import logger +router = Router() + class AdminCouponsState(StatesGroup): waiting_for_coupon_data = State() -router = Router() - - -@router.callback_query(F.data == "coupons_editor", IsAdminFilter()) +@router.callback_query( + AdminPanelCallback.filter(F.action == "coupons"), + IsAdminFilter(), +) async def show_coupon_management_menu( - callback_query: types.CallbackQuery, state: FSMContext + callback_query: types.CallbackQuery, + state: FSMContext ): await state.clear() - builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="➕ Создать купон", callback_data="create_coupon") - ) - builder.row(InlineKeyboardButton(text="Купоны", callback_data="coupons")) - builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="admin")) + + kb = build_coupons_kb() + await callback_query.message.answer( - "🛠 Меню управления купонами:", reply_markup=builder.as_markup() + text="🛠 Меню управления купонами:", + reply_markup=kb ) -@router.callback_query(F.data == "coupons", IsAdminFilter()) -async def show_coupon_list(callback_query: types.CallbackQuery, session: Any): +@router.callback_query( + AdminPanelCallback.filter(F.action == "coupons_create"), + IsAdminFilter(), +) +async def handle_create_coupon( + callback_query: types.CallbackQuery, + state: FSMContext +): + text = ( + "🎫 Введите данные для создания купона в формате:\n\n" + "📝 код 💰 сумма 🔢 лимит\n\n" + "Пример: 'COUPON1 50 5' 👈\n\n" + ) + + await callback_query.message.answer( + text=text, + reply_markup=build_admin_back_kb("coupons"), + ) + await state.set_state(AdminCouponsState.waiting_for_coupon_data) + + +@router.message( + AdminCouponsState.waiting_for_coupon_data, + IsAdminFilter() +) +async def process_coupon_data( + message: types.Message, + state: FSMContext, + session: Any +): + text = message.text.strip() + parts = text.split() + + kb = build_admin_back_kb("coupons") + + if len(parts) != 3: + text = ( + "❌ Некорректный формат! 📝 Пожалуйста, введите данные в формате:\n" + "🏷️ код 💰 сумма 🔢 лимит\n" + "Пример: 'COUPON1 50 5' 👈" + ) + + await message.answer( + text=text, + reply_markup=kb, + ) + return + + try: + coupon_code = parts[0] + coupon_amount = float(parts[1]) + usage_limit = int(parts[2]) + except ValueError: + text = ( + "⚠️ Проверьте правильность введенных данных!\n" + "💱 Сумма должна быть числом, а лимит — целым числом." + ) + + await message.answer( + text=text, + reply_markup=kb, + ) + return + + try: + await create_coupon(coupon_code, coupon_amount, usage_limit, session) + + result_message = ( + f"✅ Купон с кодом {coupon_code} успешно создан!\n" + f"💰 Сумма: {coupon_amount} рублей \n" + f"🔢 Лимит использования: {usage_limit} раз" + ) + + await message.answer( + text=result_message, + reply_markup=kb + ) + await state.clear() + + except Exception as e: + logger.error(f"Ошибка при создании купона: {e}") + + +@router.callback_query( + AdminPanelCallback.filter(F.action == "coupons_list"), + IsAdminFilter(), +) +async def show_coupon_list( + callback_query: types.CallbackQuery, + session: Any +): try: coupons = await get_all_coupons(session) if not coupons: - builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor") - ) - await callback_query.message.answer( - "❌ На данный момент нет доступных купонов. 🚫\nВы можете вернуться в меню управления. 🔙", - reply_markup=builder.as_markup(), + text="❌ На данный момент нет доступных купонов. 🚫\nВы можете вернуться в меню управления. 🔙", + reply_markup=build_admin_back_kb("coupons"), ) return + kb = build_coupons_list_kb(coupons) coupon_list = "📜 Список всех купонов:\n\n" - builder = InlineKeyboardBuilder() for coupon in coupons: coupon_list += ( @@ -62,27 +147,25 @@ async def show_coupon_list(callback_query: types.CallbackQuery, session: Any): f"✅ Использовано: {coupon['usage_count']} раз\n\n" ) - builder.row( - InlineKeyboardButton( - text=f"❌ Удалить {coupon['code']}", - callback_data=f"delete_coupon_{coupon['code']}", - ) - ) - - builder.row( - InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor") - ) await callback_query.message.answer( - coupon_list, reply_markup=builder.as_markup() + text=coupon_list, + reply_markup=kb ) except Exception as e: logger.error(f"Ошибка при получении списка купонов: {e}") -@router.callback_query(F.data.startswith("delete_coupon_"), IsAdminFilter()) -async def handle_delete_coupon(callback_query: types.CallbackQuery, session: Any): - coupon_code = callback_query.data[len("delete_coupon_") :] +@router.callback_query( + AdminCouponDeleteCallback.filter(), + IsAdminFilter(), +) +async def handle_delete_coupon( + callback_query: types.CallbackQuery, + callback_data: AdminCouponDeleteCallback, + session: Any +): + coupon_code = callback_data.coupon_code try: result = await delete_coupon_from_db(coupon_code, session) @@ -91,74 +174,9 @@ async def handle_delete_coupon(callback_query: types.CallbackQuery, session: Any await show_coupon_list(callback_query, session) else: await callback_query.message.answer( - f"❌ Купон с кодом {coupon_code} не найден.", + text=f"❌ Купон с кодом {coupon_code} не найден.", ) await show_coupon_list(callback_query, session) except Exception as e: logger.error(f"Ошибка при удалении купона: {e}") - - -@router.callback_query(F.data == "create_coupon", IsAdminFilter()) -async def handle_create_coupon(callback_query: types.CallbackQuery, state: FSMContext): - builder = InlineKeyboardBuilder() - builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor")) - - await callback_query.message.answer( - "🎫 Введите данные для создания купона в формате:\n\n" - "📝 код 💰 сумма 🔢 лимит\n\n" - "Пример: 'COUPON1 50 5' 👈\n\n", - reply_markup=builder.as_markup(), - ) - await state.set_state(AdminCouponsState.waiting_for_coupon_data) - - -@router.message(AdminCouponsState.waiting_for_coupon_data, IsAdminFilter()) -async def process_coupon_data(message: types.Message, state: FSMContext, session: Any): - text = message.text.strip() - - parts = text.split() - - builder = InlineKeyboardBuilder() - builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor")) - - if len(parts) != 3: - await message.answer( - "❌ Некорректный формат! 📝 Пожалуйста, введите данные в формате:\n" - "🏷️ код 💰 сумма 🔢 лимит\n" - "Пример: 'COUPON1 50 5' 👈", - reply_markup=builder.as_markup(), - ) - return - - try: - coupon_code = parts[0] - coupon_amount = float(parts[1]) - usage_limit = int(parts[2]) - except ValueError: - await message.answer( - "⚠️ Проверьте правильность введенных данных!\n" - "💱 Сумма должна быть числом, 🔢 а лимит — целым числом.", - reply_markup=builder.as_markup(), - ) - return - - try: - await create_coupon(coupon_code, coupon_amount, usage_limit, session) - - result_message = ( - f"✅ Купон с кодом {coupon_code} успешно создан! 🎉\n" - f"Сумма: {coupon_amount} рублей 💰\n" - f"Лимит использования: {usage_limit} раз 🔢." - ) - - builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor") - ) - - await message.answer(result_message, reply_markup=builder.as_markup()) - await state.clear() - - except Exception as e: - logger.error(f"Ошибка при создании купона: {e}") diff --git a/keyboards/admin/coupons_kb.py b/keyboards/admin/coupons_kb.py new file mode 100644 index 00000000..25467a16 --- /dev/null +++ b/keyboards/admin/coupons_kb.py @@ -0,0 +1,43 @@ +from aiogram.filters.callback_data import CallbackData +from aiogram.types import InlineKeyboardMarkup +from aiogram.utils.keyboard import InlineKeyboardBuilder + +from keyboards.admin.panel_kb import AdminPanelCallback + + +class AdminCouponDeleteCallback(CallbackData, prefix='admin_coupon_delete'): + coupon_code: str + + +def build_coupons_kb() -> InlineKeyboardMarkup: + builder = InlineKeyboardBuilder() + builder.button( + text="➕ Создать купон", + callback_data=AdminPanelCallback(action="coupons_create").pack() + ) + builder.button( + text="Купоны", + callback_data=AdminPanelCallback(action="coupons_list").pack() + ) + builder.button( + text="🔙 Назад", + callback_data="admin" + ) + return builder.as_markup() + + +def build_coupons_list_kb(coupons: list) -> InlineKeyboardMarkup: + builder = InlineKeyboardBuilder() + + for coupon in coupons: + coupon_code = coupon["code"] + builder.button( + text=f"❌ Удалить {coupon_code}", + callback_data=AdminCouponDeleteCallback(coupon_code=coupon_code), + ) + + builder.button( + text="🔙 Назад", + callback_data=AdminPanelCallback(action="coupons").pack() + ) + return builder.as_markup() diff --git a/keyboards/admin/panel_kb.py b/keyboards/admin/panel_kb.py index 3c517d48..b8e9133c 100644 --- a/keyboards/admin/panel_kb.py +++ b/keyboards/admin/panel_kb.py @@ -78,3 +78,13 @@ def build_restart_kb() -> InlineKeyboardMarkup: callback_data="admin" ) return builder.as_markup() + + +def build_admin_back_kb(action: str) -> InlineKeyboardMarkup: + return build_admin_singleton_kb("🔙 Назад", action) + + +def build_admin_singleton_kb(text: str, action: str) -> InlineKeyboardMarkup: + builder = InlineKeyboardBuilder() + builder.button(text=text, callback_data=AdminPanelCallback(action=action).pack()) + return builder.as_markup()