// V.AI STUDIO FLASHSALE floating panel v1.0 // Nút FLASHSALE đặt NGAY CẠNH nút COMBO (bottom:240px, combo ở 170px). // Danh sách FLASHSALE = mọi sản phẩm khuyến mãi CÓ ĐỦ 2 GIÁ: // - Từ catalog (vaix.allProducts): listPn (niêm yết) + salePn (khuyến mãi), listPn > salePn // - Từ BIG SALE (window.BIG_SALE_PRODUCTS): price (niêm yết) + bigsale (giá KM) // Chia sẻ cả danh sách qua ?flashsale=all (self-hosted, mở đúng panel này). (function () { if (window.__vaixFlashsaleLoaded) return; window.__vaixFlashsaleLoaded = true; console.log("[FLASHSALE] Module loaded"); function fmt(n) { n = Number(n) || 0; return n.toLocaleString("vi-VN") + " ₫"; } function catalogProductsRaw() { try { if (window.vaix && typeof window.vaix.allProducts === "function") { var arr = window.vaix.allProducts(); return Array.isArray(arr) ? arr : []; } } catch (e) {} return []; } // Catalog products that have BOTH listPn and salePn, list > sale. function catalogBuild() { var out = []; var arr = catalogProductsRaw(); for (var i = 0; i < arr.length; i++) { var p = arr[i]; var listN = Number(p.listPn) || (p.listPrice ? parseInt(String(p.listPrice).replace(/[^\d]/g, ""), 10) : 0) || 0; var saleN = Number(p.salePn) || (p.salePrice ? parseInt(String(p.salePrice).replace(/[^\d]/g, ""), 10) : 0) || Number(p.priceNum) || 0; if (!listN || !saleN || listN <= saleN) continue; if (p._deleted || p.deleted) continue; var disc = Math.round((listN - saleN) / listN * 100); // BIGSALE takes discount >= 40%; FLASHSALE keeps only < 40%. if (disc >= 40) continue; out.push({ name: p.title_clean || p.name || p.title || "", code: p.sku || p.model || p.code || "", image: p.image || (p.images && p.images[0]) || "", listPn: listN, salePn: saleN, discountNum: disc, discount: disc + "%", slug: p.slug || "", catalog: true, _full: p }); } return out; } // FLASHSALE = products having BOTH list price and sale price (list > sale). function buildFlashProducts() { var out = []; var seen = {}; function push(p) { if (!p) return; var key = String(p.code || p.sku || p.model || p.name || ""); if (!key) return; key = key.toUpperCase().replace(/\s+/g, " "); if (seen[key]) return; seen[key] = 1; out.push(p); } // 1) Catalog products with both prices. var cat = catalogBuild(); for (var i = 0; i < cat.length; i++) push(cat[i]); // 2) BIG SALE products (they always have list price + bigsale discount price). try { var bs = (window.BIG_SALE_PRODUCTS || []); for (var j = 0; j < bs.length; j++) { var b = bs[j]; var listN = parseInt(String(b.price || "0").replace(/\./g, ""), 10) || 0; var saleN = parseInt(String(b.bigsale || "0").replace(/\./g, ""), 10) || 0; if (listN <= 0 || saleN <= 0 || listN <= saleN) continue; var bdisc = Math.round((listN - saleN) / listN * 100); // BIGSALE items are discount >= 40% → not FLASHSALE. if (bdisc >= 40) continue; push({ name: b.name, code: b.code, image: b.image || "", listPn: listN, salePn: saleN, discountNum: bdisc, discount: bdisc + "%", _fromBigSale: true }); } } catch (e) {} return out; } function createElements() { if (document.getElementById("flashsale-floating")) return; var btn = document.createElement("button"); btn.id = "flashsale-floating"; btn.style.cssText = "position:fixed;right:18px;bottom:240px;z-index:9999;border:none;cursor:pointer;display:flex;align-items:center;gap:6px;background:#fff;border-radius:30px;padding:8px 14px;box-shadow:0 4px 14px rgba(0,0,0,0.15);font-family:inherit;font-size:0.75rem;font-weight:700;color:#c2410c;border:1px solid #fed7aa;transition:all 0.25s"; btn.innerHTML = 'FLASHSALE 0'; var panel = document.createElement("div"); panel.id = "flashsale-panel"; panel.style.cssText = "position:fixed;right:18px;bottom:240px;z-index:9998;width:320px;max-height:460px;background:#fff;border-radius:16px;box-shadow:0 16px 48px rgba(0,0,0,0.2);overflow:hidden;display:none;flex-direction:column;font-family:inherit;border:1px solid #fed7aa"; panel.innerHTML = '
⚡ FLASHSALE 2 GIÁ
'; document.body.appendChild(btn); document.body.appendChild(panel); var isOpen = false; btn.addEventListener("click", function (e) { e.stopPropagation(); isOpen = !isOpen; panel.style.display = isOpen ? "flex" : "none"; if (isOpen) refreshPanel(); }); var closeBtn = panel.querySelector(".flashsale-close"); if (closeBtn) closeBtn.onclick = function () { panel.style.display = "none"; isOpen = false; }; document.addEventListener("click", function (e) { if (isOpen && !panel.contains(e.target) && !btn.contains(e.target)) { panel.style.display = "none"; isOpen = false; } }); panel.addEventListener("click", function (e) { var card = e.target && e.target.closest ? e.target.closest(".fs-card") : null; if (!card) return; e.stopPropagation(); var code = card.getAttribute("data-code") || ""; if (!code) return; var p = buildFlashProducts().find(function (x) { return String(x.code || "").trim().toUpperCase() === code.toUpperCase(); }); if (p) openDetail(p); }); } function openDetail(p) { // Prefer the shared catalog detail renderer (full gallery, description, // specs, features — identical to the "danh sách SP chính" modal) when the // flashsale item came from the catalog (carries the full product in _full). try { if (p && p._full && typeof window.vaix === 'object' && window.vaix && typeof window.vaix.renderDetail === 'function') { var _full = p._full; // Honor listPn/salePn from the flashsale build in case price fields on // the record differ, so the detail keeps the 2-giá price row. var _p = Object.assign({}, _full); if (_p.listPn === undefined || _p.salePn === undefined) { _p.listPn = p.listPn; _p.salePn = p.salePn; _p.priceMode = 'both'; } _p._flashsaleShow = true; window.vaix.renderDetail(_p); var ov = document.getElementById("vaistudio-detail-overlay"); if (ov) { ov.classList.remove('show'); ov.style.display = 'flex'; } if (window.VAI_modalTop && ov) window.VAI_modalTop(ov); return; } } catch (e) {} var overlay = document.getElementById("vaistudio-detail-overlay"); var modal = document.getElementById("vaistudio-detail-modal"); if (!overlay || !modal) return; var titleEl = document.getElementById("detail-title"); var modelEl = document.getElementById("detail-model"); var priceEl = document.getElementById("detail-price"); var imagesEl = document.getElementById("detail-images"); try { if (titleEl) titleEl.textContent = p.name || ""; if (modelEl) modelEl.textContent = "Mã: " + (p.code || ""); if (priceEl) { priceEl.innerHTML = '
' + '' + fmt(p.salePn) + "" + '' + fmt(p.listPn) + "" + (p.discount ? '-' + p.discount + "" : "") + "
"; } if (imagesEl) { // Full gallery, routed through the same-origin proxy like the main list. var gal = (p.image ? [p.image] : []).concat(Array.isArray(p._full && p._full.images) ? p._full.images : (Array.isArray(p.images) ? p.images : [])); var uni = []; for (var gi = 0; gi < gal.length; gi++) { if (gal[gi] && uni.indexOf(gal[gi]) < 0) uni.push(gal[gi]); } imagesEl.innerHTML = uni.length ? uni.map(function (u) { var pu = (typeof window.primaryImgSrc === 'function' ? window.primaryImgSrc(u) : u); return '' + (p.code || '; }).join("") : '
'; } if (overlay) { overlay.classList.remove("show"); overlay.style.display = "flex"; } if (modal) { modal.classList.remove("show"); modal.style.display = "block"; } if (window.VAI_modalTop) window.VAI_modalTop(overlay); } catch (e) {} } function refreshPanel() { try { var panel = document.getElementById("flashsale-panel"); var btn = document.getElementById("flashsale-floating"); if (!panel || !btn) return; var list = panel.querySelector(".flashsale-list"); if (!list) return; var prods = buildFlashProducts(); var cEl = btn.querySelector(".flashsale-count"); if (cEl) cEl.textContent = " " + prods.length; if (!prods.length) { list.innerHTML = '
Chưa có sản phẩm 2 giá nào.
'; return; } list.innerHTML = prods.map(function (p, i) { var img = p.image ? '' : ""; return '
' + img + '
' + '
' + (p.name || "") + '
' + '
' + '' + (p.listPn ? fmt(p.listPn) : "") + '' + (p.salePn ? fmt(p.salePn) : "") + '' + '
' + '
' + '
'; }).join(""); } catch (e) { console.warn("[FLASHSALE] refresh:", e); } } // Chia sẻ TOÀN BỘ danh sách (?flashsale=all). window.shareFlashsaleList = function (ev) { try { if (ev) { ev.stopPropagation(); } var base = window.location.origin + window.location.pathname; var url = base + "?flashsale=all"; var done = function () { if (typeof window.toast === "function") window.toast("✓ Đã copy link chia sẻ danh sách FLASHSALE"); else alert("Link chia sẻ danh sách FLASHSALE:\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 FLASHSALE:", url); }); } else { window.prompt("Link chia sẻ danh sách FLASHSALE:", url); } } catch (err) { alert("Lỗi chia sẻ: " + (err && err.message || err)); } }; // Mở full panel khi vào link ?flashsale=all. window.openFlashsalePanel = function () { try { var panel = document.getElementById("flashsale-panel"); var btn = document.getElementById("flashsale-floating"); if (panel) panel.style.display = "flex"; if (btn) btn.style.display = ""; if (panel) refreshPanel(); } catch (e) {} }; function handleUrlParam() { try { var params = new URLSearchParams(location.search); var v = params.get("flashsale") || ""; if (!v) return; var tries = 0; var iv = setInterval(function () { tries++; try { if (document.getElementById("flashsale-floating")) { window.openFlashsalePanel(); clearInterval(iv); } else if (tries > 40) clearInterval(iv); } catch (e) { if (tries > 40) clearInterval(iv); } }, 400); } catch (e) {} } function boot() { var _retry = 0; (function tryCreate() { _retry++; try { createElements(); refreshPanel(); handleUrlParam(); // Recompute when the catalog finishes loading or promo data changes. if (window.vaix && typeof window.vaix.load === "function") { window.vaix.load().then(function () { refreshPanel(); }).catch(function () {}); } window.addEventListener("vai-promo-changed", function () { setTimeout(refreshPanel, 200); }); window.addEventListener("vai-catalog-loaded", function () { refreshPanel(); }); } catch (e) { if (_retry < 30) setTimeout(tryCreate, 500); } })(); } // Quick alias so the module still works even before vaix finishes loading. window.addEventListener("DOMContentLoaded", function () { try { var d = document.getElementById("flashsale-floating"); if (!d) boot(); } catch (e) {} }); if (document.readyState !== "loading") boot(); })();