0
Đang tải...
Đang tải...
Mã SKU: Đang tải...
|
Đã bán 0
|
|
Truy xuất nguồn gốc
0
Đang tải...
Đảm bảo chất lượng
Giao hàng nhanh
Đóng gói cẩn thận
Thông số kỹ thuật
Thông tin chi tiết về sản phẩm
Đang tải thông số kỹ thuật...
Không thể tải thông số kỹ thuật
Đang tải...
Voucher của cửa hàng
Chọn voucher và nhấn Lưu để dùng khi thanh toán
' +
'' +
'' + escapeHtml(title) + '
' +
'Hiệu lực từ: ' + formatDate(displayDate) + (expirationDate ? ' · Hết hạn: ' + formatDate(expirationDate) : '') + '
' +
'' +
'' +
'' +
'';
return card;
}
function escapeHtml(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function showLoading(show) {
var el = document.getElementById('voucher-loading');
if (el) el.classList.toggle('hidden', !show);
}
function showError(msg) {
var wrap = document.getElementById('voucher-slider-wrap');
var err = document.getElementById('voucher-error');
var empty = document.getElementById('voucher-empty');
if (wrap) wrap.classList.add('hidden');
if (empty) empty.classList.add('hidden');
if (err) {
err.textContent = msg || 'Không tải được voucher';
err.classList.remove('hidden');
}
}
function showSlider(items) {
var wrap = document.getElementById('voucher-slider-wrap');
var slider = document.getElementById('voucher-slider');
var loading = document.getElementById('voucher-loading');
var error = document.getElementById('voucher-error');
var empty = document.getElementById('voucher-empty');
if (loading) loading.classList.add('hidden');
if (error) error.classList.add('hidden');
if (empty) empty.classList.add('hidden');
if (!items || items.length === 0) {
if (empty) {
empty.classList.remove('hidden');
if (wrap) wrap.classList.add('hidden');
}
return;
}
if (!slider) return;
slider.innerHTML = '';
items.forEach(function (item) {
slider.appendChild(renderVoucherCard(item));
});
document.querySelectorAll('#voucher-slider .btn-conditions').forEach(function (btn) {
btn.addEventListener('click', function () {
var did = btn.getAttribute('data-discount-id');
var lim = btn.getAttribute('data-limitation-times');
var limAcc = btn.getAttribute('data-limitation-times-per-account');
if (did) openVoucherConditionsDialog(did, lim, limAcc);
});
});
if (wrap) wrap.classList.remove('hidden');
// Nút prev/next (ẩn nếu ít thẻ)
var prev = document.getElementById('voucher-slider-prev');
var next = document.getElementById('voucher-slider-next');
if (prev && next) {
if (items.length <= 2) {
prev.classList.add('hidden');
next.classList.add('hidden');
} else {
prev.classList.remove('hidden');
next.classList.remove('hidden');
prev.onclick = function () {
slider.scrollBy({ left: -300, behavior: 'smooth' });
};
next.onclick = function () {
slider.scrollBy({ left: 300, behavior: 'smooth' });
};
}
}
}
async function loadVouchers() {
var Utils = window.CommerceUtils;
if (!Utils || !Utils.getProductInfo || !Utils.fetchChannelIdByProduct) {
showError('Chưa sẵn sàng lấy channel.');
return;
}
// Ensure API hydration (avoid reading DOM data-product-id placeholder)
try {
if (typeof Utils.getApiProduct === 'function') {
await Utils.getApiProduct();
}
} catch (e) { }
var productInfo = Utils.getProductInfo();
if (!productInfo || !productInfo.productId) {
showError('Không có thông tin sản phẩm.');
return;
}
showLoading(true);
try {
var channelId = await Utils.fetchChannelIdByProduct(productInfo.productId);
if (!channelId) {
showSlider([]);
return;
}
var data = await fetchDiscountsByChannelId(channelId);
showSlider(data.items || []);
} catch (e) {
console.error('Voucher load error:', e);
showError(e.message || 'Không tải được voucher');
} finally {
showLoading(false);
}
}
function init() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
attachVoucherDialogClose();
setTimeout(loadVouchers, 500);
});
} else {
setTimeout(loadVouchers, 500);
}
}
if (typeof Liferay !== 'undefined' && Liferay.on) {
Liferay.on('allPortletsReady', function () {
setTimeout(loadVouchers, 800);
});
}
init();
})();
// ==================== LOAD ORGANIZATION INFO ====================
(function () {
'use strict';
async function loadOrganizationInfo() {
try {
const Utils = window.CommerceUtils;
if (!Utils) {
console.warn('CommerceUtils chưa sẵn sàng');
return;
}
// Lấy trực tiếp theo productId (by-id)
const apiProduct = (typeof Utils.getApiProduct === 'function') ? await Utils.getApiProduct() : null;
const accountFromProduct = apiProduct && apiProduct.account ? apiProduct.account : null;
if (!accountFromProduct) return;
const finalAccountId = accountFromProduct.id || null;
const baseUrl = window.location.origin || '';
// Cập nhật tên tổ chức
const nameElement = document.getElementById('organization-name');
if (nameElement) {
const displayName =
accountFromProduct.name ||
apiProduct.supplierName ||
apiProduct.channelName ||
'—';
nameElement.textContent = displayName;
}
// Cập nhật địa chỉ từ API products response
// Ưu tiên: account.address > account.customFields.address > firstProduct.address
const locationElement = document.getElementById('organization-location');
if (locationElement) {
let address = accountFromProduct.address ||
accountFromProduct.customFields?.address ||
apiProduct.address ||
'';
if (address) {
locationElement.textContent = address;
}
}
// Cập nhật logo/banner (nếu có trong account response)
const logoContainer = document.getElementById('organization-logo-container');
if (logoContainer) {
const logoUrlRaw =
accountFromProduct.logoURL ||
accountFromProduct.logoUrl ||
accountFromProduct.customFields?.logoURL ||
accountFromProduct.customFields?.logoUrl ||
accountFromProduct.customFields?.banner ||
'';
if (logoUrlRaw) {
const placeholder = document.getElementById('organization-logo-placeholder');
if (placeholder) placeholder.remove();
let logoImg = document.getElementById('organization-logo-img');
if (!logoImg) {
logoImg = document.createElement('img');
logoImg.id = 'organization-logo-img';
logoImg.className = 'w-full h-full rounded-full object-cover';
logoContainer.appendChild(logoImg);
}
let logoUrl = logoUrlRaw;
if (logoUrl && !logoUrl.startsWith('http') && !logoUrl.startsWith('//')) {
logoUrl = baseUrl + logoUrl;
}
logoImg.src = logoUrl;
logoImg.alt = accountFromProduct.name || 'Organization Logo';
}
}
// Cập nhật URL "Xem cửa hàng" với accountId
const urlLink = document.getElementById('organization-url-link');
if (urlLink && finalAccountId) {
const htxDetailUrl = baseUrl + '/en/htx-detail?id=' + finalAccountId;
urlLink.href = htxDetailUrl;
}
// Cập nhật rating (ưu tiên pointAvg từ API product; fallback: —)
const ratingEl = document.getElementById('organization-rating');
if (ratingEl) {
const pointAvgRaw = apiProduct?.pointAvg;
const pointAvg = (typeof pointAvgRaw === 'number') ? pointAvgRaw : Number(pointAvgRaw);
ratingEl.textContent = (pointAvg && !isNaN(pointAvg)) ? pointAvg.toFixed(1) : '—';
}
} catch (error) {
console.error('Error loading organization info:', error);
}
}
// Khởi tạo khi DOM ready
function initOrganizationInfo() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadOrganizationInfo);
} else {
loadOrganizationInfo();
}
}
initOrganizationInfo();
})();
// ==================== ADD TO CART FUNCTIONALITY ====================
(function () {
'use strict';
let isAddingToCart = false;
// Sử dụng shared utilities thay vì duplicate code
const Utils = window.CommerceUtils;
/**
* Đồng bộ affiliateTrackingCode vào Commerce Order hiện tại sau khi add-to-cart thành công.
* - Lấy trackingCode từ sessionStorage (key: AFF_TRACK)
* - Lấy commerceOrderId từ Liferay.CommerceContext.commerceOrderId
* - Nếu order đã có affiliateTrackingCode thì KHÔNG overwrite
* - Sử dụng PATCH /o/headless-commerce-delivery-cart/v1.0/carts/{orderId}
* - Có cache theo session để tránh gọi API nhiều lần cho cùng một order
*/
async function updateAffiliateTrackingCodeForCurrentOrder() {
try {
// 1) Lấy trackingCode từ sessionStorage
let trackingCode = null;
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
trackingCode = window.sessionStorage.getItem('AFF_TRACK');
}
} catch (e) {
console.warn('[AFF] Không thể đọc sessionStorage:', e);
}
// 2) Nếu không có trackingCode -> không làm gì
if (!trackingCode) {
return;
}
// 3) Lấy commerceOrderId - Thử nhiều cách
var orderId = null;
// CÁCH 1: Ưu tiên lấy từ sessionStorage (orderId vừa được lưu sau add to cart)
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
const savedOrderId = window.sessionStorage.getItem('LAST_CART_ORDER_ID');
if (savedOrderId) {
orderId = parseInt(savedOrderId, 10);
if (orderId && orderId > 0) {
console.log('[AFF] Lấy orderId từ sessionStorage:', orderId);
} else {
orderId = null;
}
}
}
} catch (e) {
console.warn('[AFF] Không thể đọc sessionStorage:', e);
}
// CÁCH 2: Thử lấy từ Liferay.CommerceContext (nếu chưa có từ sessionStorage)
if (!orderId || orderId <= 0) {
if (typeof Liferay !== 'undefined' && Liferay.CommerceContext) {
orderId = Liferay.CommerceContext.commerceOrderId;
if (orderId && orderId > 0) {
console.log('[AFF] Lấy orderId từ Liferay.CommerceContext:', orderId);
} else {
orderId = null;
}
}
}
// CÁCH 3: Nếu vẫn không có, lấy từ API Headless (cart hiện tại)
if (!orderId || orderId <= 0) {
try {
const { accountId: accountIdValue, channelId } = await Utils.fetchAccountContext();
if (accountIdValue && channelId) {
const cart = await Utils.getAccountChannelCart(accountIdValue, channelId);
if (cart && (cart.id || cart.orderId)) {
orderId = cart.id || cart.orderId;
}
}
} catch (e) {
console.error('[AFF] Lỗi khi lấy cart từ API:', e);
}
}
if (!orderId || orderId <= 0) {
return;
}
// 4) Tránh gọi API nhiều lần cho cùng một order trong một session
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
var cacheKey = 'AFF_ORDER_UPDATED_' + orderId;
if (window.sessionStorage.getItem(cacheKey)) {
return;
}
}
} catch (e) {
console.warn('[AFF] Không thể truy cập sessionStorage:', e);
}
// 5) Gọi API GET Order để kiểm tra customFields hiện tại (đặc biệt là affiliateTrackingCode)
// Lưu ý: Cart API chỉ là view của Order với orderStatus = 2, nên cần cập nhật trực tiếp vào Order API
const baseOrderUrl = APP_CONFIG && APP_CONFIG.API_BASE_ORDER ? APP_CONFIG.API_BASE_ORDER : '/o/headless-commerce-admin-order/v1.0';
const orderUrl = baseOrderUrl + '/orders/' + orderId;
const getOrderRes = await fetch(orderUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
'x-csrf-token': (typeof Liferay !== 'undefined' ? Liferay.authToken : '')
},
credentials: 'include'
});
if (!getOrderRes.ok) {
const errorText = await getOrderRes.text().catch(function () { return ''; });
console.error('[AFF] Lỗi GET order để kiểm tra affiliateTrackingCode:', getOrderRes.status, errorText);
return;
}
const orderData = await getOrderRes.json();
// affiliateTrackingCode là field ở root level, không phải trong customFields
const currentAffiliateCode = orderData && orderData.affiliateTrackingCode ? orderData.affiliateTrackingCode : null;
// 6) Không overwrite nếu order đã có affiliateTrackingCode
if (currentAffiliateCode && currentAffiliateCode.trim() !== '') {
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
window.sessionStorage.setItem('AFF_ORDER_UPDATED_' + orderId, 'true');
}
} catch (e) { }
return;
}
// 7) Chuẩn bị body PATCH: affiliateTrackingCode là custom field trong customFields
const patchBody = {
customFields: {
...(orderData.customFields || {}),
affiliateTrackingCode: trackingCode
}
};
// 8) Gọi PATCH Order API để cập nhật custom field affiliateTrackingCode
const patchOrderRes = await fetch(orderUrl, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'x-csrf-token': (typeof Liferay !== 'undefined' ? Liferay.authToken : '')
},
credentials: 'include',
body: JSON.stringify(patchBody)
});
if (!patchOrderRes.ok) {
const errorTextPatch = await patchOrderRes.text().catch(function () { return ''; });
console.error('[AFF] Lỗi PATCH order để cập nhật affiliateTrackingCode:', patchOrderRes.status, errorTextPatch);
return;
}
// 9) Sau khi cập nhật thành công, xóa AFF_TRACK khỏi sessionStorage
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
window.sessionStorage.removeItem('AFF_TRACK');
window.sessionStorage.removeItem('AFF_TRACK_USER_ID');
window.sessionStorage.setItem('AFF_ORDER_UPDATED_' + orderId, 'true');
}
} catch (e) {
console.warn('[AFF] Không thể cập nhật/xóa dữ liệu AFF trong sessionStorage:', e);
}
} catch (error) {
console.error('[AFF] Lỗi khi cập nhật affiliateTrackingCode cho order hiện tại:', error);
}
}
async function addToCart() {
if (isAddingToCart) return;
if (!Utils) {
showToastSPNB('Hệ thống đang khởi tạo. Vui lòng thử lại sau.', true);
return;
}
// Ensure API hydration (avoid reading DOM placeholder data-product-id)
try {
if (typeof Utils.getApiProduct === 'function') {
await Utils.getApiProduct();
}
} catch (e) { }
// Kiểm tra đăng nhập trước
let userId = 0;
if (typeof Liferay !== 'undefined' && Liferay.ThemeDisplay) {
userId = Liferay.ThemeDisplay.isSignedIn() ? (Liferay.ThemeDisplay.getUserId() || 0) : 0;
}
// Nếu chưa đăng nhập, redirect đến trang login
if (!userId || userId === 0) {
window.location.href = '/login';
return;
}
const productInfo = Utils.getProductInfo();
if (!productInfo || !productInfo.productId) {
showToastSPNB('Không thể lấy thông tin sản phẩm. Vui lòng thử lại sau.', true);
return;
}
// Ưu tiên lấy từ mobile input nếu có, nếu không thì lấy từ desktop
const quantityInputMobile = document.getElementById('quantity-input-mobile');
const quantityInput = document.getElementById('quantity-input');
const quantityInputToUse = quantityInputMobile || quantityInput;
if (!quantityInputToUse) {
showToastSPNB('Không tìm thấy input số lượng. Vui lòng thử lại sau.', true);
return;
}
const quantity = parseInt(quantityInputToUse.value) || 1;
if (quantity < 1) {
showToastSPNB('Số lượng phải lớn hơn 0.', true);
return;
}
const { accountId: accountIdValue } = await Utils.fetchAccountContext();
if (!accountIdValue) {
// Nếu không lấy được accountId, redirect đến trang login
window.location.href = '/login';
return;
}
// Kênh bán: chỉ từ product (by-id / Admin catalog), không fallback kênh cố định
const channelFromApi = safeParseInt(Utils && Utils.apiProduct ? Utils.apiProduct.channelId : null);
const channelFromProduct = await Utils.fetchChannelIdByProduct(productInfo.productId);
const channelIdToUse = channelFromProduct || channelFromApi;
if (!channelIdToUse) {
showToastSPNB('Không xác định được kênh bán hàng (channel).', true);
return;
}
isAddingToCart = true;
const btnAddToCartDesktop = document.getElementById('btn-add-to-cart-desktop');
const btnAddToCartMobile = document.getElementById('btn-add-to-cart-mobile');
const addToCartTextDesktop = document.getElementById('add-to-cart-text-desktop');
const addToCartTextMobile = document.getElementById('add-to-cart-text-mobile');
if (btnAddToCartDesktop) btnAddToCartDesktop.disabled = true;
if (btnAddToCartMobile) btnAddToCartMobile.disabled = true;
if (addToCartTextDesktop) addToCartTextDesktop.textContent = 'Đang thêm...';
if (addToCartTextMobile) addToCartTextMobile.textContent = 'Đang thêm...';
let success = false;
try {
// Ưu tiên SKU đang chọn trên UI (variant selector đã sync vào DOM)
const totalPriceEl = document.getElementById('product-total-price') ||
document.getElementById('product-total-price-mobile');
let skuId = null;
let skuUnitOfMeasure = null;
if (totalPriceEl) {
const skuIdAttr = (totalPriceEl.getAttribute('data-sku-id') || '').toString().replace(/[^\d]/g, '');
if (skuIdAttr) {
skuId = safeParseInt(skuIdAttr);
}
const skuUomKeyAttr = (totalPriceEl.getAttribute('data-unit-of-measure-key') || '').trim();
if (skuUomKeyAttr) {
skuUnitOfMeasure = {
key: skuUomKeyAttr,
precision: 0,
incrementalOrderQuantity: 1
};
}
}
// Fallback: cùng nguồn by-id (resolveSkuForCart) — không gọi thêm API SKU
if (!skuId) {
const skuData = typeof Utils.resolveSkuForCart === 'function'
? Utils.resolveSkuForCart(productInfo.productId)
: null;
if (!skuData || !skuData.id) {
throw new Error('Không tìm thấy SKU cho sản phẩm này.');
}
skuId = skuData.id;
if (!skuUnitOfMeasure) {
skuUnitOfMeasure = skuData.skuUnitOfMeasure;
}
}
try {
if (typeof Utils.getEffectiveStockLevel === 'function') {
const eff = Utils.getEffectiveStockLevel(skuId);
if (eff != null && eff <= 0) {
showToastSPNB('Sản phẩm đã hết hàng', true);
return;
}
}
} catch (e) { }
if (typeof Utils.getEffectiveFinalPrice === 'function') {
const unitPrice = Utils.getEffectiveFinalPrice();
if (!unitPrice || unitPrice <= 0) {
showToastSPNB('Sản phẩm chưa có giá niêm yết. Vui lòng liên hệ.', true);
return;
}
}
// Không ép UOM mặc định — để cart API dùng UOM của SKU (tránh 400 do sai key).
// Kiểm tra giỏ hàng hiện tại
let cart = await Utils.getAccountChannelCart(accountIdValue, channelIdToUse);
let currentOrderId = null;
if (!cart || !cart.id) {
// Tạo giỏ hàng mới kèm sản phẩm
try {
const newCart = await Utils.createCartWithItem(accountIdValue, skuId, skuUnitOfMeasure, quantity, channelIdToUse);
if (newCart && (newCart.id || newCart.orderId)) {
currentOrderId = newCart.id || newCart.orderId;
success = true;
}
} catch (createErr) {
// Không dùng giỏ mở không lọc channelId — sẽ gộp nhầm sang đơn kênh khác (HTX khác)
const recoveredCart = await Utils.getAccountChannelCart(accountIdValue, channelIdToUse);
if (recoveredCart && (recoveredCart.id || recoveredCart.orderId)) {
cart = recoveredCart;
} else {
throw createErr;
}
}
}
if (cart && (cart.id || cart.orderId)) {
const cartId = cart.id || cart.orderId;
currentOrderId = cartId;
const cartWithItems = await Utils.getCartWithItems(cartId) || cart;
const cartItems = cartWithItems.cartItems || [];
const existingItem = cartItems.find(item =>
item && item.skuId === skuId &&
(!skuUnitOfMeasure || !item.skuUnitOfMeasure || item.skuUnitOfMeasure.key === skuUnitOfMeasure.key)
);
if (existingItem) {
const newQty = (parseInt(existingItem.quantity) || 0) + quantity;
await Utils.updateCartItemQuantity(cartId, existingItem.id || existingItem.itemId, newQty);
} else {
await Utils.addItemToCart(cartId, skuId, quantity, null, skuUnitOfMeasure);
}
success = true;
}
if (currentOrderId && typeof window !== 'undefined' && window.sessionStorage) {
window.sessionStorage.setItem('LAST_CART_ORDER_ID', currentOrderId);
}
} catch (err) {
console.error('Add to cart error:', err);
success = false;
// Kiểm tra xem có phải lỗi do item đã tồn tại không
const errorMsg = err.message || '';
const errorMsgLower = errorMsg.toLowerCase();
// Một số trường hợp có thể coi là thành công (item đã có trong cart)
if (errorMsgLower.includes('already') ||
errorMsgLower.includes('đã tồn tại') ||
errorMsgLower.includes('duplicate') ||
errorMsgLower.includes('đã có trong giỏ hàng')) {
// Item đã tồn tại, coi như thành công
success = true;
window._cartAlertShown = true; // Đánh dấu đã hiển thị toast
showToastSPNB('Sản phẩm đã có trong giỏ hàng!', false);
} else {
// Hiển thị lỗi thực sự
window._cartAlertShown = true; // Đánh dấu đã hiển thị toast
showToastSPNB(errorMsg, true);
}
} finally {
isAddingToCart = false;
const btnAddToCartDesktop = document.getElementById('btn-add-to-cart-desktop');
const btnAddToCartMobile = document.getElementById('btn-add-to-cart-mobile');
const addToCartTextDesktop = document.getElementById('add-to-cart-text-desktop');
const addToCartTextMobile = document.getElementById('add-to-cart-text-mobile');
const stockInfoEl = document.getElementById('stock-info');
let allowPurchaseNow = true;
if (stockInfoEl) {
const maxQtyNow = safeParseInt(
stockInfoEl.getAttribute('data-max-quantity'),
APP_CONFIG.MAX_QUANTITY_DEFAULT
);
if (maxQtyNow === 0) {
allowPurchaseNow = false;
}
}
if (btnAddToCartDesktop) btnAddToCartDesktop.disabled = !allowPurchaseNow;
if (btnAddToCartMobile) btnAddToCartMobile.disabled = !allowPurchaseNow;
if (addToCartTextDesktop) addToCartTextDesktop.textContent = 'Thêm vào giỏ hàng';
if (addToCartTextMobile) addToCartTextMobile.textContent = 'Thêm vào giỏ';
// Chỉ hiển thị thông báo thành công nếu đã thành công VÀ chưa hiển thị toast trong catch
if (success && !window._cartAlertShown) {
showToastSPNB('Đã thêm sản phẩm vào giỏ hàng thành công!', false);
}
// Sau khi add-to-cart thành công, cố gắng gắn affiliateTrackingCode vào order hiện tại (nếu có)
if (success) {
window.updateStaticViewProductsAddToCart?.().catch(function (err) {
console.error('[STATIC_VIEW_ADD_TO_CART] error:', err);
});
// Không await để tránh chặn UI; mọi lỗi được log bên trong hàm
updateAffiliateTrackingCodeForCurrentOrder().catch(function (err) {
console.error('[AFF] Lỗi ngoài khi gọi updateAffiliateTrackingCodeForCurrentOrder:', err);
});
}
// Reset flag
window._cartAlertShown = false;
}
}
function initAddToCartButton() {
// Desktop button
const btnAddToCartDesktop = document.getElementById('btn-add-to-cart-desktop');
if (btnAddToCartDesktop) {
const newBtnDesktop = btnAddToCartDesktop.cloneNode(true);
btnAddToCartDesktop.parentNode.replaceChild(newBtnDesktop, btnAddToCartDesktop);
newBtnDesktop.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
addToCart();
});
}
// Mobile button
const btnAddToCartMobile = document.getElementById('btn-add-to-cart-mobile');
if (btnAddToCartMobile) {
const newBtnMobile = btnAddToCartMobile.cloneNode(true);
btnAddToCartMobile.parentNode.replaceChild(newBtnMobile, btnAddToCartMobile);
newBtnMobile.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
addToCart();
});
}
}
// Chỉ init một lần để tránh duplicate
let addToCartInitialized = false;
function tryInitAddToCart() {
if (addToCartInitialized) return;
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
initAddToCartButton();
addToCartInitialized = true;
});
} else {
initAddToCartButton();
addToCartInitialized = true;
}
}
tryInitAddToCart();
// Hỗ trợ Liferay events - chỉ init một lần
if (typeof Liferay !== 'undefined') {
if (Liferay.on && !addToCartInitialized) {
Liferay.on('allPortletsReady', function () {
if (!addToCartInitialized) {
setTimeout(function () {
initAddToCartButton();
addToCartInitialized = true;
}, 500);
}
});
}
}
})();
Đảm bảo chất lượng
Giao hàng nhanh
Đóng gói cẩn thận
Thông số kỹ thuật
Thông tin chi tiết về sản phẩm
Đang tải thông số kỹ thuật...
Không thể tải thông số kỹ thuật
Đang tải...
Voucher của cửa hàng
Chọn voucher và nhấn Lưu để dùng khi thanh toán
Chưa có voucher nào cho
cửa hàng này.
0
Đang tải...
Đang tải...
Mã SKU: Đang tải...
0
Đang tải...
Đảm bảo chất lượng
Giao hàng nhanh
Đóng gói cẩn thận
Thông số kỹ thuật
Thông tin chi tiết về sản phẩm
Đang tải thông số kỹ thuật...
Không thể tải thông số kỹ thuật
Đang tải...
Voucher của cửa hàng
Chọn voucher và nhấn Lưu để dùng khi thanh toán
' +
'
';
return card;
}
function escapeHtml(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function showLoading(show) {
var el = document.getElementById('voucher-loading');
if (el) el.classList.toggle('hidden', !show);
}
function showError(msg) {
var wrap = document.getElementById('voucher-slider-wrap');
var err = document.getElementById('voucher-error');
var empty = document.getElementById('voucher-empty');
if (wrap) wrap.classList.add('hidden');
if (empty) empty.classList.add('hidden');
if (err) {
err.textContent = msg || 'Không tải được voucher';
err.classList.remove('hidden');
}
}
function showSlider(items) {
var wrap = document.getElementById('voucher-slider-wrap');
var slider = document.getElementById('voucher-slider');
var loading = document.getElementById('voucher-loading');
var error = document.getElementById('voucher-error');
var empty = document.getElementById('voucher-empty');
if (loading) loading.classList.add('hidden');
if (error) error.classList.add('hidden');
if (empty) empty.classList.add('hidden');
if (!items || items.length === 0) {
if (empty) {
empty.classList.remove('hidden');
if (wrap) wrap.classList.add('hidden');
}
return;
}
if (!slider) return;
slider.innerHTML = '';
items.forEach(function (item) {
slider.appendChild(renderVoucherCard(item));
});
document.querySelectorAll('#voucher-slider .btn-conditions').forEach(function (btn) {
btn.addEventListener('click', function () {
var did = btn.getAttribute('data-discount-id');
var lim = btn.getAttribute('data-limitation-times');
var limAcc = btn.getAttribute('data-limitation-times-per-account');
if (did) openVoucherConditionsDialog(did, lim, limAcc);
});
});
if (wrap) wrap.classList.remove('hidden');
// Nút prev/next (ẩn nếu ít thẻ)
var prev = document.getElementById('voucher-slider-prev');
var next = document.getElementById('voucher-slider-next');
if (prev && next) {
if (items.length <= 2) {
prev.classList.add('hidden');
next.classList.add('hidden');
} else {
prev.classList.remove('hidden');
next.classList.remove('hidden');
prev.onclick = function () {
slider.scrollBy({ left: -300, behavior: 'smooth' });
};
next.onclick = function () {
slider.scrollBy({ left: 300, behavior: 'smooth' });
};
}
}
}
async function loadVouchers() {
var Utils = window.CommerceUtils;
if (!Utils || !Utils.getProductInfo || !Utils.fetchChannelIdByProduct) {
showError('Chưa sẵn sàng lấy channel.');
return;
}
// Ensure API hydration (avoid reading DOM data-product-id placeholder)
try {
if (typeof Utils.getApiProduct === 'function') {
await Utils.getApiProduct();
}
} catch (e) { }
var productInfo = Utils.getProductInfo();
if (!productInfo || !productInfo.productId) {
showError('Không có thông tin sản phẩm.');
return;
}
showLoading(true);
try {
var channelId = await Utils.fetchChannelIdByProduct(productInfo.productId);
if (!channelId) {
showSlider([]);
return;
}
var data = await fetchDiscountsByChannelId(channelId);
showSlider(data.items || []);
} catch (e) {
console.error('Voucher load error:', e);
showError(e.message || 'Không tải được voucher');
} finally {
showLoading(false);
}
}
function init() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
attachVoucherDialogClose();
setTimeout(loadVouchers, 500);
});
} else {
setTimeout(loadVouchers, 500);
}
}
if (typeof Liferay !== 'undefined' && Liferay.on) {
Liferay.on('allPortletsReady', function () {
setTimeout(loadVouchers, 800);
});
}
init();
})();
// ==================== LOAD ORGANIZATION INFO ====================
(function () {
'use strict';
async function loadOrganizationInfo() {
try {
const Utils = window.CommerceUtils;
if (!Utils) {
console.warn('CommerceUtils chưa sẵn sàng');
return;
}
// Lấy trực tiếp theo productId (by-id)
const apiProduct = (typeof Utils.getApiProduct === 'function') ? await Utils.getApiProduct() : null;
const accountFromProduct = apiProduct && apiProduct.account ? apiProduct.account : null;
if (!accountFromProduct) return;
const finalAccountId = accountFromProduct.id || null;
const baseUrl = window.location.origin || '';
// Cập nhật tên tổ chức
const nameElement = document.getElementById('organization-name');
if (nameElement) {
const displayName =
accountFromProduct.name ||
apiProduct.supplierName ||
apiProduct.channelName ||
'—';
nameElement.textContent = displayName;
}
// Cập nhật địa chỉ từ API products response
// Ưu tiên: account.address > account.customFields.address > firstProduct.address
const locationElement = document.getElementById('organization-location');
if (locationElement) {
let address = accountFromProduct.address ||
accountFromProduct.customFields?.address ||
apiProduct.address ||
'';
if (address) {
locationElement.textContent = address;
}
}
// Cập nhật logo/banner (nếu có trong account response)
const logoContainer = document.getElementById('organization-logo-container');
if (logoContainer) {
const logoUrlRaw =
accountFromProduct.logoURL ||
accountFromProduct.logoUrl ||
accountFromProduct.customFields?.logoURL ||
accountFromProduct.customFields?.logoUrl ||
accountFromProduct.customFields?.banner ||
'';
if (logoUrlRaw) {
const placeholder = document.getElementById('organization-logo-placeholder');
if (placeholder) placeholder.remove();
let logoImg = document.getElementById('organization-logo-img');
if (!logoImg) {
logoImg = document.createElement('img');
logoImg.id = 'organization-logo-img';
logoImg.className = 'w-full h-full rounded-full object-cover';
logoContainer.appendChild(logoImg);
}
let logoUrl = logoUrlRaw;
if (logoUrl && !logoUrl.startsWith('http') && !logoUrl.startsWith('//')) {
logoUrl = baseUrl + logoUrl;
}
logoImg.src = logoUrl;
logoImg.alt = accountFromProduct.name || 'Organization Logo';
}
}
// Cập nhật URL "Xem cửa hàng" với accountId
const urlLink = document.getElementById('organization-url-link');
if (urlLink && finalAccountId) {
const htxDetailUrl = baseUrl + '/en/htx-detail?id=' + finalAccountId;
urlLink.href = htxDetailUrl;
}
// Cập nhật rating (ưu tiên pointAvg từ API product; fallback: —)
const ratingEl = document.getElementById('organization-rating');
if (ratingEl) {
const pointAvgRaw = apiProduct?.pointAvg;
const pointAvg = (typeof pointAvgRaw === 'number') ? pointAvgRaw : Number(pointAvgRaw);
ratingEl.textContent = (pointAvg && !isNaN(pointAvg)) ? pointAvg.toFixed(1) : '—';
}
} catch (error) {
console.error('Error loading organization info:', error);
}
}
// Khởi tạo khi DOM ready
function initOrganizationInfo() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadOrganizationInfo);
} else {
loadOrganizationInfo();
}
}
initOrganizationInfo();
})();
// ==================== ADD TO CART FUNCTIONALITY ====================
(function () {
'use strict';
let isAddingToCart = false;
// Sử dụng shared utilities thay vì duplicate code
const Utils = window.CommerceUtils;
/**
* Đồng bộ affiliateTrackingCode vào Commerce Order hiện tại sau khi add-to-cart thành công.
* - Lấy trackingCode từ sessionStorage (key: AFF_TRACK)
* - Lấy commerceOrderId từ Liferay.CommerceContext.commerceOrderId
* - Nếu order đã có affiliateTrackingCode thì KHÔNG overwrite
* - Sử dụng PATCH /o/headless-commerce-delivery-cart/v1.0/carts/{orderId}
* - Có cache theo session để tránh gọi API nhiều lần cho cùng một order
*/
async function updateAffiliateTrackingCodeForCurrentOrder() {
try {
// 1) Lấy trackingCode từ sessionStorage
let trackingCode = null;
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
trackingCode = window.sessionStorage.getItem('AFF_TRACK');
}
} catch (e) {
console.warn('[AFF] Không thể đọc sessionStorage:', e);
}
// 2) Nếu không có trackingCode -> không làm gì
if (!trackingCode) {
return;
}
// 3) Lấy commerceOrderId - Thử nhiều cách
var orderId = null;
// CÁCH 1: Ưu tiên lấy từ sessionStorage (orderId vừa được lưu sau add to cart)
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
const savedOrderId = window.sessionStorage.getItem('LAST_CART_ORDER_ID');
if (savedOrderId) {
orderId = parseInt(savedOrderId, 10);
if (orderId && orderId > 0) {
console.log('[AFF] Lấy orderId từ sessionStorage:', orderId);
} else {
orderId = null;
}
}
}
} catch (e) {
console.warn('[AFF] Không thể đọc sessionStorage:', e);
}
// CÁCH 2: Thử lấy từ Liferay.CommerceContext (nếu chưa có từ sessionStorage)
if (!orderId || orderId <= 0) {
if (typeof Liferay !== 'undefined' && Liferay.CommerceContext) {
orderId = Liferay.CommerceContext.commerceOrderId;
if (orderId && orderId > 0) {
console.log('[AFF] Lấy orderId từ Liferay.CommerceContext:', orderId);
} else {
orderId = null;
}
}
}
// CÁCH 3: Nếu vẫn không có, lấy từ API Headless (cart hiện tại)
if (!orderId || orderId <= 0) {
try {
const { accountId: accountIdValue, channelId } = await Utils.fetchAccountContext();
if (accountIdValue && channelId) {
const cart = await Utils.getAccountChannelCart(accountIdValue, channelId);
if (cart && (cart.id || cart.orderId)) {
orderId = cart.id || cart.orderId;
}
}
} catch (e) {
console.error('[AFF] Lỗi khi lấy cart từ API:', e);
}
}
if (!orderId || orderId <= 0) {
return;
}
// 4) Tránh gọi API nhiều lần cho cùng một order trong một session
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
var cacheKey = 'AFF_ORDER_UPDATED_' + orderId;
if (window.sessionStorage.getItem(cacheKey)) {
return;
}
}
} catch (e) {
console.warn('[AFF] Không thể truy cập sessionStorage:', e);
}
// 5) Gọi API GET Order để kiểm tra customFields hiện tại (đặc biệt là affiliateTrackingCode)
// Lưu ý: Cart API chỉ là view của Order với orderStatus = 2, nên cần cập nhật trực tiếp vào Order API
const baseOrderUrl = APP_CONFIG && APP_CONFIG.API_BASE_ORDER ? APP_CONFIG.API_BASE_ORDER : '/o/headless-commerce-admin-order/v1.0';
const orderUrl = baseOrderUrl + '/orders/' + orderId;
const getOrderRes = await fetch(orderUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
'x-csrf-token': (typeof Liferay !== 'undefined' ? Liferay.authToken : '')
},
credentials: 'include'
});
if (!getOrderRes.ok) {
const errorText = await getOrderRes.text().catch(function () { return ''; });
console.error('[AFF] Lỗi GET order để kiểm tra affiliateTrackingCode:', getOrderRes.status, errorText);
return;
}
const orderData = await getOrderRes.json();
// affiliateTrackingCode là field ở root level, không phải trong customFields
const currentAffiliateCode = orderData && orderData.affiliateTrackingCode ? orderData.affiliateTrackingCode : null;
// 6) Không overwrite nếu order đã có affiliateTrackingCode
if (currentAffiliateCode && currentAffiliateCode.trim() !== '') {
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
window.sessionStorage.setItem('AFF_ORDER_UPDATED_' + orderId, 'true');
}
} catch (e) { }
return;
}
// 7) Chuẩn bị body PATCH: affiliateTrackingCode là custom field trong customFields
const patchBody = {
customFields: {
...(orderData.customFields || {}),
affiliateTrackingCode: trackingCode
}
};
// 8) Gọi PATCH Order API để cập nhật custom field affiliateTrackingCode
const patchOrderRes = await fetch(orderUrl, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'x-csrf-token': (typeof Liferay !== 'undefined' ? Liferay.authToken : '')
},
credentials: 'include',
body: JSON.stringify(patchBody)
});
if (!patchOrderRes.ok) {
const errorTextPatch = await patchOrderRes.text().catch(function () { return ''; });
console.error('[AFF] Lỗi PATCH order để cập nhật affiliateTrackingCode:', patchOrderRes.status, errorTextPatch);
return;
}
// 9) Sau khi cập nhật thành công, xóa AFF_TRACK khỏi sessionStorage
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
window.sessionStorage.removeItem('AFF_TRACK');
window.sessionStorage.removeItem('AFF_TRACK_USER_ID');
window.sessionStorage.setItem('AFF_ORDER_UPDATED_' + orderId, 'true');
}
} catch (e) {
console.warn('[AFF] Không thể cập nhật/xóa dữ liệu AFF trong sessionStorage:', e);
}
} catch (error) {
console.error('[AFF] Lỗi khi cập nhật affiliateTrackingCode cho order hiện tại:', error);
}
}
async function addToCart() {
if (isAddingToCart) return;
if (!Utils) {
showToastSPNB('Hệ thống đang khởi tạo. Vui lòng thử lại sau.', true);
return;
}
// Ensure API hydration (avoid reading DOM placeholder data-product-id)
try {
if (typeof Utils.getApiProduct === 'function') {
await Utils.getApiProduct();
}
} catch (e) { }
// Kiểm tra đăng nhập trước
let userId = 0;
if (typeof Liferay !== 'undefined' && Liferay.ThemeDisplay) {
userId = Liferay.ThemeDisplay.isSignedIn() ? (Liferay.ThemeDisplay.getUserId() || 0) : 0;
}
// Nếu chưa đăng nhập, redirect đến trang login
if (!userId || userId === 0) {
window.location.href = '/login';
return;
}
const productInfo = Utils.getProductInfo();
if (!productInfo || !productInfo.productId) {
showToastSPNB('Không thể lấy thông tin sản phẩm. Vui lòng thử lại sau.', true);
return;
}
// Ưu tiên lấy từ mobile input nếu có, nếu không thì lấy từ desktop
const quantityInputMobile = document.getElementById('quantity-input-mobile');
const quantityInput = document.getElementById('quantity-input');
const quantityInputToUse = quantityInputMobile || quantityInput;
if (!quantityInputToUse) {
showToastSPNB('Không tìm thấy input số lượng. Vui lòng thử lại sau.', true);
return;
}
const quantity = parseInt(quantityInputToUse.value) || 1;
if (quantity < 1) {
showToastSPNB('Số lượng phải lớn hơn 0.', true);
return;
}
const { accountId: accountIdValue } = await Utils.fetchAccountContext();
if (!accountIdValue) {
// Nếu không lấy được accountId, redirect đến trang login
window.location.href = '/login';
return;
}
// Kênh bán: chỉ từ product (by-id / Admin catalog), không fallback kênh cố định
const channelFromApi = safeParseInt(Utils && Utils.apiProduct ? Utils.apiProduct.channelId : null);
const channelFromProduct = await Utils.fetchChannelIdByProduct(productInfo.productId);
const channelIdToUse = channelFromProduct || channelFromApi;
if (!channelIdToUse) {
showToastSPNB('Không xác định được kênh bán hàng (channel).', true);
return;
}
isAddingToCart = true;
const btnAddToCartDesktop = document.getElementById('btn-add-to-cart-desktop');
const btnAddToCartMobile = document.getElementById('btn-add-to-cart-mobile');
const addToCartTextDesktop = document.getElementById('add-to-cart-text-desktop');
const addToCartTextMobile = document.getElementById('add-to-cart-text-mobile');
if (btnAddToCartDesktop) btnAddToCartDesktop.disabled = true;
if (btnAddToCartMobile) btnAddToCartMobile.disabled = true;
if (addToCartTextDesktop) addToCartTextDesktop.textContent = 'Đang thêm...';
if (addToCartTextMobile) addToCartTextMobile.textContent = 'Đang thêm...';
let success = false;
try {
// Ưu tiên SKU đang chọn trên UI (variant selector đã sync vào DOM)
const totalPriceEl = document.getElementById('product-total-price') ||
document.getElementById('product-total-price-mobile');
let skuId = null;
let skuUnitOfMeasure = null;
if (totalPriceEl) {
const skuIdAttr = (totalPriceEl.getAttribute('data-sku-id') || '').toString().replace(/[^\d]/g, '');
if (skuIdAttr) {
skuId = safeParseInt(skuIdAttr);
}
const skuUomKeyAttr = (totalPriceEl.getAttribute('data-unit-of-measure-key') || '').trim();
if (skuUomKeyAttr) {
skuUnitOfMeasure = {
key: skuUomKeyAttr,
precision: 0,
incrementalOrderQuantity: 1
};
}
}
// Fallback: cùng nguồn by-id (resolveSkuForCart) — không gọi thêm API SKU
if (!skuId) {
const skuData = typeof Utils.resolveSkuForCart === 'function'
? Utils.resolveSkuForCart(productInfo.productId)
: null;
if (!skuData || !skuData.id) {
throw new Error('Không tìm thấy SKU cho sản phẩm này.');
}
skuId = skuData.id;
if (!skuUnitOfMeasure) {
skuUnitOfMeasure = skuData.skuUnitOfMeasure;
}
}
try {
if (typeof Utils.getEffectiveStockLevel === 'function') {
const eff = Utils.getEffectiveStockLevel(skuId);
if (eff != null && eff <= 0) {
showToastSPNB('Sản phẩm đã hết hàng', true);
return;
}
}
} catch (e) { }
if (typeof Utils.getEffectiveFinalPrice === 'function') {
const unitPrice = Utils.getEffectiveFinalPrice();
if (!unitPrice || unitPrice <= 0) {
showToastSPNB('Sản phẩm chưa có giá niêm yết. Vui lòng liên hệ.', true);
return;
}
}
// Không ép UOM mặc định — để cart API dùng UOM của SKU (tránh 400 do sai key).
// Kiểm tra giỏ hàng hiện tại
let cart = await Utils.getAccountChannelCart(accountIdValue, channelIdToUse);
let currentOrderId = null;
if (!cart || !cart.id) {
// Tạo giỏ hàng mới kèm sản phẩm
try {
const newCart = await Utils.createCartWithItem(accountIdValue, skuId, skuUnitOfMeasure, quantity, channelIdToUse);
if (newCart && (newCart.id || newCart.orderId)) {
currentOrderId = newCart.id || newCart.orderId;
success = true;
}
} catch (createErr) {
// Không dùng giỏ mở không lọc channelId — sẽ gộp nhầm sang đơn kênh khác (HTX khác)
const recoveredCart = await Utils.getAccountChannelCart(accountIdValue, channelIdToUse);
if (recoveredCart && (recoveredCart.id || recoveredCart.orderId)) {
cart = recoveredCart;
} else {
throw createErr;
}
}
}
if (cart && (cart.id || cart.orderId)) {
const cartId = cart.id || cart.orderId;
currentOrderId = cartId;
const cartWithItems = await Utils.getCartWithItems(cartId) || cart;
const cartItems = cartWithItems.cartItems || [];
const existingItem = cartItems.find(item =>
item && item.skuId === skuId &&
(!skuUnitOfMeasure || !item.skuUnitOfMeasure || item.skuUnitOfMeasure.key === skuUnitOfMeasure.key)
);
if (existingItem) {
const newQty = (parseInt(existingItem.quantity) || 0) + quantity;
await Utils.updateCartItemQuantity(cartId, existingItem.id || existingItem.itemId, newQty);
} else {
await Utils.addItemToCart(cartId, skuId, quantity, null, skuUnitOfMeasure);
}
success = true;
}
if (currentOrderId && typeof window !== 'undefined' && window.sessionStorage) {
window.sessionStorage.setItem('LAST_CART_ORDER_ID', currentOrderId);
}
} catch (err) {
console.error('Add to cart error:', err);
success = false;
// Kiểm tra xem có phải lỗi do item đã tồn tại không
const errorMsg = err.message || '';
const errorMsgLower = errorMsg.toLowerCase();
// Một số trường hợp có thể coi là thành công (item đã có trong cart)
if (errorMsgLower.includes('already') ||
errorMsgLower.includes('đã tồn tại') ||
errorMsgLower.includes('duplicate') ||
errorMsgLower.includes('đã có trong giỏ hàng')) {
// Item đã tồn tại, coi như thành công
success = true;
window._cartAlertShown = true; // Đánh dấu đã hiển thị toast
showToastSPNB('Sản phẩm đã có trong giỏ hàng!', false);
} else {
// Hiển thị lỗi thực sự
window._cartAlertShown = true; // Đánh dấu đã hiển thị toast
showToastSPNB(errorMsg, true);
}
} finally {
isAddingToCart = false;
const btnAddToCartDesktop = document.getElementById('btn-add-to-cart-desktop');
const btnAddToCartMobile = document.getElementById('btn-add-to-cart-mobile');
const addToCartTextDesktop = document.getElementById('add-to-cart-text-desktop');
const addToCartTextMobile = document.getElementById('add-to-cart-text-mobile');
const stockInfoEl = document.getElementById('stock-info');
let allowPurchaseNow = true;
if (stockInfoEl) {
const maxQtyNow = safeParseInt(
stockInfoEl.getAttribute('data-max-quantity'),
APP_CONFIG.MAX_QUANTITY_DEFAULT
);
if (maxQtyNow === 0) {
allowPurchaseNow = false;
}
}
if (btnAddToCartDesktop) btnAddToCartDesktop.disabled = !allowPurchaseNow;
if (btnAddToCartMobile) btnAddToCartMobile.disabled = !allowPurchaseNow;
if (addToCartTextDesktop) addToCartTextDesktop.textContent = 'Thêm vào giỏ hàng';
if (addToCartTextMobile) addToCartTextMobile.textContent = 'Thêm vào giỏ';
// Chỉ hiển thị thông báo thành công nếu đã thành công VÀ chưa hiển thị toast trong catch
if (success && !window._cartAlertShown) {
showToastSPNB('Đã thêm sản phẩm vào giỏ hàng thành công!', false);
}
// Sau khi add-to-cart thành công, cố gắng gắn affiliateTrackingCode vào order hiện tại (nếu có)
if (success) {
window.updateStaticViewProductsAddToCart?.().catch(function (err) {
console.error('[STATIC_VIEW_ADD_TO_CART] error:', err);
});
// Không await để tránh chặn UI; mọi lỗi được log bên trong hàm
updateAffiliateTrackingCodeForCurrentOrder().catch(function (err) {
console.error('[AFF] Lỗi ngoài khi gọi updateAffiliateTrackingCodeForCurrentOrder:', err);
});
}
// Reset flag
window._cartAlertShown = false;
}
}
function initAddToCartButton() {
// Desktop button
const btnAddToCartDesktop = document.getElementById('btn-add-to-cart-desktop');
if (btnAddToCartDesktop) {
const newBtnDesktop = btnAddToCartDesktop.cloneNode(true);
btnAddToCartDesktop.parentNode.replaceChild(newBtnDesktop, btnAddToCartDesktop);
newBtnDesktop.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
addToCart();
});
}
// Mobile button
const btnAddToCartMobile = document.getElementById('btn-add-to-cart-mobile');
if (btnAddToCartMobile) {
const newBtnMobile = btnAddToCartMobile.cloneNode(true);
btnAddToCartMobile.parentNode.replaceChild(newBtnMobile, btnAddToCartMobile);
newBtnMobile.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
addToCart();
});
}
}
// Chỉ init một lần để tránh duplicate
let addToCartInitialized = false;
function tryInitAddToCart() {
if (addToCartInitialized) return;
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
initAddToCartButton();
addToCartInitialized = true;
});
} else {
initAddToCartButton();
addToCartInitialized = true;
}
}
tryInitAddToCart();
// Hỗ trợ Liferay events - chỉ init một lần
if (typeof Liferay !== 'undefined') {
if (Liferay.on && !addToCartInitialized) {
Liferay.on('allPortletsReady', function () {
if (!addToCartInitialized) {
setTimeout(function () {
initAddToCartButton();
addToCartInitialized = true;
}, 500);
}
});
}
}
})();
' + escapeHtml(title) + '
' + 'Hiệu lực từ: ' + formatDate(displayDate) + (expirationDate ? ' · Hết hạn: ' + formatDate(expirationDate) : '') + '
' + '' +
'' +
'
' +
'Đảm bảo chất lượng
Giao hàng nhanh
Đóng gói cẩn thận
Thông số kỹ thuật
Thông tin chi tiết về sản phẩm
Đang tải thông số kỹ thuật...
Không thể tải thông số kỹ thuật
Đang tải...
Voucher của cửa hàng
Chọn voucher và nhấn Lưu để dùng khi thanh toán
Chưa có voucher nào cho
cửa hàng này.
Đánh giá sản phẩm
0
Dựa trên 0 đánh giá
Đánh giá sản phẩm
0
Dựa trên 0 đánh giá