diff --git a/bot/handlers/admin/promo/manage.py b/bot/handlers/admin/promo/manage.py index 1b00406..97a5620 100644 --- a/bot/handlers/admin/promo/manage.py +++ b/bot/handlers/admin/promo/manage.py @@ -34,16 +34,12 @@ async def view_promo_codes_handler(callback: types.CallbackQuery, offset=0) if not promo_models: - text = _( - "admin_no_promo_codes", - default="πŸ“‹ АктивныС ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Ρ‹\n\n❌ НСт Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²" - ) + text = f"{_('admin_active_promos_list_header')}\n\n{_('admin_no_active_promos')}" else: - lines = [_("admin_active_promo_codes_title", - default="πŸ“‹ АктивныС ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Ρ‹\n")] + lines = [_("admin_active_promos_list_header"), ""] for promo in promo_models: - validity_str = _("admin_promo_unlimited", default="Π‘Π΅Π· ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠΉ") + validity_str = _("admin_promo_valid_indefinitely") if promo.valid_until: validity_str = promo.valid_until.strftime("%d.%m.%Y") @@ -90,10 +86,7 @@ async def promo_management_handler(callback: types.CallbackQuery, promo_models = await promo_code_dal.get_all_promo_codes_with_details(session, limit=50, offset=0) if not promo_models: - text = _( - "admin_no_promo_codes_to_manage", - default="πŸ›  Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π°ΠΌΠΈ\n\n❌ НСт ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ² для управлСния" - ) + text = _("admin_promo_management_empty") await callback.message.edit_text( text, reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), @@ -103,21 +96,9 @@ async def promo_management_handler(callback: types.CallbackQuery, return builder = InlineKeyboardBuilder() - text_lines = [_("admin_promo_management_title", default="πŸ›  Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π°ΠΌΠΈ\n")] + text = _("admin_promo_management_title") for promo in promo_models: - status_emoji = "βœ…" if promo.is_active else "❌" - validity_str = _("admin_promo_unlimited", default="∞") - if promo.valid_until: - validity_str = promo.valid_until.strftime("%d.%m") - - text_lines.append( - f"{status_emoji} {promo.code} | " - f"🎁{promo.bonus_days}Π΄ | " - f"πŸ“Š{promo.current_activations}/{promo.max_activations} | " - f"⏰{validity_str}" - ) - builder.row( InlineKeyboardButton( text=f"πŸ“ {promo.code}", @@ -127,13 +108,11 @@ async def promo_management_handler(callback: types.CallbackQuery, builder.row( InlineKeyboardButton( - text=_("admin_back_to_panel", default="πŸ”™ Π’ Π°Π΄ΠΌΠΈΠ½ панСль"), + text=_("back_to_admin_panel_button"), callback_data="admin_action:main" ) ) - text = "\n".join(text_lines) - try: await callback.message.edit_text( text, @@ -170,80 +149,65 @@ async def promo_detail_handler(callback: types.CallbackQuery, 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) + await callback.answer(_("admin_promo_not_found"), show_alert=True) return - # Format promo details - status_text = _("admin_promo_active", default="βœ… АктивСн") if promo.is_active else _("admin_promo_inactive", default="❌ НСактивСн") - validity_text = _("admin_promo_unlimited", default="Π‘Π΅Π· ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠΉ") + status = _("admin_promo_status_active") if promo.is_active else _("admin_promo_status_inactive") + if promo.valid_until and promo.valid_until < datetime.now(timezone.utc): + status = _("admin_promo_status_expired") + elif promo.current_activations >= promo.max_activations: + status = _("admin_promo_status_used_up") + + validity = _("admin_promo_valid_indefinitely") if promo.valid_until: - if promo.valid_until > datetime.now(timezone.utc): - validity_text = promo.valid_until.strftime("%d.%m.%Y %H:%M") - else: - validity_text = f"{promo.valid_until.strftime('%d.%m.%Y %H:%M')} (истСк)" + validity = promo.valid_until.strftime("%d.%m.%Y %H:%M") - created_at = promo.created_at.strftime("%d.%m.%Y %H:%M") if promo.created_at else "N/A" + created = promo.created_at.strftime("%d.%m.%Y %H:%M") if promo.created_at else "N/A" - text = _( - "admin_promo_detail_info", - default="🎟 Π”Π΅Ρ‚Π°Π»ΠΈ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π°\n\n" - "πŸ“ Код: {code}\n" - "🎁 БонусныС Π΄Π½ΠΈ: {bonus_days}\n" - "πŸ“Š Использований: {current}/{max}\n" - "πŸ“‹ Бтатус: {status}\n" - "⏰ ДСйствуСт Π΄ΠΎ: {validity}\n" - "πŸ‘€ Π‘ΠΎΠ·Π΄Π°Π»: {admin_id}\n" - "πŸ• Π‘ΠΎΠ·Π΄Π°Π½: {created}", - code=promo.code, - bonus_days=promo.bonus_days, - current=promo.current_activations, - max=promo.max_activations, - status=status_text, - validity=validity_text, - admin_id=promo.created_by_admin_id, - created=created_at - ) + text = "\n".join([ + _("admin_promo_card_title", code=promo.code), + _("admin_promo_card_bonus_days", days=promo.bonus_days), + _("admin_promo_card_activations", current=promo.current_activations, max=promo.max_activations), + _("admin_promo_card_validity", validity=validity), + _("admin_promo_card_status", status=status), + _("admin_promo_card_created", created=created), + _("admin_promo_card_created_by", creator=promo.created_by_admin_id) + ]) # Create management buttons builder = InlineKeyboardBuilder() - # Edit button builder.row( InlineKeyboardButton( - text=_("admin_promo_edit", default="✏️ Π Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ"), + text=_("admin_promo_edit_button"), callback_data=f"promo_edit:{promo_id}" ) ) - # Toggle active/inactive - toggle_text = _("admin_promo_deactivate", default="❌ Π”Π΅Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ") if promo.is_active else _("admin_promo_activate", default="βœ… ΠΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ") builder.row( InlineKeyboardButton( - text=toggle_text, + text=_("admin_promo_toggle_status_button"), callback_data=f"promo_toggle:{promo_id}" ) ) - # View activations builder.row( InlineKeyboardButton( - text=_("admin_promo_view_activations", default="πŸ“‹ Активации"), + text=_("admin_promo_view_activations_button"), callback_data=f"promo_activations:{promo_id}" ) ) - # Delete button builder.row( InlineKeyboardButton( - text=_("admin_promo_delete", default="πŸ—‘ Π£Π΄Π°Π»ΠΈΡ‚ΡŒ"), + text=_("admin_promo_delete_button"), callback_data=f"promo_delete:{promo_id}" ) ) - # Back button builder.row( InlineKeyboardButton( - text=_("admin_back_to_management", default="πŸ”™ К ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΡŽ"), + text=_("admin_promo_back_to_list_button"), callback_data="admin_action:promo_management" ) ) @@ -263,7 +227,7 @@ async def promo_detail_handler(callback: types.CallbackQuery, await callback.answer() except ValueError: - await callback.answer(_("admin_invalid_promo_id", default="❌ НСвСрный ID ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π°"), show_alert=True) + await callback.answer(_("admin_promo_not_found"), show_alert=True) except Exception as e: logging.error(f"Error in promo detail: {e}") await callback.answer(_("error_occurred_try_again"), show_alert=True) @@ -285,7 +249,7 @@ async def promo_toggle_handler(callback: types.CallbackQuery, i18n_data: dict, 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) + await callback.answer(_("admin_promo_not_found"), show_alert=True) return new_status = not promo.is_active @@ -294,9 +258,9 @@ async def promo_toggle_handler(callback: types.CallbackQuery, i18n_data: dict, updated = await promo_code_dal.update_promo_code(session, promo_id, update_data) if updated: await session.commit() - status_text = _("admin_promo_activated", default="Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Π½") if new_status else _("admin_promo_deactivated", default="Π΄Π΅Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Π½") + status_text = _("admin_promo_status_activated") if new_status else _("admin_promo_status_deactivated") await callback.answer( - _("admin_promo_status_changed", default="βœ… ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ {status}", status=status_text) + _("admin_promo_toggle_success", code=promo.code, status=status_text) ) # Refresh the detail view callback.data = f"promo_detail:{promo_id}" @@ -305,7 +269,7 @@ async def promo_toggle_handler(callback: types.CallbackQuery, i18n_data: dict, await callback.answer(_("error_occurred_try_again"), show_alert=True) except ValueError: - await callback.answer(_("admin_invalid_promo_id", default="❌ НСвСрный ID ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π°"), show_alert=True) + await callback.answer(_("admin_promo_not_found"), show_alert=True) except Exception as e: logging.error(f"Error toggling promo: {e}") await callback.answer(_("error_occurred_try_again"), show_alert=True) @@ -327,35 +291,31 @@ async def promo_activations_handler(callback: types.CallbackQuery, i18n_data: di 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) + await callback.answer(_("admin_promo_not_found"), show_alert=True) return activations = await promo_code_dal.get_promo_activations_by_code_id(session, promo_id) - text_lines = [ - _("admin_promo_activations_title", - default="πŸ“‹ Активации ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π° {code}\n", - code=promo.code) - ] - if not activations: - text_lines.append(_("admin_no_activations", default="❌ НСт Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΉ")) + text = _("admin_promo_no_activations", code=promo.code) else: - for activation in activations[:10]: # Show last 10 - activated_at = activation.activated_at.strftime("%d.%m.%Y %H:%M") if activation.activated_at else "N/A" + text_lines = [ + _("admin_promo_activations_header", code=promo.code) + ] + for activation in activations[:20]: text_lines.append( - f"πŸ‘€ {activation.user_id} | πŸ• {activated_at}" + _("admin_promo_activation_item", + user_id=activation.user_id, + date=activation.activated_at.strftime("%d.%m.%Y %H:%M")) ) - - if len(activations) > 10: - text_lines.append(f"\n... ΠΈ Π΅Ρ‰Π΅ {len(activations) - 10}") - - text = "\n".join(text_lines) + if len(activations) > 20: + text_lines.append(f"\n... (Π΅Ρ‰Π΅ {len(activations) - 20})") + text = "\n".join(text_lines) builder = InlineKeyboardBuilder() builder.row( InlineKeyboardButton( - text=_("admin_back_to_promo", default="πŸ”™ К ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Ρƒ"), + text=_("admin_promo_back_to_detail_button"), callback_data=f"promo_detail:{promo_id}" ) ) @@ -375,7 +335,7 @@ async def promo_activations_handler(callback: types.CallbackQuery, i18n_data: di await callback.answer() except ValueError: - await callback.answer(_("admin_invalid_promo_id", default="❌ НСвСрный ID ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π°"), show_alert=True) + await callback.answer(_("admin_promo_not_found"), show_alert=True) except Exception as e: logging.error(f"Error viewing activations: {e}") await callback.answer(_("error_occurred_try_again"), show_alert=True) @@ -399,16 +359,16 @@ async def promo_delete_handler(callback: types.CallbackQuery, i18n_data: dict, if promo: await session.commit() await callback.answer( - _("admin_promo_deleted", default="βœ… ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ ΡƒΠ΄Π°Π»Π΅Π½", code=promo.code) + _("admin_promo_deleted_success", code=promo.code) ) # Go back to management callback.data = "admin_action:promo_management" await promo_management_handler(callback, i18n_data, settings, session) else: - await callback.answer(_("admin_promo_not_found", default="❌ ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½"), show_alert=True) + await callback.answer(_("admin_promo_not_found"), show_alert=True) except ValueError: - await callback.answer(_("admin_invalid_promo_id", default="❌ НСвСрный ID ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π°"), show_alert=True) + await callback.answer(_("admin_promo_not_found"), show_alert=True) except Exception as e: logging.error(f"Error deleting promo: {e}") await callback.answer(_("error_occurred_try_again"), show_alert=True) @@ -419,4 +379,4 @@ async def manage_promo_codes_handler(callback: types.CallbackQuery, i18n_data: dict, settings: Settings, session: AsyncSession): """Redirect to new unified handler""" - await promo_management_handler(callback, i18n_data, settings, session) \ No newline at end of file + await promo_management_handler(callback, i18n_data, settings, session)