from typing import Optional, Callable from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiogram.utils.keyboard import InlineKeyboardBuilder from config import PAGE_SIZE from db.models import Category, Product from dto.cart import CartItemDTO from dto.catalogue import CatalogueView, CatalogueType from .common import return_menu def pagination_row( *, page: int, prev_cb: Optional[str] = None, next_cb: Optional[str] = None, ) -> list[InlineKeyboardButton]: row = [] if not (prev_cb or next_cb): return row if prev_cb: row.append(InlineKeyboardButton(text="⬅️", callback_data=prev_cb)) row.append(InlineKeyboardButton(text=str(page + 1), callback_data="...")) if next_cb: row.append(InlineKeyboardButton(text="➡️", callback_data=next_cb)) return row def cart_btn(cart_items: int) -> InlineKeyboardButton: return InlineKeyboardButton(text=f"🛒 Корзина ({cart_items})", callback_data="cart") def get_back_to_catalogue( cat_id: int, *, text: Optional[str] = "⬅️ Назад" ) -> InlineKeyboardMarkup: return InlineKeyboardBuilder( [[InlineKeyboardButton(text=text, callback_data=f"cat:{cat_id}")]] ).as_markup() def main_menu_kb(cart_items: int) -> InlineKeyboardMarkup: return InlineKeyboardBuilder( [ [ InlineKeyboardButton(text="📦 Каталог", callback_data="cat:root"), InlineKeyboardButton(text="🔍", callback_data="find"), ], [cart_btn(cart_items)], ] ).as_markup() def render_category( children: list[Category], parent_id: Optional[int] = None, show_menu: bool = False, *, category_cb_factory: Callable[[Category], str], is_admin: bool = False, ) -> InlineKeyboardMarkup: btns = [ InlineKeyboardButton( text=category.name, callback_data=category_cb_factory(category), ) for category in children ] nav: list[list[InlineKeyboardButton]] = [] if is_admin: nav.append( [ InlineKeyboardButton( text="📄 Добавить категорию", callback_data=f"new:category:{parent_id}", ) ] ) if show_menu: nav.append([InlineKeyboardButton(text="⬅️ Назад", callback_data="menu:main")]) elif parent_id is not None: nav.append( [InlineKeyboardButton(text="⬅️ Назад", callback_data=f"cat:{parent_id}")] ) return InlineKeyboardBuilder( [btns[i : i + 2] for i in range(0, len(children), 2)] + nav, ).as_markup() def render_products( products: list[Product], *, product_cb_factory: Callable[[Product], str], back_cb: Optional[str] = None, prev_cb: Optional[str] = None, next_cb: Optional[str] = None, columns: int = 2, is_admin: bool = False, cat_id: Optional[int] = None, ) -> InlineKeyboardMarkup: builder = InlineKeyboardBuilder() if back_cb: builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=back_cb)) for i in range(0, len(products), columns): row = [ InlineKeyboardButton( text=p.name, callback_data=product_cb_factory(p), ) for p in products[i : i + columns] ] builder.row(*row) if is_admin: builder.row( InlineKeyboardButton( text="📦 Добавить товар", callback_data=f"new:product:{cat_id}" ) ) nav_buttons = [] if prev_cb: nav_buttons.append(InlineKeyboardButton(text="◀️", callback_data=prev_cb)) if next_cb: nav_buttons.append(InlineKeyboardButton(text="▶️", callback_data=next_cb)) if nav_buttons: builder.row(*nav_buttons) return builder.as_markup() def render_catalogue(view: CatalogueView, *, is_admin: bool): if view.view_type == CatalogueType.CATEGORIES: return render_category( children=view.children[:PAGE_SIZE], # type: ignore parent_id=view.parent_id, show_menu=view.show_menu, category_cb_factory=lambda c: f"cat:{c.id}", is_admin=is_admin, ) if view.view_type == CatalogueType.PRODUCTS: return render_products( products=view.products[:PAGE_SIZE], # type: ignore product_cb_factory=lambda p: f"product:{p.id}", back_cb=f"cat:{view.parent_id}", prev_cb=( f"products:{view.category}:{view.page - 1}" if view.page > 0 else None ), next_cb=( f"products:{view.category}:{view.page + 1}" if view.has_next else None ), is_admin=is_admin, ) return return_menu def render_product_interactions( *, product_id: int, category_id: int, show_cart: bool = False, cart_amount: int = 0 ) -> InlineKeyboardMarkup: btns: list[list[InlineKeyboardButton]] = [] if show_cart: btns.append( [ InlineKeyboardButton( text="➖", callback_data=f"cart_action:remove:{product_id}" ), InlineKeyboardButton(text=f"🛒 {cart_amount}", callback_data="..."), InlineKeyboardButton( text="➕", callback_data=f"cart_action:add:{product_id}" ), ] ) btns.append( [ InlineKeyboardButton( text="❌ Удалить из корзины", callback_data=f"cart_action:clear:{product_id}", ) ] ) else: btns.append( [ InlineKeyboardButton( text="🛒 В корзину", callback_data=f"cart_action:add:{product_id}" ) ] ) btns.append([InlineKeyboardButton(text="⬅️", callback_data=f"cat:{category_id}")]) return InlineKeyboardBuilder(btns).as_markup() def render_cart( cart_items: list[CartItemDTO], *, page: int = 0, show_prev: Optional[bool] = False, show_next: Optional[bool] = False, show_purchase: Optional[bool] = False, ) -> InlineKeyboardMarkup: btns = [ InlineKeyboardButton( text=f"🛒 {cart_item.quantity} | {cart_item.name} | {cart_item.total}₽", callback_data=f"product:{cart_item.product_id}", ) for cart_item in cart_items ] nav = pagination_row( page=page, prev_cb=f"cart:{page - 1}" if show_prev else None, next_cb=f"cart:{page + 1}" if show_next else None, ) markup: list[list[InlineKeyboardButton]] = ( [ [ InlineKeyboardButton( text="⬅️ Назад", callback_data="menu:main", ) ], ] + [btns[i : i + 2] for i in range(0, len(cart_items), 2)] + [nav] ) if show_purchase: markup.append( [InlineKeyboardButton(text="🔷 Оформить заказ", callback_data="checkout")] ) return InlineKeyboardBuilder(markup).as_markup() def order_confirmation(order_id: int) -> InlineKeyboardMarkup: return InlineKeyboardBuilder( [ [ InlineKeyboardButton( text="🟢 Подтвердить заказ", callback_data=f"confirm_order:{order_id}", ) ], [InlineKeyboardButton(text="❌ Отменить", callback_data="menu:main")], ] ).as_markup() def order_specs_confirmation(order_id: int) -> InlineKeyboardMarkup: return InlineKeyboardBuilder( [ [ InlineKeyboardButton( text="🟢 Подтвердить", callback_data=f"payment:{order_id}", ) ], [InlineKeyboardButton(text="❌", callback_data="menu:main")], ] ).as_markup() def confirm_payment(invoice_id: int): return InlineKeyboardBuilder( [ [ InlineKeyboardButton( text="✅ Подтвердить оплату", callback_data=f"refresh_payment:{invoice_id}", ) ] ] ).as_markup()