feat: pagination hints
This commit is contained in:
@@ -20,6 +20,7 @@ class CatalogueView:
|
|||||||
children: list[Category] | None = None
|
children: list[Category] | None = None
|
||||||
products: list[Product] | None = None
|
products: list[Product] | None = None
|
||||||
page: int = 0
|
page: int = 0
|
||||||
|
total: int = 0
|
||||||
parent_id: int | str | None = None
|
parent_id: int | str | None = None
|
||||||
has_next: bool = False
|
has_next: bool = False
|
||||||
show_menu: bool = False
|
show_menu: bool = False
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ async def searching(
|
|||||||
await redis_client.set_search_query(query_hash, msg.text)
|
await redis_client.set_search_query(query_hash, msg.text)
|
||||||
next_cb = f"search:{query_hash}:1"
|
next_cb = f"search:{query_hash}:1"
|
||||||
|
|
||||||
|
total = await products_repo.count_search(session, msg.text, include_hidden=is_admin)
|
||||||
|
total_pages = (total + PAGE_SIZE - 1) // PAGE_SIZE if total else None
|
||||||
|
|
||||||
await orig_msg.edit_text(
|
await orig_msg.edit_text(
|
||||||
"<b>🗒 Результаты поиска:</b>",
|
"<b>🗒 Результаты поиска:</b>",
|
||||||
reply_markup=render_products(
|
reply_markup=render_products(
|
||||||
@@ -59,6 +62,8 @@ async def searching(
|
|||||||
back_cb="menu:main",
|
back_cb="menu:main",
|
||||||
next_cb=next_cb,
|
next_cb=next_cb,
|
||||||
is_admin=is_admin,
|
is_admin=is_admin,
|
||||||
|
page=0,
|
||||||
|
total_pages=total_pages,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -89,6 +94,9 @@ async def search_pagination(
|
|||||||
include_hidden=is_admin,
|
include_hidden=is_admin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
total = await products_repo.count_search(session, query, include_hidden=is_admin)
|
||||||
|
total_pages = (total + PAGE_SIZE - 1) // PAGE_SIZE if total else None
|
||||||
|
|
||||||
await cb.message.edit_reply_markup(
|
await cb.message.edit_reply_markup(
|
||||||
reply_markup=render_products(
|
reply_markup=render_products(
|
||||||
results[:PAGE_SIZE],
|
results[:PAGE_SIZE],
|
||||||
@@ -97,5 +105,7 @@ async def search_pagination(
|
|||||||
next_cb=f"search:{q_hash}:{page + 1}" if len(results) > PAGE_SIZE else None,
|
next_cb=f"search:{q_hash}:{page + 1}" if len(results) > PAGE_SIZE else None,
|
||||||
prev_cb=f"search:{q_hash}:{page - 1}" if page > 0 else None,
|
prev_cb=f"search:{q_hash}:{page - 1}" if page > 0 else None,
|
||||||
is_admin=is_admin,
|
is_admin=is_admin,
|
||||||
|
page=page,
|
||||||
|
total_pages=total_pages,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -141,6 +141,8 @@ def render_products(
|
|||||||
columns: int = 2,
|
columns: int = 2,
|
||||||
is_admin: bool = False,
|
is_admin: bool = False,
|
||||||
cat_id: int | None = None,
|
cat_id: int | None = None,
|
||||||
|
page: int = 0,
|
||||||
|
total_pages: int | None = None,
|
||||||
) -> InlineKeyboardMarkup:
|
) -> InlineKeyboardMarkup:
|
||||||
builder = InlineKeyboardBuilder()
|
builder = InlineKeyboardBuilder()
|
||||||
|
|
||||||
@@ -166,7 +168,10 @@ def render_products(
|
|||||||
nav_buttons = []
|
nav_buttons = []
|
||||||
if prev_cb:
|
if prev_cb:
|
||||||
nav_buttons.append(InlineKeyboardButton(text="◀️", callback_data=prev_cb))
|
nav_buttons.append(InlineKeyboardButton(text="◀️", callback_data=prev_cb))
|
||||||
nav_buttons.append(InlineKeyboardButton(text="🔍", callback_data="find"))
|
page_text = (
|
||||||
|
f"{page + 1}/{total_pages}" if total_pages is not None else f"{page + 1}"
|
||||||
|
)
|
||||||
|
nav_buttons.append(InlineKeyboardButton(text=page_text, callback_data="..."))
|
||||||
if next_cb:
|
if next_cb:
|
||||||
nav_buttons.append(InlineKeyboardButton(text="▶️", callback_data=next_cb))
|
nav_buttons.append(InlineKeyboardButton(text="▶️", callback_data=next_cb))
|
||||||
|
|
||||||
@@ -187,6 +192,7 @@ def render_catalogue(view: CatalogueView, *, is_admin: bool):
|
|||||||
cat_id=view.category,
|
cat_id=view.category,
|
||||||
)
|
)
|
||||||
if view.view_type == CatalogueType.PRODUCTS:
|
if view.view_type == CatalogueType.PRODUCTS:
|
||||||
|
total_pages = (view.total + PAGE_SIZE - 1) // PAGE_SIZE if view.total else None
|
||||||
return render_products(
|
return render_products(
|
||||||
products=view.products[:PAGE_SIZE], # type: ignore
|
products=view.products[:PAGE_SIZE], # type: ignore
|
||||||
product_cb_factory=lambda p: f"product:{p.id}",
|
product_cb_factory=lambda p: f"product:{p.id}",
|
||||||
@@ -195,6 +201,8 @@ def render_catalogue(view: CatalogueView, *, is_admin: bool):
|
|||||||
next_cb=(f"products:{view.category}:{view.page + 1}" if view.has_next else None),
|
next_cb=(f"products:{view.category}:{view.page + 1}" if view.has_next else None),
|
||||||
is_admin=is_admin,
|
is_admin=is_admin,
|
||||||
cat_id=view.category,
|
cat_id=view.category,
|
||||||
|
page=view.page,
|
||||||
|
total_pages=total_pages,
|
||||||
)
|
)
|
||||||
|
|
||||||
return get_back_to_catalogue(
|
return get_back_to_catalogue(
|
||||||
|
|||||||
@@ -83,6 +83,40 @@ class ProductRepository:
|
|||||||
await session.commit()
|
await session.commit()
|
||||||
return product
|
return product
|
||||||
|
|
||||||
|
async def count_products_by_category(
|
||||||
|
self,
|
||||||
|
session: AsyncSession,
|
||||||
|
category_id: int,
|
||||||
|
*,
|
||||||
|
include_hidden: bool = False,
|
||||||
|
) -> int:
|
||||||
|
stmt = select(func.count()).select_from(Product).where(
|
||||||
|
Product.category_id == category_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not include_hidden:
|
||||||
|
stmt = stmt.where(Product.is_hidden.is_(False))
|
||||||
|
|
||||||
|
return await session.scalar(stmt)
|
||||||
|
|
||||||
|
async def count_search(
|
||||||
|
self,
|
||||||
|
session: AsyncSession,
|
||||||
|
query: str,
|
||||||
|
*,
|
||||||
|
include_hidden: bool = False,
|
||||||
|
) -> int:
|
||||||
|
ts_query = func.plainto_tsquery("simple", query)
|
||||||
|
|
||||||
|
stmt = select(func.count()).select_from(Product).where(
|
||||||
|
Product.search_vector.op("@@")(ts_query)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not include_hidden:
|
||||||
|
stmt = stmt.where(Product.is_hidden.is_(False))
|
||||||
|
|
||||||
|
return await session.scalar(stmt)
|
||||||
|
|
||||||
async def search(
|
async def search(
|
||||||
self,
|
self,
|
||||||
session: AsyncSession,
|
session: AsyncSession,
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ class CatalogueService:
|
|||||||
view.view_type = CatalogueType.PRODUCTS
|
view.view_type = CatalogueType.PRODUCTS
|
||||||
view.products = products[:PAGE_SIZE]
|
view.products = products[:PAGE_SIZE]
|
||||||
view.has_next = len(products) > PAGE_SIZE
|
view.has_next = len(products) > PAGE_SIZE
|
||||||
|
view.total = await self.product_repo.count_products_by_category(
|
||||||
|
session, cat_id, include_hidden=include_hidden
|
||||||
|
)
|
||||||
|
|
||||||
return view
|
return view
|
||||||
view.view_type = CatalogueType.EMPTY
|
view.view_type = CatalogueType.EMPTY
|
||||||
|
|||||||
Reference in New Issue
Block a user