V.AISTUDIO2 / vai-fix.js
bep40's picture
Khôi phục file từ commit a3b87f83469d
f91b98c verified
Raw
History Blame Contribute Delete
13.3 kB
/**
* 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 = '<tr><td colspan="10" style="text-align:center;padding:30px;color:var(--g)">Gio hang trong</td></tr>';
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 '<tr>'
+ '<td style="text-align:center">' + (i + 1) + '</td>'
+ '<td><img class="qt-img" src="' + (c.image || '') + '" referrerpolicy="no-referrer" alt="' + (c.name||'') + '" onerror="this.style.display=\'none\'" style="width:72px;height:72px;object-fit:contain"></td>'
+ '<td class="qt-name">' + (c.name||'') + '</td>'
+ '<td style="text-align:center">' + model + '</td>'
+ '<td style="font-size:.72rem;color:var(--g);max-width:160px">' + specs + '</td>'
+ '<td style="text-align:center">' + (c.qty || 1) + '</td>'
+ '<td class="qt-price">' + (c.priceNum > 0 ? Number(c.priceNum).toLocaleString('vi-VN') + 'đ' : 'Lien he') + '</td>'
+ '<td><input class="qt-disc" data-idx="' + i + '" value="' + (c.priceNum > 0 ? Number(c.priceNum).toLocaleString('vi-VN') : '') + '" oninput="window.updateQuoteTotal()" ' + (locked ? 'disabled' : '') + ' style="' + discStyle + '"></td>'
+ '<td class="qt-price qt-linetotal" data-idx="' + i + '">' + (lineTotal > 0 ? lineTotal.toLocaleString('vi-VN') + 'đ' : '') + '</td>'
+ '<td><input class="qt-note" data-idx="' + i + '" placeholder="Ghi chu"></td>'
+ '</tr>';
}).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');
})();