Add product pagination and enable BestBuy

Implement client-side pagination for the products grid: add PRODUCTS_PER_PAGE, currentProductPage, offset in /products requests, renderPagination(), and changeProductPage() (with smooth scroll). Reset page when filters or event filters change and render empty-state pagination. Add pagination container to the template and styles for pagination buttons and info. Also enable "bestbuy" in SITES_ENABLED in config.py and add a taskkill line to .claude/settings.local.json for closing stray python.exe processes.
This commit is contained in:
2026-03-28 16:57:03 -04:00
parent f01bde54a5
commit d33639fdc7
5 changed files with 79 additions and 3 deletions
+39 -1
View File
@@ -6,6 +6,8 @@ const API_BASE = '/api';
let currentPage = 'dashboard';
let charts = {};
let currentEventFilter = null; // { type: 'new_drop'|'restock', period: 'today'|'week' }
let currentProductPage = 1;
const PRODUCTS_PER_PAGE = 50;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
@@ -81,6 +83,7 @@ function initEventListeners() {
if (el) {
el.addEventListener('change', () => {
currentEventFilter = null; // Clear event filter when using regular filters
currentProductPage = 1;
loadProducts();
});
}
@@ -106,6 +109,7 @@ function clearAllFilters() {
document.getElementById('filterStock').value = '';
document.getElementById('filterFavorites').checked = false;
currentEventFilter = null;
currentProductPage = 1;
loadProducts();
}
@@ -296,12 +300,14 @@ async function loadActivityFeed() {
// Navigate to products filtered by event type
function goToFilteredProducts(eventType, period) {
currentEventFilter = { type: eventType, period: period };
currentProductPage = 1;
navigateTo('products');
}
// Clear event filter
function clearEventFilter() {
currentEventFilter = null;
currentProductPage = 1;
loadProducts();
}
@@ -311,8 +317,9 @@ async function loadProducts() {
const category = document.getElementById('filterCategory')?.value || '';
const inStock = document.getElementById('filterStock')?.value || '';
const favoritesOnly = document.getElementById('filterFavorites')?.checked || false;
const offset = (currentProductPage - 1) * PRODUCTS_PER_PAGE;
let url = '/products?limit=50';
let url = `/products?limit=${PRODUCTS_PER_PAGE}&offset=${offset}`;
if (site) url += `&site=${site}`;
if (category) url += `&category=${encodeURIComponent(category)}`;
if (inStock) url += `&in_stock=${inStock}`;
@@ -333,6 +340,7 @@ async function loadProducts() {
if (data.products.length === 0) {
grid.innerHTML = '<div class="loading">No products found</div>';
renderPagination(0);
return;
}
@@ -376,6 +384,36 @@ async function loadProducts() {
</div>
`;
}).join('');
renderPagination(data.total || 0);
}
function renderPagination(total) {
const container = document.getElementById('productsPagination');
if (!container) return;
const totalPages = Math.ceil(total / PRODUCTS_PER_PAGE);
if (totalPages <= 1) {
container.innerHTML = '';
return;
}
const start = (currentProductPage - 1) * PRODUCTS_PER_PAGE + 1;
const end = Math.min(currentProductPage * PRODUCTS_PER_PAGE, total);
container.innerHTML = `
<div class="pagination">
<button class="btn-page" onclick="changeProductPage(${currentProductPage - 1})" ${currentProductPage === 1 ? 'disabled' : ''}>&laquo; Prev</button>
<span class="page-info">Page ${currentProductPage} of ${totalPages} &nbsp;(${start}${end} of ${total})</span>
<button class="btn-page" onclick="changeProductPage(${currentProductPage + 1})" ${currentProductPage === totalPages ? 'disabled' : ''}>Next &raquo;</button>
</div>
`;
}
function changeProductPage(page) {
currentProductPage = page;
loadProducts();
document.getElementById('page-products').scrollIntoView({ behavior: 'smooth' });
}
// Handle product image errors by showing store icon
+36
View File
@@ -859,6 +859,42 @@ h3 {
background: var(--border-color);
}
/* Pagination */
#productsPagination {
margin-top: 20px;
}
.pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
}
.btn-page {
padding: 8px 18px;
background: var(--bg-card);
color: var(--text-primary);
border: 1px solid var(--border-color);
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
.btn-page:hover:not(:disabled) {
background: var(--border-color);
}
.btn-page:disabled {
opacity: 0.4;
cursor: default;
}
.page-info {
color: var(--text-secondary);
font-size: 14px;
}
/* Settings */
.settings-section {
max-width: 600px;
+1
View File
@@ -110,6 +110,7 @@
<div class="products-grid" id="productsGrid">
<div class="loading">Loading products...</div>
</div>
<div id="productsPagination"></div>
</section>
<!-- Analytics Page -->