[FIX] website_sale_aplicoop: Complete infinite scroll and search filter integration
Major fixes: - Fix JSON body parsing in load_products_ajax with type='http' route * Parse JSON from request.httprequest.get_data() instead of post params * Correctly read page, search, category from JSON request body - Fix search and category filter combination * Use intersection (&) instead of replacement to preserve both filters * Now respects search AND category simultaneously - Integrate realtime_search.js with infinite_scroll.js * Add resetWithFilters() method to reset scroll to page 1 with new filters * When search/category changes, reload products from server * Clear grid and load fresh results - Fix pagination reset logic * Set currentPage = 0 in resetWithFilters() so loadNextPage() increments to 1 * Prevents loading empty page 2 when resetting filters Results: ✅ Infinite scroll loads all pages correctly (1, 2, 3...) ✅ Search filters work across all products (not just loaded) ✅ Category filters work correctly ✅ Search AND category filters work together ✅ Page resets to 1 when filters change
This commit is contained in:
parent
534876242e
commit
5eb039ffe0
4 changed files with 603 additions and 71 deletions
225
website_sale_aplicoop/static/src/js/infinite_scroll.js
Normal file
225
website_sale_aplicoop/static/src/js/infinite_scroll.js
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/**
|
||||
* Infinite Scroll Handler for Eskaera Shop
|
||||
*
|
||||
* Automatically loads more products as user scrolls down the page.
|
||||
* Falls back to manual "Load More" button if disabled or on error.
|
||||
*/
|
||||
|
||||
console.log("[INFINITE_SCROLL] Script loaded!");
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// Also run immediately if DOM is already loaded
|
||||
var initInfiniteScroll = function () {
|
||||
console.log("[INFINITE_SCROLL] Initializing infinite scroll...");
|
||||
|
||||
var infiniteScroll = {
|
||||
orderId: null,
|
||||
searchQuery: "",
|
||||
category: "0",
|
||||
perPage: 20,
|
||||
currentPage: 1,
|
||||
isLoading: false,
|
||||
hasMore: true,
|
||||
config: {},
|
||||
|
||||
init: function () {
|
||||
// Get configuration from page data
|
||||
var configEl = document.getElementById("eskaera-config");
|
||||
if (!configEl) {
|
||||
console.log("[INFINITE_SCROLL] No eskaera-config found, lazy loading disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
this.orderId = configEl.getAttribute("data-order-id");
|
||||
this.searchQuery = configEl.getAttribute("data-search") || "";
|
||||
this.category = configEl.getAttribute("data-category") || "0";
|
||||
this.perPage = parseInt(configEl.getAttribute("data-per-page")) || 20;
|
||||
this.currentPage = parseInt(configEl.getAttribute("data-current-page")) || 1;
|
||||
|
||||
// Check if there are more products to load
|
||||
var hasNextBtn = document.getElementById("load-more-btn");
|
||||
this.hasMore = hasNextBtn && hasNextBtn.offsetParent !== null; // offsetParent === null means hidden
|
||||
|
||||
if (!this.hasMore) {
|
||||
console.log("[INFINITE_SCROLL] No more products to load");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[INFINITE_SCROLL] Initialized with:", {
|
||||
orderId: this.orderId,
|
||||
searchQuery: this.searchQuery,
|
||||
category: this.category,
|
||||
perPage: this.perPage,
|
||||
currentPage: this.currentPage,
|
||||
});
|
||||
|
||||
this.attachScrollListener();
|
||||
// Also keep the button listener as fallback
|
||||
this.attachFallbackButtonListener();
|
||||
},
|
||||
|
||||
attachScrollListener: function () {
|
||||
var self = this;
|
||||
var scrollThreshold = 0.8; // Load when 80% scrolled
|
||||
|
||||
window.addEventListener("scroll", function () {
|
||||
if (self.isLoading || !self.hasMore) {
|
||||
return;
|
||||
}
|
||||
|
||||
var scrollHeight = document.documentElement.scrollHeight;
|
||||
var scrollTop = window.scrollY;
|
||||
var clientHeight = window.innerHeight;
|
||||
var scrollPercent = (scrollTop + clientHeight) / scrollHeight;
|
||||
|
||||
if (scrollPercent >= scrollThreshold) {
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] Scroll threshold reached, loading next page"
|
||||
);
|
||||
self.loadNextPage();
|
||||
}
|
||||
});
|
||||
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] Scroll listener attached (threshold:",
|
||||
scrollThreshold * 100 + "%)"
|
||||
);
|
||||
},
|
||||
|
||||
attachFallbackButtonListener: function () {
|
||||
var self = this;
|
||||
var btn = document.getElementById("load-more-btn");
|
||||
|
||||
if (!btn) {
|
||||
console.log("[INFINITE_SCROLL] No fallback button found");
|
||||
return;
|
||||
}
|
||||
|
||||
btn.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
if (!self.isLoading && self.hasMore) {
|
||||
console.log("[INFINITE_SCROLL] Manual button click, loading next page");
|
||||
self.loadNextPage();
|
||||
}
|
||||
});
|
||||
|
||||
console.log("[INFINITE_SCROLL] Fallback button listener attached");
|
||||
},
|
||||
|
||||
loadNextPage: function () {
|
||||
var self = this;
|
||||
this.isLoading = true;
|
||||
this.currentPage += 1;
|
||||
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] Loading page",
|
||||
this.currentPage,
|
||||
"for order",
|
||||
this.orderId
|
||||
);
|
||||
|
||||
// Show spinner
|
||||
var spinner = document.getElementById("loading-spinner");
|
||||
if (spinner) {
|
||||
spinner.classList.remove("d-none");
|
||||
}
|
||||
|
||||
var data = {
|
||||
page: this.currentPage,
|
||||
search: this.searchQuery,
|
||||
category: this.category,
|
||||
};
|
||||
|
||||
fetch("/eskaera/" + this.orderId + "/load-products-ajax", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(function (response) {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok: " + response.status);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(function (result) {
|
||||
if (result.error) {
|
||||
console.error("[INFINITE_SCROLL] Server error:", result.error);
|
||||
self.isLoading = false;
|
||||
self.currentPage -= 1;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[INFINITE_SCROLL] Page loaded successfully", result);
|
||||
|
||||
// Insert HTML into grid
|
||||
var grid = document.getElementById("products-grid");
|
||||
if (grid && result.html) {
|
||||
grid.insertAdjacentHTML("beforeend", result.html);
|
||||
console.log("[INFINITE_SCROLL] Products inserted into grid");
|
||||
}
|
||||
|
||||
// Update has_more flag
|
||||
self.hasMore = result.has_next || false;
|
||||
|
||||
if (!self.hasMore) {
|
||||
console.log("[INFINITE_SCROLL] No more products available");
|
||||
}
|
||||
|
||||
// Hide spinner
|
||||
if (spinner) {
|
||||
spinner.classList.add("d-none");
|
||||
}
|
||||
|
||||
self.isLoading = false;
|
||||
|
||||
// Re-attach event listeners for newly added products
|
||||
if (
|
||||
window.aplicoopShop &&
|
||||
typeof window.aplicoopShop._attachEventListeners === "function"
|
||||
) {
|
||||
window.aplicoopShop._attachEventListeners();
|
||||
console.log("[INFINITE_SCROLL] Event listeners re-attached");
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error("[INFINITE_SCROLL] Fetch error:", error);
|
||||
self.isLoading = false;
|
||||
self.currentPage -= 1;
|
||||
|
||||
// Hide spinner on error
|
||||
if (spinner) {
|
||||
spinner.classList.add("d-none");
|
||||
}
|
||||
|
||||
// Show fallback button
|
||||
var btn = document.getElementById("load-more-btn");
|
||||
if (btn) {
|
||||
btn.classList.remove("d-none");
|
||||
btn.style.display = "";
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Initialize infinite scroll
|
||||
infiniteScroll.init();
|
||||
|
||||
// Export to global scope for debugging
|
||||
window.infiniteScroll = infiniteScroll;
|
||||
};
|
||||
|
||||
// Run on DOMContentLoaded if DOM not yet ready
|
||||
if (document.readyState === "loading") {
|
||||
console.log("[INFINITE_SCROLL] DOM not ready, waiting for DOMContentLoaded...");
|
||||
document.addEventListener("DOMContentLoaded", initInfiniteScroll);
|
||||
} else {
|
||||
// DOM is already loaded
|
||||
console.log("[INFINITE_SCROLL] DOM already loaded, initializing immediately...");
|
||||
initInfiniteScroll();
|
||||
}
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue