// BIG SALE Floating Promotion Module v1.1 // Integrates BIG SALE promotion button with product detail modal // Compatible with bep40/vai-avatar2 space architecture // // CHANGES v1.1: // - Removed 6 BIG SALE products that have NO image and NO description in the // catalog dataset (bep40/grob-products-updated): K136A, MST-6667, // MOVS-10128, "BỘ LÀM SẠCH NHÀ BẾP", 901X, 642X. These fell back to emoji // placeholders / empty body in the detail modal. 25 -> 19 products. // - Fixed the share link. Previously it pointed to the production space // https://bep40-vai-avatar.hf.space/?product=, which (a) does not load // this big_sale module at all and (b) opens the catalog product at its // LISTED price (no BIG SALE discount). Now the share link is self-hosted // (?bigsale= on the CURRENT page/origin), and a deep-link handler opens // the BIG SALE detail modal with the correct BIG SALE (discounted) price. console.log('[BIG SALE] Module loaded (v1.1)'); // BIG SALE Product Data — 19 products with discount info // (removed: K136A, MST-6667, MOVS-10128, BỘ LÀM SẠCH NHÀ BẾP, 901X, 642X) const DEFAULT_BIG_SALE_PRODUCTS = [ { stt: 1, code: "MC-7086HS", name: "MÁY HÚT", category: "MÁY HÚT", qty: 3, price: "11.389.000", discount: "45%", bigsale: "6.263.950", status: "HÀNG MỚI" }, { stt: 2, code: "MH-70SMC", name: "MÁY HÚT ECO", category: "MÁY HÚT", qty: 2, price: "4.620.000", discount: "50%", bigsale: "2.310.000", status: "HÀNG MỚI" }, { stt: 3, code: "MH-90SMC", name: "MÁY HÚT ECO", category: "MÁY HÚT", qty: 4, price: "5.060.000", discount: "50%", bigsale: "2.530.000", status: "HÀNG MỚI" }, { stt: 4, code: "MH-600GT", name: "MÁY HÚT", category: "MÁY HÚT", qty: 5, price: "8.208.000", discount: "40%", bigsale: "4.924.800", status: "HÀNG MỚI" }, { stt: 5, code: "AS-9602DG", name: "BẾP 2 GAS", category: "BẾP GAS", qty: 2, price: "7.815.000", discount: "40%", bigsale: "4.689.000", status: "HÀNG MỚI" }, { stt: 6, code: "SMART K-450", name: "CHẬU ĐÁ", category: "CHẬU ĐÁ", qty: 1, price: "21.109.000", discount: "45%", bigsale: "11.609.950", status: "HÀNG MỚI" }, { stt: 7, code: "MS-1022D", name: "CHẬU RỰA", category: "CHẬU RỰA", qty: 4, price: "5.852.000", discount: "40%", bigsale: "3.511.200", status: "HÀNG MỚI" }, { stt: 8, code: "MS-2025", name: "CHẬU RỰA", category: "CHẬU RỰA", qty: 1, price: "5.700.000", discount: "45%", bigsale: "3.135.000", status: "HÀNG MỚI" }, { stt: 9, code: "MS-6070", name: "CHẬU RỰA", category: "CHẬU RỰA", qty: 11, price: "6.464.000", discount: "40%", bigsale: "3.878.400", status: "HÀNG MỚI" }, { stt: 10, code: "MS-5066W", name: "CHẬU RỰA", category: "CHẬU RỰA", qty: 8, price: "4.385.000", discount: "40%", bigsale: "2.631.000", status: "HÀNG MỚI" }, { stt: 11, code: "MS-5082W", name: "CHẬU RỰA", category: "CHẬU RỰA", qty: 3, price: "6.350.000", discount: "40%", bigsale: "3.810.000", status: "HÀNG MỚI" }, { stt: 12, code: "MS-862", name: "CHẬU RỰA", category: "CHẬU RỰA", qty: 3, price: "12.960.000", discount: "45%", bigsale: "7.128.000", status: "HÀNG MỚI" }, { stt: 13, code: "MPC-5KCB", name: "BỘ LỌC NƯỚC", category: "LỌC NƯỚC", qty: 1, price: "10.702.000", discount: "40%", bigsale: "6.421.200", status: "HÀNG MỚI" }, { stt: 14, code: "K061S", name: "VÒI", category: "VÒI", qty: 10, price: "3.056.000", discount: "40%", bigsale: "1.833.600", status: "HÀNG MỚI" }, { stt: 15, code: "K569SN", name: "VÒI ECO", category: "VÒI", qty: 2, price: "3.520.000", discount: "45%", bigsale: "1.936.000", status: "HÀNG MỚI" }, { stt: 16, code: "K525B", name: "VÒI", category: "VÒI", qty: 2, price: "2.916.000", discount: "45%", bigsale: "1.603.800", status: "HÀNG MỚI" }, { stt: 17, code: "K130S", name: "VÒI", category: "VÒI", qty: 4, price: "4.104.000", discount: "45%", bigsale: "2.257.200", status: "HÀNG MỚI" }, { stt: 18, code: "K501B", name: "VÒI", category: "VÒI", qty: 2, price: "5.076.000", discount: "45%", bigsale: "2.791.800", status: "HÀNG MỚI" }, { stt: 19, code: "MMPM-657A", name: "XAY TIÊU", category: "PHỤ KIỆN", qty: 34, price: "295.000", discount: "60%", bigsale: "118.000", status: "HÀNG MỚI" } ].map(function(p, i){ return Object.assign({}, p, { stt: i + 1 }); }); // Runtime list (starts from DEFAULT, replaced by /api/promos when available). let BIG_SALE_PRODUCTS = DEFAULT_BIG_SALE_PRODUCTS.slice().map(function(p, i){ return Object.assign({}, p, { stt: i + 1 }); }); // Expose BIG SALE data + opener so the self-hosted deep-link handler below and // any other module can open a BIG SALE product by code. window.BIG_SALE_PRODUCTS = BIG_SALE_PRODUCTS; // Format VND currency function formatVND(price) { const num = parseInt(price.replace(/\./g, '')); if (isNaN(num)) return price; return new Intl.NumberFormat('vi-VN', { style: 'currency', currency: 'VND' }).format(num); } // Plain digit-group format WITHOUT currency symbol (for BIGSALE price fields). function fmtNum(n) { const num = Number(n || 0); if (!num) return '0'; return num.toLocaleString('vi-VN'); } // Build a product card HTML string function buildProductCard(p) { const discountClass = p.discount.includes('7') ? 'discount-high' : 'discount-mid'; // Try to get full product name from catalog var fullName = p.name; var imgSrc = p.image || ''; try { if (window.vaix && typeof window.vaix.findProduct === 'function') { var matched = window.vaix.findProduct(String(p.code || '')); if (matched && (matched.title_clean || matched.name)) fullName = matched.title_clean || matched.name; if (!imgSrc) imgSrc = matched && (matched.image || (matched.images && matched.images[0])) ? (matched.image || matched.images[0]) : ''; } } catch(e) {} return `
S${p.stt} -${p.discount}
${imgSrc ? '
'+p.code+'
' : ''}
${fullName}
${p.code}
SL: ${p.qty} HÀNG MỚI
${formatVND(p.price)} ${formatVND(p.bigsale)}
`; } // Create the floating button + panel function createBigSaleElements() { // Check if already exists if (document.getElementById('big-sale-floating')) return; const floating = document.createElement('button'); floating.id = 'big-sale-floating'; floating.setAttribute('aria-label', 'Chương trình BIG SALE'); floating.setAttribute('title', 'Chương trình BIG SALE'); floating.innerHTML = `
BIG SALE ${BIG_SALE_PRODUCTS.length} SP
`; const panel = document.createElement('div'); panel.id = 'big-sale-panel'; panel.setAttribute('role', 'dialog'); panel.setAttribute('aria-label', 'Chương trình khuyến mãi BIG SALE'); panel.innerHTML = `
🔥 CHƯƠNG TRÌNH BIG SALE
${BIG_SALE_PRODUCTS.length} sản phẩm
${BIG_SALE_PRODUCTS.map(p => buildProductCard(p)).join('')}
`; // Inject inline style for BIGSALE positioning var es = document.getElementById('bigsale-pos-style'); if (!es) { es = document.createElement('style'); es.id = 'bigsale-pos-style'; es.textContent = '#big-sale-floating{position:fixed!important;right:18px!important;bottom:100px!important;z-index:9999!important;}#big-sale-panel{display:none;}#big-sale-panel.open{display:flex!important;}'; document.head.appendChild(es); } document.body.appendChild(floating); document.body.appendChild(panel); attachBigSaleEventListeners(floating, panel); } // Event listeners function attachBigSaleEventListeners(floating, panel) { let isOpen = false; floating.addEventListener('click', function(e) { e.stopPropagation(); isOpen = !isOpen; panel.classList.toggle('open', isOpen); floating.classList.toggle('open', isOpen); }); // Close when clicking outside document.addEventListener('click', function(e) { if (isOpen && !panel.contains(e.target) && !floating.contains(e.target)) { panel.classList.remove('open'); floating.classList.remove('open'); isOpen = false; } }); // Product click → open detail modal (event delegation, survives panel refresh) panel.addEventListener('click', function(e) { var card = e.target.closest('.big-sale-product'); if (!card) return; e.stopPropagation(); var code = card.getAttribute('data-code'); if (!code) return; var norm = function (s) { return String(s || '').trim().toUpperCase().replace(/\s+/g, ' '); }; var target = norm(code); var product = BIG_SALE_PRODUCTS.find(function(p) { return norm(p.code) === target; }); if (!product && window.BIG_SALE_PRODUCTS) { product = window.BIG_SALE_PRODUCTS.find(function(p) { return norm(p.code) === target; }); } if (product) { openBigSaleDetail(product); } }); // "Xem tất cả" button const viewAllBtn = panel.querySelector('.big-sale-view-all-btn'); if (viewAllBtn) { viewAllBtn.addEventListener('click', function(e) { e.stopPropagation(); closeBigSalePanel(); if (window.vaistudioToggle && typeof window.vaistudioToggle === 'function') { window.vaistudioToggle(); } else { const toggle = document.getElementById('vaistudio-toggle'); if (toggle) toggle.click(); } }); } } // Open vaistudio detail modal with BIG SALE product info function openBigSaleDetail(product) { const overlay = document.getElementById('vaistudio-detail-overlay'); const modal = document.getElementById('vaistudio-detail-modal'); if (!overlay || !modal) return; // ── BIG SALE → Malloca product match ── // If this BIG SALE code exists in the live catalog, reuse the real product's // gallery/specs/subtitle but keep the BIG SALE price. Falls back to the // standalone BIG SALE card when there is no match. let matched = null; try { if (window.vaix && typeof window.vaix.findProduct === 'function' && product && product.code) matched = window.vaix.findProduct(String(product.code)); } catch (e) { matched = null; } // Set product title and code const titleEl = document.getElementById('detail-title'); const modelEl = document.getElementById('detail-model'); const brandEl = document.getElementById('detail-brand'); const priceEl = document.getElementById('detail-price'); if (titleEl) titleEl.textContent = matched ? (matched.title_clean || matched.name || product.name) : product.name; if (modelEl) modelEl.textContent = `Mã: ${product.code}`; if (brandEl) { brandEl.textContent = matched ? (matched.brand || product.category) : product.category; brandEl.style.background = '#fff1f2'; brandEl.style.color = '#e11d48'; } if (priceEl) { priceEl.innerHTML = `
${formatVND(product.bigsale)} ${formatVND(product.price)} -${product.discount}
`; } // Set images: full gallery via renderDetailImages (scrollable + zoom) const imagesEl = document.getElementById('detail-images'); if (imagesEl) { const gal = (matched && (matched.images && matched.images.length ? matched.images : (matched.image ? [matched.image] : []))) || (product.image ? [product.image] : []); const synthetic = { image: gal[0] || '', images: gal, slug: (matched&&matched.slug)||'', title_clean: (matched&&matched.title_clean)||product.name }; if (typeof renderDetailImages === 'function') { renderDetailImages(synthetic); if (window.vaix && typeof window.vaix.ensureGallery === 'function') { setTimeout(function(){ try { window.vaix.ensureGallery().then(function(){ renderDetailImages(synthetic); }); } catch(e){} }, 200); } } else if (gal.length) { imagesEl.innerHTML = gal.map(function(u){ return ''+product.code+''; }).join(''); } else { imagesEl.innerHTML = '
'+(getCategoryEmoji(product.category))+'
'; } } // Show BIG SALE pricing info in summary const summaryEl = document.getElementById('detail-summary'); if (summaryEl) { summaryEl.style.display = 'block'; summaryEl.innerHTML = `
🔥 Giá BigSale: ${formatVND(product.bigsale)}
💰 Giá bán lẻ: ${formatVND(product.price)} → ${formatVND(product.bigsale)}
🏷️ Chiết khấu: -${product.discount}
📦 Tình trạng: ${product.status}
💡 Danh mục: ${product.category}
`; } // Hide features and specs sections const featuresEl = document.getElementById('detail-features'); const specsEl = document.getElementById('detail-specs'); if (featuresEl) featuresEl.style.display = 'none'; if (specsEl) specsEl.style.display = 'none'; // ── Action buttons: Chia sẻ / Hỏi AI / Thêm giỏ hàng / Báo giá ── // BIG SALE products open through this modal but, unlike catalog products // (renderDetail in vaix-rag.js), they were NOT being given the share / // ask-AI / add-to-cart action row. Wire it here so every BIG SALE product // gets the same actionable buttons as catalog products. try { setupBigSaleDetailButtons(product, matched); } catch (e) { console.warn('[BIG SALE] detail buttons:', e); } // CRITICAL: keep the BIG SALE price as THE detail price. The catalog modal's // reapplyCk() re-renders #detail-price from window.lastShownProduct when the // customer CK changes — if lastShownProduct were the matched catalog product // (no promo flag), reapplyCk would overwrite the BIG SALE price with a CK // price. Point it at the synthetic BIG SALE product (promo:'bigsale', // noCk:true) so _ckPrice() returns null and the discount price survives. window.lastShownProduct = bigSaleAsProduct(product, matched); window.currentContextProduct = window.lastShownProduct; overlay.style.display = 'flex'; // Close vaistudio panel if open const vaistudioPanel = document.getElementById('vaistudio-panel'); if (vaistudioPanel && vaistudioPanel.classList.contains('open')) { vaistudioPanel.classList.remove('open'); const toggle = document.getElementById('vaistudio-toggle'); if (toggle) toggle.classList.remove('open'); } } // Build a synthetic product object for the BIG SALE card so we can reuse the // catalog modal's own button helpers (share / ask-AI / add-to-cart / quote). function bigSaleAsProduct(product, matched) { const priceNum = parseInt(String(product.bigsale || '').replace(/\./g, ''), 10) || 0; const code = product.code || ''; const slug = (matched && (matched.slug || matched.sku)) || code; return { slug: slug, sku: matched && (matched.sku || matched.model) ? (matched.sku || matched.model) : code, model: (matched && matched.model) || '', title_clean: (matched && (matched.title_clean || matched.name)) || product.name || code, name: (matched && (matched.title_clean || matched.name)) || product.name || code, brand: (matched && matched.brand) || product.category || '', priceNum: priceNum, price: product.bigsale || '', image: (matched && (matched.image || (matched.images && matched.images[0]))) || '', summary: (matched && (matched.summary || matched.description)) || ('🔥 Giá BIG SALE: ' + product.bigsale + ' (giảm ' + product.discount + ')'), features: [], specs: {}, noCk: true, promo: 'bigsale' }; } // Build the self-hosted BIG SALE share link for a given BIG SALE product. // Points to the CURRENT page/origin with ?bigsale= so it opens the BIG // SALE detail modal at the discounted price (NOT the catalog listed price and // NOT the unrelated production space). function bigSaleShareUrl(product) { var base = window.location.origin + window.location.pathname; return base + '?bigsale=' + encodeURIComponent((product && product.code) || ''); } // Per-card "Chia sẻ link" (SEO share). Copies the self-hosted SEO link to the // clipboard and shows a toast. Exposed on window because the list cards are // built as HTML strings with inline onclick. window.shareBigSale = function (code) { try { var url = bigSaleShareUrl({ code: code }); var copy = function (u) { if (window.navigator && navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(u).then(function(){ if (typeof window.toast === 'function') window.toast('✓ Đã copy link chia sẻ BIG SALE'); else alert('Link chia sẻ BIG SALE (SEO):\n' + u); }).catch(function(){ window.prompt('Link chia sẻ BIG SALE:', u); }); } else { window.prompt('Link chia sẻ BIG SALE:', u); } }; copy(url); } catch (err) { alert('Lỗi chia sẻ: ' + (err && err.message || err)); } }; // Create/re-ensure the share + add-to-cart + quote buttons in the BIG SALE // detail modal. These mirror the catalog modal's detail-cart-section / // detail-share-section so behaviour (addToCart / openQuotation / shareProduct / // openAskAIForProduct) is identical for BIG SALE products. function setupBigSaleDetailButtons(product, matched) { const p = bigSaleAsProduct(product, matched); // Share button let shareSection = document.getElementById('detail-share-section'); if (!shareSection) { shareSection = document.createElement('div'); shareSection.id = 'detail-share-section'; shareSection.style.cssText = 'padding:10px 16px 4px;display:block'; shareSection.innerHTML = ''; const specsEl = document.getElementById('detail-specs'); if (specsEl && specsEl.nextSibling) specsEl.parentNode.insertBefore(shareSection, specsEl.nextSibling); else document.getElementById('vaistudio-detail-modal').appendChild(shareSection); } // Cart + Quote buttons let cartSection = document.getElementById('detail-cart-section'); if (!cartSection) { cartSection = document.createElement('div'); cartSection.id = 'detail-cart-section'; cartSection.style.cssText = 'padding:8px 16px 2px;display:flex;gap:8px;flex-wrap:wrap'; cartSection.innerHTML = '' + ''; const specsEl2 = document.getElementById('detail-specs'); if (specsEl2 && specsEl2.nextSibling) specsEl2.parentNode.insertBefore(cartSection, specsEl2.nextSibling); else document.getElementById('vaistudio-detail-modal').appendChild(cartSection); } // Wire handlers const shareBtn = document.getElementById('detail-share-btn'); if (shareBtn) { shareBtn.onclick = function (e) { e.stopPropagation(); try { // Build the share text + self-hosted BIG SALE link ourselves (not the // catalog's getShareLink which would point to the listed-price deep link). var url = bigSaleShareUrl(product); var shareText = '🛒 ' + p.title_clean + '\n💰 ' + (p.priceNum > 0 ? p.priceNum.toLocaleString('vi-VN') + '₫' : 'Liên hệ'); if (p.brand) shareText += '\n🏷️ ' + p.brand; shareText += '\n🔥 Giá BIG SALE: ' + (product.bigsale || '') + ' (-' + (product.discount || '') + ')'; shareText += '\n\n' + url; if (navigator.share) { navigator.share({ title: p.title_clean, text: shareText, url: url }).catch(function () {}); } else { navigator.clipboard.writeText(shareText).then(function () { (window.showShareToast || function () {})( 'Đã sao chép link chia sẻ: ' + p.title_clean); }).catch(function () { prompt('Sao chép link chia sẻ:', url); }); } } catch (e) { console.warn('[BIG SALE] share:', e); } }; } const addCartBtn = document.getElementById('detail-addcart-btn'); if (addCartBtn) { addCartBtn.onclick = function (e) { e.stopPropagation(); const prodObj = { slug: p.slug || '', sku: p.sku || '', name: p.title_clean || p.name || '', brand: p.brand || '', price: p.priceNum, priceNum: p.priceNum, image: p.image || '', noCk: true, promo: 'bigsale' }; if (window.addToCart) window.addToCart(prodObj); addCartBtn.innerHTML = ' Đã thêm!'; addCartBtn.style.background = '#166534'; const self = this; setTimeout(function () { self.innerHTML = ' Thêm giỏ hàng'; self.style.background = '#25d366'; }, 1500); }; } const quoteBtn = document.getElementById('detail-quote-btn'); if (quoteBtn) { quoteBtn.onclick = function (e) { e.stopPropagation(); if (window.openQuotation) window.openQuotation(); }; } // Hỏi AI — ask the avatar about THIS product (mirrors catalog openAskAIForProduct). let askSection = document.getElementById('detail-asksale-section'); if (!askSection) { askSection = document.createElement('div'); askSection.id = 'detail-asksale-section'; askSection.style.cssText = 'padding:2px 16px 10px;display:block'; askSection.innerHTML = ''; const cartSectionEl = document.getElementById('detail-cart-section'); if (cartSectionEl && cartSectionEl.nextSibling) askSection = cartSectionEl.parentNode.insertBefore(askSection, cartSectionEl.nextSibling); else document.getElementById('vaistudio-detail-modal').appendChild(askSection); } const askBtn = document.getElementById('detail-asksale-btn'); if (askBtn) { askBtn.onclick = function (e) { e.stopPropagation(); if (window.vaix && window.vaix.openAskAIForProduct) { window.vaix.openAskAIForProduct(p); } else { const q = 'Cho em hỏi chi tiết về sản phẩm ' + p.title_clean + '. Cho em biết giá, đặc điểm nổi bật và ưu đãi BIG SALE nhé.'; if (window.sendAsUserQuestion) window.sendAsUserQuestion(q); else if (window._triggerSuggestionSend) window._triggerSuggestionSend(q); } }; } } // Get emoji for category function getCategoryEmoji(category) { const map = { "MÁY HÚT": "🧹", "BẾP GAS": "🔥", "CHẬU ĐÁ": "🧊", "CHẬU RỰA": "🚰", "LỌC NƯỚC": "💧", "VÒI": "🚿", "PHỤ KIỆN": "🧰" }; return map[category] || "📦"; } // Close BIG SALE panel function closeBigSalePanel() { const panel = document.getElementById('big-sale-panel'); const floating = document.getElementById('big-sale-floating'); if (panel) panel.classList.remove('open'); if (floating) floating.classList.remove('open'); } // ── Self-hosted BIG SALE deep link (?bigsale=) ── // When a shared BIG SALE link is opened, open the BIG SALE detail modal at the // discounted price. Waits for the detail modal DOM + this module to be ready, // then opens the product. Uses ?bigsale= (not ?product=) so it never collides // with the catalog deep-link handler in greeting-news.js/vaix-rag.js. function dismissWelcomeForBigSale() { try { var wm = document.getElementById('welcome-modal'); if (wm) { wm.classList.remove('show'); wm.style.display = 'none'; } var ids = ['topbar','controls','subtitles','vaistudio-toggle','text-chat']; for (var i = 0; i < ids.length; i++) { var el = document.getElementById(ids[i]); if (el) { el.style.display = ''; el.hidden = false; } } var ld = document.getElementById('loading'); if (ld) ld.classList.add('done'); var av = document.getElementById('avatar-picker-modal'); if (av) { av.classList.remove('show'); av.style.display = 'none'; } try { var panel = document.getElementById('vaistudio-panel'); var toggle = document.getElementById('vaistudio-toggle'); if (panel) { panel.classList.add('open'); panel.style.display = 'flex'; } if (toggle) toggle.classList.add('active'); } catch (_e) {} } catch (e) { /* never block */ } } function handleBigSaleUrlParam() { try { var params = new URLSearchParams(location.search); var code = params.get('bigsale') || ''; if (!code) return; // ?bigsale=all → open the FULL list (not a single product). var codeNorm = String(code).trim().toUpperCase().replace(/\s+/g, ' '); if (codeNorm === 'ALL' || codeNorm === 'DANHSACH' || codeNorm === 'LIST') { var triesAll = 0; var ivAll = setInterval(function () { triesAll++; try { if (document.getElementById('big-sale-floating')) { if (!document.getElementById('big-sale-panel')) initBigSale(); dismissWelcomeForBigSale(); openBigSalePanel(); clearInterval(ivAll); return; } } catch (e) {} if (triesAll > 40) clearInterval(ivAll); }, 500); return; } var tries = 0; var iv = setInterval(function () { tries++; try { var modal = document.getElementById('vaistudio-detail-modal'); // Normalize the code (uppercase, collapse spaces) for matching. var norm = function (s) { return String(s || '').trim().toUpperCase().replace(/\s+/g, ' '); }; var target = norm(code); var product = BIG_SALE_PRODUCTS.find(function (p) { return norm(p.code) === target; }); if (!product && window.BIG_SALE_PRODUCTS) { product = window.BIG_SALE_PRODUCTS.find(function (p) { return norm(p.code) === target; }); } if (modal && product && typeof openBigSaleDetail === 'function') { // Make sure the floating panel/module is initialized (creates panel). if (!document.getElementById('big-sale-floating')) initBigSale(); dismissWelcomeForBigSale(); openBigSaleDetail(product); try { if (window.vaix && typeof window.vaix.openAskAIForProduct === 'function') { const matchedBS = (window.vaix && typeof window.vaix.findProduct === 'function') ? window.vaix.findProduct(String(code)) : null; const p = (typeof bigSaleAsProduct === 'function') ? bigSaleAsProduct(product, matchedBS) : null; if (p) window.vaix.openAskAIForProduct(p); } } catch(e) {} clearInterval(iv); return; } } catch (e) { if (tries > 40) clearInterval(iv); } if (tries > 60) clearInterval(iv); // give up after ~30s (catalog may be slow) }, 500); } catch (e) { /* never block */ } } // Ensure the BIGSALE floating button shows even on discount-link pages (?kh=...) function ensureBigSaleButton() { var existing = document.getElementById('big-sale-floating'); if (existing) { existing.style.display = ''; var panel = document.getElementById('big-sale-panel'); if (panel && panel.classList.contains('open')) { panel.style.display = ''; } return; } if (typeof initBigSale === 'function') { try { initBigSale(); } catch(e) {} return; } var _retry = 0; var _iv = setInterval(function() { _retry++; if (typeof initBigSale === 'function') { clearInterval(_iv); try { initBigSale(); } catch(e) {} } else if (_retry > 30) { clearInterval(_iv); } }, 300); } window.ensureBigSaleButton = ensureBigSaleButton; // Initialize when DOM is ready // ── Dynamic BIG SALE data (from Promo CMS /api/promos) ── // Replaces the runtime list with admin-managed BIGSALE products. Falls back to // DEFAULT_BIG_SALE_PRODUCTS when the API is unavailable. Emits an event so the // panel can re-render without a page reload. function loadBigSaleProducts(force) { if (force !== true && _bigsaleLoaded) return Promise.resolve(BIG_SALE_PRODUCTS); return fetch('/api/promos', { method: 'GET', cache: 'no-store' }) .then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(function (d) { var list = (d && Array.isArray(d.bigsale)) ? d.bigsale : null; if (list && list.length) { BIG_SALE_PRODUCTS = list.slice().map(function (p, i) { return Object.assign({}, p, { stt: i + 1 }); }); } else if (list && list.length === 0 && force === true) { // Admin explicitly emptied the BIGSALE list — respect it. BIG_SALE_PRODUCTS = []; } // Auto-merge catalog products whose discount is >= 40% (BIGSALE tier). // Tagged _auto:true so they are NOT deletable via the promo API. try { var cat = (window.vaix && typeof window.vaix.allProducts === 'function') ? window.vaix.allProducts() : []; var normCode = function (s) { return String(s || '').trim().toUpperCase().replace(/\s+/g, ' '); }; var have = {}; for (var h = 0; h < BIG_SALE_PRODUCTS.length; h++) { var hk = normCode(BIG_SALE_PRODUCTS[h].code) || normCode(BIG_SALE_PRODUCTS[h].name); if (hk) have[hk] = 1; } var added = 0; for (var ci = 0; ci < cat.length && added < 30; ci++) { var p2 = cat[ci]; if (p2._deleted || p2.deleted) continue; var ln = Number(p2.listPn) || (p2.listPrice ? parseInt(String(p2.listPrice).replace(/[^\d]/g, ''), 10) : 0) || 0; var sn = Number(p2.salePn) || (p2.salePrice ? parseInt(String(p2.salePrice).replace(/[^\d]/g, ''), 10) : 0) || Number(p2.priceNum) || 0; if (!ln || !sn || ln <= sn) continue; var disc = Math.round((ln - sn) / ln * 100); if (disc < 40) continue; var ck = normCode(p2.sku || p2.model || p2.code || ''); if (!ck) ck = normCode(p2.title_clean || p2.name || ''); if (!ck || have[ck]) continue; have[ck] = 1; BIG_SALE_PRODUCTS.push({ stt: BIG_SALE_PRODUCTS.length + 1, code: p2.sku || p2.model || p2.code || ck, name: (p2.title_clean || p2.name || '').toUpperCase(), category: p2.category || p2.category_slug || '', price: fmtNum(ln), bigsale: fmtNum(sn), discount: disc + '%', status: 'AUTO', image: p2.image || (p2.images && p2.images[0]) || '', _auto: true }); added++; } } catch (e) {} window.BIG_SALE_PRODUCTS = BIG_SALE_PRODUCTS; _bigsaleLoaded = true; refreshBigSalePanel(); return BIG_SALE_PRODUCTS; }) .catch(function () { return BIG_SALE_PRODUCTS; }); } let _bigsaleLoaded = false; // Re-render the floating panel in place (preserve open state). function refreshBigSalePanel() { try { var panel = document.getElementById('big-sale-panel'); var floating = document.getElementById('big-sale-floating'); if (!panel || !floating) return; var wasOpen = panel.classList.contains('open'); var listEl = panel.querySelector('.big-sale-product-list'); var countEl = panel.querySelector('.big-sale-product-count'); var countBadge = floating.querySelector('.big-sale-count'); if (listEl) listEl.innerHTML = BIG_SALE_PRODUCTS.map(function (p) { return buildProductCard(p); }).join(''); if (countEl) countEl.textContent = BIG_SALE_PRODUCTS.length + ' sản phẩm'; if (countBadge) countBadge.textContent = BIG_SALE_PRODUCTS.length + ' SP'; if (wasOpen) panel.classList.add('open'); else panel.classList.remove('open'); injectBigSaleAdminActions(); } catch (e) {} } // ── List-level share (toàn bộ danh sách BIG SALE) ── // Copies a self-hosted deep link (?bigsale=all) that opens the FULL BIG SALE // panel — NOT a single product. Replaces the old per-product share. window.shareBigSaleList = function () { var base = window.location.origin + window.location.pathname; var url = base + '?bigsale=all'; var done = function () { if (typeof window.toast === 'function') window.toast('✓ Đã copy link chia sẻ danh sách BIG SALE'); else alert('Link chia sẻ danh sách BIG SALE:\n' + url); }; if (window.navigator && navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(url).then(done).catch(function () { window.prompt('Link chia sẻ danh sách BIG SALE:', url); }); } else { window.prompt('Link chia sẻ danh sách BIG SALE:', url); } }; // Open the full BIG SALE panel (used by ?bigsale=all deep link). function openBigSalePanel() { try { var panel = document.getElementById('big-sale-panel'); var floating = document.getElementById('big-sale-floating'); if (panel) panel.classList.add('open'); if (floating) floating.classList.add('open'); } catch (e) {} } window.openBigSalePanel = openBigSalePanel; // Admin-only delete for a BIG SALE item (permanent, via /api/delete-promo). async function deleteBigSaleProduct(code) { try { var isAdmin = (typeof window.isVaAdmin === 'function') ? window.isVaAdmin() : false; if (!isAdmin) { alert('🔒 Cần nhập mã V.AISTUDIO trên trang chủ.'); return; } if (!window.confirm('Xóa sản phẩm ' + code + ' khỏi danh sách BIG SALE?')) return; var r = await fetch('/api/delete-promo', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-vai-admin': '1' }, body: JSON.stringify({ type: 'bigsale', key: code }) }); var d = await r.json().catch(function () { return {}; }); if (!r.ok) { alert('❌ ' + (d.error || 'Lỗi xóa')); return; } if (window.toast) window.toast('✓ Đã xóa khỏi BIG SALE'); loadBigSaleProducts(true); } catch (e) { alert('❌ Lỗi xóa: ' + (e && e.message || e)); } } window.deleteBigSaleProduct = deleteBigSaleProduct; // Inject admin delete buttons into every BIG SALE card (after each render). function injectBigSaleAdminActions() { try { var isAdmin = (typeof window.isVaAdmin === 'function') ? window.isVaAdmin() : false; if (!isAdmin) return; var panel = document.getElementById('big-sale-panel'); if (!panel) return; var norm = function (s) { return String(s || '').trim().toUpperCase().replace(/\s+/g, ' '); }; var cards = panel.querySelectorAll('.big-sale-product'); for (var i = 0; i < cards.length; i++) { var card = cards[i]; var code = card.getAttribute('data-code') || ''; if (!code) continue; // Auto-merged catalog items are NOT admin-managed → no delete button. var isAuto = false; for (var k = 0; k < BIG_SALE_PRODUCTS.length; k++) { if (norm(BIG_SALE_PRODUCTS[k].code) === norm(code)) { isAuto = !!BIG_SALE_PRODUCTS[k]._auto; break; } } if (isAuto) continue; var actions = card.querySelector('.big-sale-card-actions'); if (!actions || actions.querySelector('.bs-del-btn')) continue; var del = document.createElement('button'); del.type = 'button'; del.className = 'bs-del-btn'; del.innerHTML = ' Xóa'; del.style.cssText = "flex:1;padding:6px 8px;border:none;border-radius:8px;background:#dc2626;color:#fff;font-size:0.7rem;font-weight:700;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;justify-content:center;gap:5px"; del.addEventListener('click', function (e) { e.stopPropagation(); window.deleteBigSaleProduct(code); }); actions.appendChild(del); } } catch (e) {} } function initBigSale() { createBigSaleElements(); console.log('[BIG SALE] Floating promotion initialized with ' + BIG_SALE_PRODUCTS.length + ' products'); // Load admin-managed BIGSALE products from the Promo CMS backend. loadBigSaleProducts().then(function (list) { console.log('[BIG SALE] Dynamic list loaded: ' + list.length + ' products'); injectBigSaleAdminActions(); handleBigSaleUrlParam(); }).catch(function () { injectBigSaleAdminActions(); handleBigSaleUrlParam(); }); // Listen for CMS saves so the panel stays in sync. try { window.addEventListener('vai-promo-changed', function () { loadBigSaleProducts(true).then(function () { injectBigSaleAdminActions(); }); }); } catch (e) {} } // Handle dynamic DOM changes function tryInitBigSale(retries = 20) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => initBigSale()); } else { initBigSale(); } } tryInitBigSale();