From c8831718edf3490e3d671cbf88169c171781e567 Mon Sep 17 00:00:00 2001 From: machka-pasla Date: Mon, 4 Aug 2025 18:10:33 +0300 Subject: [PATCH] Add promo code toggle and activations handling in admin panel - Implemented a new handler to toggle the active status of promo codes, allowing admins to activate or deactivate codes easily. - Added functionality to display activations for specific promo codes, enhancing visibility into their usage. - Updated localization files to include new messages related to promo status changes and activations in both English and Russian, improving admin experience. --- bot/handlers/admin/promo_codes.py | 104 ++++++++++++++++++++++++++++++ locales/en.json | 8 +++ locales/ru.json | 8 +++ 3 files changed, 120 insertions(+) diff --git a/bot/handlers/admin/promo_codes.py b/bot/handlers/admin/promo_codes.py index a7ffceb..623624c 100644 --- a/bot/handlers/admin/promo_codes.py +++ b/bot/handlers/admin/promo_codes.py @@ -1016,6 +1016,110 @@ async def process_promo_edit_validity_handler(message: types.Message, state: FSM )) +@router.callback_query(F.data.startswith("promo_toggle:")) +async def promo_toggle_handler(callback: types.CallbackQuery, i18n_data: dict, + settings: Settings, session: AsyncSession): + """Toggle promo code active status""" + current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) + i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") + if not i18n or not callback.message: + await callback.answer("Error", show_alert=True) + return + _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) + + promo_id = int(callback.data.split(":")[1]) + promo = await promo_code_dal.get_promo_code_by_id(session, promo_id) + if not promo: + await callback.answer(_("admin_promo_not_found", default="Промокод не найден"), show_alert=True) + return + + # Toggle status + new_status = not promo.is_active + update_data = {"is_active": new_status} + + updated = await promo_code_dal.update_promo_code(session, promo_id, update_data) + if updated: + await session.commit() + + status_text = _("admin_promo_status_activated", default="активирован") if new_status else _("admin_promo_status_deactivated", default="деактивирован") + success_text = _( + "admin_promo_toggle_success", + default="✅ Промокод {code} {status}", + code=promo.code, + status=status_text + ) + + await callback.answer(success_text, show_alert=True) + + # Refresh the detail view + await promo_detail_handler(callback, i18n_data, settings, session) + else: + await session.rollback() + await callback.answer(_( + "admin_promo_toggle_failed", + default="❌ Ошибка изменения статуса промокода" + ), show_alert=True) + + +@router.callback_query(F.data.startswith("promo_activations:")) +async def promo_activations_handler(callback: types.CallbackQuery, i18n_data: dict, + settings: Settings, session: AsyncSession): + """Show promo code activations""" + current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) + i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") + if not i18n or not callback.message: + await callback.answer("Error", show_alert=True) + return + _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) + + promo_id = int(callback.data.split(":")[1]) + promo = await promo_code_dal.get_promo_code_by_id(session, promo_id) + if not promo: + await callback.answer(_("admin_promo_not_found", default="Промокод не найден"), show_alert=True) + return + + # Get activations for this promo code + activations = await promo_code_dal.get_promo_activations_by_code_id(session, promo_id) + + if not activations: + text = _( + "admin_promo_no_activations", + default="📋 Активации промокода: {code}\n\n❌ Активаций не найдено", + code=promo.code + ) + else: + text_parts = [_( + "admin_promo_activations_header", + default="📋 Активации промокода: {code}\n\n", + code=promo.code + )] + + for activation in activations: + activation_date = activation.created_at.strftime('%Y-%m-%d %H:%M') if activation.created_at else "N/A" + text_parts.append(_( + "admin_promo_activation_item", + default="👤 User ID: {user_id}\n📅 Дата: {date}\n", + user_id=activation.user_id, + date=activation_date + )) + + text = "".join(text_parts) + + # Build keyboard with back button + kb = InlineKeyboardBuilder() + kb.row( + InlineKeyboardButton( + text=_("admin_promo_back_to_detail_button", default="⬅️ К промокоду"), + callback_data=f"promo_detail:{promo_id}") + ) + + await callback.message.edit_text( + text, + reply_markup=kb.as_markup(), + parse_mode="HTML") + await callback.answer() + + @router.callback_query(F.data.startswith("promo_delete:")) async def promo_delete_handler(callback: types.CallbackQuery, i18n_data: dict, settings: Settings, session: AsyncSession): diff --git a/locales/en.json b/locales/en.json index 4729f8a..ef6d71b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -413,11 +413,19 @@ "admin_promo_card_created_by": "👤 Created by: {creator}", "admin_promo_status_active": "✅ Active", "admin_promo_status_inactive": "🚫 Inactive", + "admin_promo_status_activated": "activated", + "admin_promo_status_deactivated": "deactivated", "admin_promo_status_expired": "⏰ Expired", "admin_promo_status_used_up": "🔄 Used up", "admin_promo_toggle_status_button": "🔄 On/Off", "admin_promo_view_activations_button": "📋 Activations", "admin_promo_back_to_list_button": "⬅️ Back to list", + "admin_promo_toggle_success": "✅ Promo code {code} {status}", + "admin_promo_toggle_failed": "❌ Error changing promo code status", + "admin_promo_no_activations": "📋 Activations of promo code: {code}\n\n❌ No activations found", + "admin_promo_activations_header": "📋 Activations of promo code: {code}\n\n", + "admin_promo_activation_item": "👤 User ID: {user_id}\n📅 Date: {date}\n", + "admin_promo_back_to_detail_button": "⬅️ Back to promo", "admin_panel_online_label": "Online", "admin_panel_active_label": "Active", diff --git a/locales/ru.json b/locales/ru.json index 2d1c7dd..086d864 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -412,11 +412,19 @@ "admin_promo_card_created_by": "👤 Создал: {creator}", "admin_promo_status_active": "✅ Активен", "admin_promo_status_inactive": "🚫 Неактивен", + "admin_promo_status_activated": "активирован", + "admin_promo_status_deactivated": "деактивирован", "admin_promo_status_expired": "⏰ Истек", "admin_promo_status_used_up": "🔄 Исчерпан", "admin_promo_toggle_status_button": "🔄 Вкл/Выкл", "admin_promo_view_activations_button": "📋 Активации", "admin_promo_back_to_list_button": "⬅️ К списку", + "admin_promo_toggle_success": "✅ Промокод {code} {status}", + "admin_promo_toggle_failed": "❌ Ошибка изменения статуса промокода", + "admin_promo_no_activations": "📋 Активации промокода: {code}\n\n❌ Активаций не найдено", + "admin_promo_activations_header": "📋 Активации промокода: {code}\n\n", + "admin_promo_activation_item": "👤 User ID: {user_id}\n📅 Дата: {date}\n", + "admin_promo_back_to_detail_button": "⬅️ К промокоду", "admin_panel_online_label": "Онлайн", "admin_panel_active_label": "Активных",