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()