// 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Á Chia sẻ danh sách×
';
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 ' '; }).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();
})();