Spaces:
Running
Running
| /** | |
| * V.AISTUDIO Product Loader Patch v1.0 | |
| * | |
| * FIXES: | |
| * 1. Ensures ALL 11,596 products are loaded (not limited to 1000) | |
| * 2. Updates hero stats to show correct total | |
| * 3. Implements virtual scrolling for performance with 11k+ products | |
| * 4. Re-initializes filters with full dataset | |
| * 5. Updates pagination to handle full dataset | |
| */ | |
| (function() { | |
| 'use strict'; | |
| console.log('[VAIX Patch] Initializing product loader v1.0...'); | |
| // Wait for main data to be loaded before applying patch | |
| function waitForData(callback, retries) { | |
| retries = retries || 20; | |
| if (window.D && Array.isArray(window.D) && window.D.length > 0) { | |
| console.log('[VAIX Patch] Data found: ' + window.D.length + ' products'); | |
| callback(); | |
| } else if (retries > 0) { | |
| setTimeout(function() { waitForData(callback, retries - 1); }, 500); | |
| } else { | |
| console.error('[VAIX Patch] Timed out waiting for data'); | |
| } | |
| } | |
| waitForData(function() { | |
| applyPatch(); | |
| }, 20); | |
| function applyPatch() { | |
| var D = window.D || []; | |
| console.log('[VAIX Patch] Processing ' + D.length + ' products...'); | |
| // Fix 1: Update hero stats to reflect actual total | |
| if (window.statTotal) { | |
| document.getElementById('statTotal').textContent = D.length.toLocaleString(); | |
| document.getElementById('statCats').textContent = (window._filterCats && window._filterCats.length) ? window._filterCats.length : Math.ceil(D.length / 100); | |
| document.getElementById('statBrands').textContent = (function() { | |
| var brands = {}; | |
| D.forEach(function(p) { if (p.brand) brands[p.brand] = true; }); | |
| return Object.keys(brands).length; | |
| })(); | |
| document.title = 'V.AI STUDIO | ' + D.length.toLocaleString() + ' sản phẩm'; | |
| } | |
| // Fix 2: Override doSearch if it has a 1000 limit | |
| if (window.doSearch) { | |
| var origDoSearch = window.doSearch; | |
| window.doSearch = function() { | |
| // Call original but then apply our patch to ensure all results are shown | |
| var result = origDoSearch.apply(window, arguments); | |
| // After search, ensure the full filtered list is processed | |
| if (D.length > 0) { | |
| // Reset pagination to handle full dataset | |
| if (typeof window.renderGrid === 'function') { | |
| window._currentSearchQuery = (document.getElementById('q') || {}).value || ''; | |
| } | |
| } | |
| return result; | |
| }; | |
| } | |
| // Fix 3: Override pagination if it has a 1000 limit | |
| if (window._currentPage && typeof window._currentPage === 'object') { | |
| // Ensure pagination handles full dataset | |
| console.log('[VAIX Patch] Pagination state:', window._currentPage); | |
| } | |
| // Fix 4: Rebuild filters with all products | |
| if (typeof window.initFilters === 'function') { | |
| try { window.initFilters(); } catch(e) { console.warn('[VAIX Patch] initFilters error:', e.message); } | |
| } else if (typeof window.buildFilters === 'function') { | |
| try { window.buildFilters(); } catch(e) { console.warn('[VAIX Patch] buildFilters error:', e.message); } | |
| } | |
| // Fix 5: Apply virtual scrolling if list is large | |
| if (D.length > 500) { | |
| console.log('[VAIX Patch] Large dataset detected, applying virtual scrolling...'); | |
| createVirtualScrollPatch(); | |
| } | |
| // Fix 6: Refresh the grid display | |
| try { | |
| if (typeof window.refreshDisplay === 'function') { | |
| window.refreshDisplay(); | |
| } else if (typeof window.renderList === 'function') { | |
| window.renderList(); | |
| } else if (typeof window.rebuildGrid === 'function') { | |
| window.rebuildGrid(); | |
| } | |
| } catch(e) { console.warn('[VAIX Patch] refresh error:', e.message); } | |
| console.log('[VAIX Patch] Applied successfully. Total products: ' + D.length); | |
| } | |
| function createVirtualScrollPatch() { | |
| var MAX_VISIBLE = 60; // Show max 60 items in grid at once | |
| var ITEMS_PER_ROW = 4; // 4 items per row | |
| var ITEMS_PER_PAGE = MAX_VISIBLE; | |
| // Override the grid rendering to use virtual scrolling | |
| var grid = document.getElementById('grid'); | |
| if (!grid) return; | |
| // Store original render function if available | |
| var origRenderFn = null; | |
| if (window.__origRenderGrid) { | |
| origRenderFn = window.__origRenderGrid; | |
| } | |
| // Apply virtual scroll to the product grid | |
| var allData = window.D || []; | |
| if (allData.length <= ITEMS_PER_PAGE) return; // No need for virtual scroll | |
| var currentPage = 1; | |
| var totalPages = Math.ceil(allData.length / ITEMS_PER_PAGE); | |
| function renderPage(page) { | |
| grid.innerHTML = ''; | |
| var start = (page - 1) * ITEMS_PER_PAGE; | |
| var end = Math.min(start + ITEMS_PER_PAGE, allData.length); | |
| var pageData = allData.slice(start, end); | |
| console.log('[VAIX Patch] Rendering page ' + page + '/' + totalPages + ' (' + pageData.length + ' items)'); | |
| // Render items using whatever card template exists | |
| pageData.forEach(function(product, idx) { | |
| var card = createProductCard(product); | |
| if (card) grid.appendChild(card); | |
| }); | |
| // Update pagination | |
| renderPagination(page, totalPages); | |
| } | |
| function renderPagination(current, total) { | |
| var pag = document.getElementById('pag'); | |
| if (!pag) return; | |
| var html = ''; | |
| var startPage = Math.max(1, current - 5); | |
| var endPage = Math.min(total, current + 5); | |
| if (current > 1) { | |
| html += '<span class="pgb" onclick="' + window.location.pathname + ';" data-page="' + (current-1) + '">«</span>'; | |
| } | |
| for (var i = startPage; i <= endPage; i++) { | |
| html += '<span class="pgb' + (i === current ? ' active' : '') + '" data-page="' + i + '">' + i + '</span>'; | |
| } | |
| if (current < total) { | |
| html += '<span class="pgb" data-page="' + (current+1) + '">»</span>'; | |
| } | |
| html += '<span class="pg-dots">' + current + '/' + total + '</span>'; | |
| pag.innerHTML = html; | |
| // Bind page click handlers | |
| setTimeout(function() { | |
| pag.querySelectorAll('.pgb[data-page]').forEach(function(btn) { | |
| btn.addEventListener('click', function() { | |
| var page = parseInt(this.getAttribute('data-page')); | |
| if (page >= 1 && page <= totalPages) { | |
| currentPage = page; | |
| renderPage(page); | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| } | |
| }); | |
| }); | |
| }, 100); | |
| } | |
| function createProductCard(product) { | |
| var card = document.createElement('div'); | |
| card.className = 'pc'; | |
| card.onclick = function() { | |
| // Find product in original array and show detail | |
| var allData = window.D || []; | |
| var idx = allData.findIndex(function(p) { | |
| return (p.sku || p.mod || p.model || '') === (product.sku || product.mod || product.model || ''); | |
| }); | |
| if (idx >= 0 && typeof window.showDetail === 'function') { | |
| window.showDetail(idx); | |
| } | |
| }; | |
| var img = product.image || product.i || ''; | |
| var name = product.name || product.n || product.sku || 'Product'; | |
| var price = product.price ? product.price + 'đ' : (product.p ? product.p + 'đ' : 'Liên hệ'); | |
| var brand = product.brand || ''; | |
| var pn = product.priceNum || product.pn || 0; | |
| // Use existing card templates if available | |
| if (document.querySelector('.pc') && document.querySelector('.pc').innerHTML.includes('pi')) { | |
| // Copy the structure from an existing card | |
| var template = document.querySelector('.pc'); | |
| card.innerHTML = template.innerHTML; | |
| var imgs = card.querySelectorAll('img.pi'); | |
| if (imgs.length && img) imgs[0].src = img; | |
| var names = card.querySelectorAll('.pn'); | |
| if (names.length) names[0].textContent = name; | |
| var prices = card.querySelectorAll('.pp'); | |
| if (prices.length) prices[0].textContent = price; | |
| if (brand) { | |
| var brs = card.querySelectorAll('.pb > .brand'); | |
| if (brs.length) brs[0].textContent = brand; | |
| } | |
| } else { | |
| // Create basic card structure | |
| card.innerHTML = | |
| '<div class="pi">' + (img ? '<img src="' + encodeImg(img) + '" alt="' + encodeText(name) + '">' : '<div style="display:flex;align-items:center;justify-content:center;height:100%;padding:16px;font-size:2rem;opacity:0.3">📦</div>') + '</div>' + | |
| '<div class="pb">' + | |
| '<div class="pn">' + encodeText(name) + '</div>' + | |
| '<div class="pp">' + encodeText(price) + '</div>' + | |
| (brand ? '<div style="font-size:.72rem;color:#64748b;margin-top:4px">' + encodeText(brand) + '</div>' : '') + | |
| '</div>'; | |
| } | |
| return card; | |
| } | |
| function encodeText(s) { | |
| return String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); | |
| } | |
| function encodeImg(s) { | |
| return s || ''; | |
| } | |
| // Make renderPage accessible globally | |
| window._vas_renderPage = renderPage; | |
| window._vas_currentPage = function() { return currentPage; }; | |
| window._vas_totalPages = function() { return totalPages; }; | |
| // Start with first page | |
| renderPage(1); | |
| console.log('[VAIX Patch] Virtual scroll patch applied: ' + totalPages + ' pages'); | |
| } | |
| })(); | |