page is better in shop now
This commit is contained in:
@@ -315,6 +315,7 @@ order-search-placeholder = Search orders…
|
||||
search-empty = Nothing matched your search:
|
||||
results-count = { $count } products
|
||||
sort-label = Sort
|
||||
per-page-label = Per page
|
||||
sort-relevance = Relevance
|
||||
sort-newest = Newest
|
||||
sort-price_asc = Price: low to high
|
||||
|
||||
@@ -315,6 +315,7 @@ order-search-placeholder = Hľadať objednávky…
|
||||
search-empty = Pre váš výraz sme nič nenašli:
|
||||
results-count = { $count } produktov
|
||||
sort-label = Zoradiť
|
||||
per-page-label = Na stránku
|
||||
sort-relevance = Relevancia
|
||||
sort-newest = Najnovšie
|
||||
sort-price_asc = Cena: od najnižšej
|
||||
|
||||
@@ -52,6 +52,24 @@
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<!-- per-page count -->
|
||||
<label class="flex items-center gap-2 text-xs font-medium text-on-surface/70 dark:text-on-surface-dark/70">
|
||||
{{ t(key="per-page-label", lang=L) }}
|
||||
<select name="per_page"
|
||||
class="rounded-radius border border-outline bg-surface px-2 py-1.5 text-sm text-on-surface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
|
||||
{% for opt in per_page_options %}
|
||||
<option value="{{ opt }}"{% if per_page == opt %} selected{% endif %}>{{ opt }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<!-- in stock only -->
|
||||
<label class="flex items-center gap-2 text-sm text-on-surface dark:text-on-surface-dark">
|
||||
<input type="checkbox" name="in_stock" value="1"{% if in_stock %} checked{% endif %}
|
||||
class="size-4 rounded border-outline text-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:text-primary-dark" />
|
||||
{{ t(key="filter-in-stock", lang=L) }}
|
||||
</label>
|
||||
|
||||
<!-- grid / list view toggle -->
|
||||
<div class="inline-flex gap-0.5 rounded-radius border border-outline p-0.5 dark:border-outline-dark" role="group"
|
||||
aria-label="{{ t(key='view-grid', lang=L) }} / {{ t(key='view-list', lang=L) }}">
|
||||
|
||||
@@ -22,8 +22,21 @@ use crate::{
|
||||
views::shop as view,
|
||||
};
|
||||
|
||||
/// Results per page in the storefront listing/search.
|
||||
/// Default results per page in the storefront listing/search.
|
||||
const PER_PAGE: usize = 24;
|
||||
/// Allowed per-page choices offered in the toolbar; any other value falls back
|
||||
/// to [`PER_PAGE`].
|
||||
const PER_PAGE_OPTIONS: [usize; 3] = [24, 48, 96];
|
||||
|
||||
/// Resolve the requested per-page count to one of [`PER_PAGE_OPTIONS`],
|
||||
/// defaulting to [`PER_PAGE`].
|
||||
fn resolve_per_page(params: &SearchParams) -> usize {
|
||||
params
|
||||
.per_page
|
||||
.map(|p| p as usize)
|
||||
.filter(|p| PER_PAGE_OPTIONS.contains(p))
|
||||
.unwrap_or(PER_PAGE)
|
||||
}
|
||||
/// Hard cap on candidates a single text search considers before faceting; well
|
||||
/// above any realistic page of results for this catalog.
|
||||
const SEARCH_CAP: u64 = 1000;
|
||||
@@ -40,6 +53,7 @@ struct SearchParams {
|
||||
in_stock: Option<String>,
|
||||
sort: Option<String>,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
}
|
||||
|
||||
/// A candidate product with everything the listing needs to filter, sort and
|
||||
@@ -81,6 +95,9 @@ fn query_base(params: &SearchParams) -> String {
|
||||
if let Some(s) = params.sort.as_deref().filter(|s| !s.is_empty()) {
|
||||
ser.append_pair("sort", s);
|
||||
}
|
||||
if let Some(p) = params.per_page.filter(|p| *p as usize != PER_PAGE) {
|
||||
ser.append_pair("per_page", &p.to_string());
|
||||
}
|
||||
ser.finish()
|
||||
}
|
||||
|
||||
@@ -198,14 +215,15 @@ async fn run_search(
|
||||
}
|
||||
|
||||
// 7. Paginate.
|
||||
let per_page = resolve_per_page(params);
|
||||
let total = items.len();
|
||||
let pages = total.div_ceil(PER_PAGE).max(1);
|
||||
let pages = total.div_ceil(per_page).max(1);
|
||||
let page = params.page.unwrap_or(1).clamp(1, pages as u32);
|
||||
let start = (page as usize - 1) * PER_PAGE;
|
||||
let start = (page as usize - 1) * per_page;
|
||||
|
||||
// 8. Render only the current page's cards (images fetched per row).
|
||||
let mut rows = Vec::new();
|
||||
for item in items.iter().skip(start).take(PER_PAGE) {
|
||||
for item in items.iter().skip(start).take(per_page) {
|
||||
let image = product_images::first_for(ctx, item.product.id).await?;
|
||||
let cat_name = item.product.category_id.and_then(|id| category_name.get(&id).cloned());
|
||||
rows.push(view::product_card(
|
||||
@@ -229,6 +247,8 @@ async fn run_search(
|
||||
"selected_category_id": selected_category.parse::<i32>().unwrap_or(-1),
|
||||
"uncategorized_count": uncategorized_count,
|
||||
"sort": sort,
|
||||
"per_page": per_page,
|
||||
"per_page_options": PER_PAGE_OPTIONS,
|
||||
"in_stock": in_stock_only,
|
||||
"min_price": params.min_price.clone().unwrap_or_default(),
|
||||
"max_price": params.max_price.clone().unwrap_or_default(),
|
||||
|
||||
Reference in New Issue
Block a user