[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:
snt 2026-02-17 00:28:17 +01:00
parent 534876242e
commit 5eb039ffe0
4 changed files with 603 additions and 71 deletions

View 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();
}
})();