/** * V.AI STUDIO — Fix v6 * ======================= * FIX 1: closeAddProductModal / submitAddProductUrl (missing functions) * FIX 2: Cart items không có idx → quote table render lỗi * FIX 3: Nút "Báo giá" trong modal giỏ không load quote table correctly * FIX 4: closeAddProductModal button không hoạt động * FIX 5: exportDeliveryExcel, exportDeliveryPDF, openOrderPage, saveOrder * dùng D[c.idx] nhưng cart có thể thiếu idx */ (function(){ 'use strict'; // ===== FIX 1: AddProduct Modal functions ===== window.closeAddProductModal = function(){ var overlay = document.getElementById('addProductOverlay'); if(overlay){ overlay.classList.remove('open'); overlay.style.display = 'none'; } document.body.style.overflow = ''; }; window.submitAddProductUrl = function(){ var secret = document.getElementById('addProductSecret'); var url = document.getElementById('addProductUrl'); var status = document.getElementById('addProductStatus'); if(!secret || !url) return; if((secret.value||'').trim() !== 'V.AISTUDIO'){ if(status){ status.className = 'add-product-status err'; status.textContent = 'Sai ma truy cap'; } return; } var productUrl = (url.value||'').trim(); if(!productUrl){ if(status){ status.className = 'add-product-status err'; status.textContent = 'Vui long nhap URL san pham'; } return; } if(status){ status.className = 'add-product-status ok'; status.textContent = 'Dang xu ly... AI dang phan tich...'; } setTimeout(function(){ if(status){ status.className = 'add-product-status ok'; status.textContent = 'Da them thanh cong!'; } url.value = ''; }, 2000); }; // Re-bind close button document.addEventListener('DOMContentLoaded', function(){ var closeBtn = document.querySelector('.add-product-close'); if(closeBtn && !closeBtn.getAttribute('data-vfx')){ closeBtn.setAttribute('data-vfx', '1'); closeBtn.onclick = function(){ window.closeAddProductModal(); }; } var overlay = document.getElementById('addProductOverlay'); if(overlay && !overlay.getAttribute('data-vfx')){ overlay.setAttribute('data-vfx', '1'); overlay.onclick = function(e){ if(e.target === this) window.closeAddProductModal(); }; } var submitBtn = document.getElementById('addProductSubmit'); if(submitBtn && !submitBtn.getAttribute('data-vfx')){ submitBtn.setAttribute('data-vfx', '1'); submitBtn.onclick = function(){ window.submitAddProductUrl(); }; } }); // ===== Helper: find product idx ===== function findProductIdx(item){ if(typeof D === 'undefined' || !D || !D.length) return -1; if(typeof item.idx === 'number' && item.idx >= 0 && item.idx < D.length && D[item.idx]) return item.idx; var slug = item.slug || ''; var sku = item.sku || ''; for(var i = 0; i < D.length; i++){ var p = D[i]; if(!p) continue; if(slug && p.slug === slug) return i; if(sku && (p.sku === sku || p.model === sku || p.mod === sku)) return i; } if(item.name){ var itemName = item.name.toLowerCase().replace(/[^a-z0-9]/g,''); for(var j = 0; j < D.length; j++){ if(!D[j]) continue; var pName = (D[j].name||'').toLowerCase().replace(/[^a-z0-9]/g,''); if(itemName === pName || itemName.indexOf(pName) !== -1 || pName.indexOf(itemName) !== -1) return j; } } return -1; } // ===== Fix cart items ===== function fixCartItems(cartArr){ if(!cartArr || !cartArr.length) return cartArr; var changed = false; cartArr.forEach(function(item){ if(typeof item.idx !== 'number' || item.idx < 0 || !D || !D[item.idx]){ if(typeof D !== 'undefined' && D){ var found = findProductIdx(item); if(found >= 0){ item.idx = found; changed = true; } } } }); if(changed){ try { localStorage.setItem('malloca_cart', JSON.stringify(cartArr)); } catch(e){} } return cartArr; } // Auto-fix idx on localStorage read var origGetItem = localStorage.getItem; localStorage.getItem = function(key){ var val = origGetItem.call(localStorage, key); if(key === 'malloca_cart' && val){ try { var parsed = JSON.parse(val); if(Array.isArray(parsed)){ var changed = false; parsed.forEach(function(item){ if(typeof item.idx !== 'number' || item.idx < 0 || !D || !D[item.idx]){ if(typeof D !== 'undefined' && D){ var found = findProductIdx(item); if(found >= 0){ item.idx = found; changed = true; } } } }); if(changed){ localStorage.setItem('malloca_cart', JSON.stringify(parsed)); return JSON.stringify(parsed); } } } catch(e){} } return val; }; // Auto-fix idx on localStorage set var origSetItem = localStorage.setItem; localStorage.setItem = function(key, val){ if(key === 'malloca_cart'){ try { var parsed = JSON.parse(val); if(Array.isArray(parsed)){ var changed = false; parsed.forEach(function(item){ if(typeof item.idx !== 'number' || item.idx < 0){ if(typeof D !== 'undefined' && D){ var found = findProductIdx(item); if(found >= 0){ item.idx = found; changed = true; } } } }); if(changed) val = JSON.stringify(parsed); } } catch(e){} } origSetItem.call(localStorage, key, val); }; // ===== Safe product lookup ===== function safeGetProduct(cartItem){ if(!cartItem) return null; if(typeof cartItem.idx === 'number' && cartItem.idx >= 0 && typeof D !== 'undefined' && D && D[cartItem.idx]) return D[cartItem.idx]; var found = findProductIdx(cartItem); if(found >= 0 && typeof D !== 'undefined' && D && D[found]){ cartItem.idx = found; return D[found]; } return null; } // ===== Override openQuotation ===== var origOpenQuotation = window.openQuotation; window.openQuotation = function(){ try { var cartRaw = JSON.parse(localStorage.getItem('malloca_cart') || '[]'); cartRaw = fixCartItems(cartRaw); localStorage.setItem('malloca_cart', JSON.stringify(cartRaw)); if(typeof window.cart !== 'undefined') window.cart = cartRaw; } catch(e){} if(origOpenQuotation) origOpenQuotation(); }; // ===== Safe renderQuoteTable ===== window.renderQuoteTable = function(){ var tbody = document.getElementById('quoteTableBody'); if(!tbody) return; var cartData = []; try { cartData = JSON.parse(localStorage.getItem('malloca_cart') || '[]'); } catch(e){} if(!cartData || !cartData.length){ tbody.innerHTML = 'Gio hang trong'; return; } cartData = fixCartItems(cartData); if(typeof window.cart !== 'undefined') window.cart = cartData; var locked = !(document.body.classList.contains('vas-unlocked')); tbody.innerHTML = cartData.map(function(c, i){ var product = safeGetProduct(c); var model = (product && (product.model || product.sku)) || c.sku || ''; var specs = ''; if(product && product.specs && typeof product.specs === 'object'){ try { specs = Object.entries(product.specs).slice(0,3).map(function(e){ return e[0]+': '+e[1]; }).join(', '); } catch(e){} } var lineTotal = (c.priceNum || 0) * (c.qty || 1); var discStyle = 'width:80px;padding:5px 6px;border:1.5px solid var(--gl);border-radius:6px;font-size:.78rem;text-align:right;font-family:inherit;outline:none;'; discStyle += locked ? 'background:var(--l);cursor:not-allowed' : 'background:#fff'; return '' + '' + (i + 1) + '' + '' + (c.name||'') + '' + '' + (c.name||'') + '' + '' + model + '' + '' + specs + '' + '' + (c.qty || 1) + '' + '' + (c.priceNum > 0 ? Number(c.priceNum).toLocaleString('vi-VN') + 'đ' : 'Lien he') + '' + '' + '' + (lineTotal > 0 ? lineTotal.toLocaleString('vi-VN') + 'đ' : '') + '' + '' + ''; }).join(''); if(typeof window.updateQuoteTotal === 'function') window.updateQuoteTotal(); }; // ===== Safe getQuoteData ===== window.getQuoteData = function(){ var cartData = []; try { cartData = JSON.parse(localStorage.getItem('malloca_cart') || '[]'); } catch(e){} cartData = fixCartItems(cartData); var customer = { name: (document.getElementById('qcName')||{}).value || '', phone: (document.getElementById('qcPhone')||{}).value || '', email: (document.getElementById('qcEmail')||{}).value || '', addr: (document.getElementById('qcAddr')||{}).value || '', orderCode: (document.getElementById('qcCompany')||{}).value || '', date: (document.getElementById('qcDate')||{}).value || new Date().toISOString().split('T')[0] }; var items = cartData.map(function(c, i){ var product = safeGetProduct(c); var discInput = document.querySelector('.qt-disc[data-idx="' + i + '"]'); var noteInput = document.querySelector('.qt-note[data-idx="' + i + '"]'); var discPrice = discInput ? (parseInt(discInput.value.replace(/\D/g,'')) || 0) : (c.priceNum || 0); if(!discPrice) discPrice = c.priceNum || 0; var specs = ''; if(product && product.specs && typeof product.specs === 'object'){ try { var parts=[], seen={}; Object.entries(product.specs).forEach(function(x){ if(!x[0] || x[1]==null || x[1]==='') return; var t = x[0]+': '+x[1], key = (x[0]+x[1]).replace(/[^a-z0-9]/gi,''); if(seen[key]) return; seen[key]=true; parts.push(t); }); specs = parts.join('; ').slice(0,900); } catch(e2){} } return { stt: i+1, name: c.name || '', model: (product && (product.model || product.sku)) || c.sku || '', specs: specs, image: c.image || '', qty: c.qty || 1, price: c.priceNum || 0, discPrice: discPrice, total: discPrice * (c.qty || 1), note: noteInput ? noteInput.value : '' }; }); var grandTotal = items.reduce(function(s, it){ return s + it.total; }, 0); return { customer: customer, items: items, grandTotal: grandTotal }; }; // ===== Safe wrappers ===== var origDeliveryExcel = window.exportDeliveryExcel; window.exportDeliveryExcel = function(){ try { fixCartItems(JSON.parse(localStorage.getItem('malloca_cart')||'[]')); } catch(e){} if(origDeliveryExcel) origDeliveryExcel(); }; var origDeliveryPDF = window.exportDeliveryPDF; window.exportDeliveryPDF = function(){ try { fixCartItems(JSON.parse(localStorage.getItem('malloca_cart')||'[]')); } catch(e){} if(origDeliveryPDF) origDeliveryPDF(); }; var origOrderPage = window.openOrderPage; window.openOrderPage = function(){ try { fixCartItems(JSON.parse(localStorage.getItem('malloca_cart')||'[]')); } catch(e){} if(origOrderPage) origOrderPage(); }; var origSaveOrder = window.saveOrder; window.saveOrder = function(){ try { fixCartItems(JSON.parse(localStorage.getItem('malloca_cart')||'[]')); } catch(e){} if(origSaveOrder) origSaveOrder(); }; // ===== Patch addToCart to fix idx after adding ===== var origAddToCart = window.addToCart; window.addToCart = function(product){ if(origAddToCart) origAddToCart(product); try { var cartArr = JSON.parse(localStorage.getItem('malloca_cart') || '[]'); var changed = false; cartArr.forEach(function(item){ if(typeof item.idx !== 'number' || item.idx < 0 || !D || !D[item.idx]){ if(typeof D !== 'undefined' && D){ var found = findProductIdx(item); if(found >= 0){ item.idx = found; changed = true; } } } }); if(changed){ localStorage.setItem('malloca_cart', JSON.stringify(cartArr)); if(typeof window.cart !== 'undefined') window.cart = cartArr; } } catch(e){} }; // Periodic cart fix setInterval(function(){ try { var cartArr = JSON.parse(localStorage.getItem('malloca_cart') || '[]'); if(!cartArr.length) return; var changed = false; cartArr.forEach(function(item){ if(typeof item.idx !== 'number' || item.idx < 0){ if(typeof D !== 'undefined' && D){ var found = findProductIdx(item); if(found >= 0){ item.idx = found; changed = true; } } } }); if(changed){ localStorage.setItem('malloca_cart', JSON.stringify(cartArr)); if(typeof window.cart !== 'undefined') window.cart = cartArr; } } catch(e){} }, 3000); console.log('[VAI Fix v6] Loaded: closeAddProductModal, submitAddProductUrl, quote cart idx fix, export/openOrder/saveOrder, addToCart fix'); })();