623 lines
25 KiB
JavaScript
623 lines
25 KiB
JavaScript
class ProductQuickView {
|
||
constructor() {
|
||
this.product = null;
|
||
this.size = null;
|
||
this.idmProductId = null;
|
||
this.basketSubmitted = false;
|
||
this.htmlQuickView = '';
|
||
this.htmlEl = document.querySelector('html');
|
||
}
|
||
|
||
// Escapes HTML to prevent XSS in outputs
|
||
escapeHtml(v = '') {
|
||
return String(v)
|
||
.replace(/&/g, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>')
|
||
.replace(/"/g, '"')
|
||
.replace(/'/g, '');
|
||
}
|
||
|
||
// Main listener to set up product quick view and handlers after DOM load
|
||
init = () => {
|
||
const self = this;
|
||
document.addEventListener("DOMContentLoaded", async () => {
|
||
const productImgLinks = document.querySelectorAll('#search .product');
|
||
const prodInfoContainer = document.querySelector('.prod-info__container');
|
||
const asideProdMenu = document.querySelector('.right-aside');
|
||
const customAdded = document.getElementById('quick_view_basket');
|
||
productImgLinks.forEach(prod => this.initProductCard(prod, prodInfoContainer, asideProdMenu, customAdded));
|
||
this.renderProduct(asideProdMenu, prodInfoContainer, customAdded);
|
||
});
|
||
}
|
||
|
||
// Add quick view card buttons, setup close listeners
|
||
initProductCard(prod, prodInfoContainer, asideProdMenu, customAdded) {
|
||
const cardPictureContainer = document.createElement('div');
|
||
if (!prod.querySelector('.hover-btn') && !prod.querySelector('.card__picture-container')) {
|
||
cardPictureContainer.className = 'card__picture-container';
|
||
prod.insertAdjacentElement('afterbegin', cardPictureContainer);
|
||
|
||
const productContentWrapper = prod.querySelector('.product__content_wrapper');
|
||
const closeRightAsideBtn = document.querySelector('.right-aside__close');
|
||
const closeRightAsideBlur = document.querySelector('.right-aside__bg-blur');
|
||
const continueShopping = document.querySelector('.added__button.--close');
|
||
|
||
continueShopping.addEventListener('click', () => this.closeRightAside(prodInfoContainer, asideProdMenu));
|
||
closeRightAsideBtn.addEventListener("click", () => this.closeRightAside(prodInfoContainer, asideProdMenu));
|
||
closeRightAsideBlur.addEventListener("click", () => this.closeRightAside(prodInfoContainer, asideProdMenu));
|
||
|
||
const btn = document.createElement('button');
|
||
btn.className = 'hover-btn';
|
||
btn.textContent = 'CHOOSE OPTION';
|
||
prod.insertAdjacentElement('afterbegin', btn);
|
||
|
||
// Add button for mobile card
|
||
if (productContentWrapper && productContentWrapper.parentElement) {
|
||
const mobileBtn = btn.cloneNode(true);
|
||
productContentWrapper.insertAdjacentElement('afterend', mobileBtn);
|
||
}
|
||
}
|
||
// Attach anchors and buttons to quick view container
|
||
const link = prod.querySelector('a');
|
||
const button = prod.querySelector('button');
|
||
if (link) cardPictureContainer.appendChild(link);
|
||
if (button) cardPictureContainer.appendChild(button);
|
||
}
|
||
|
||
// Animate/Close quick view sidebar, handle basket state
|
||
closeRightAside(prodInfoContainer, asideProdMenu) {
|
||
asideProdMenu.classList.remove('active');
|
||
setTimeout(() => {
|
||
this.htmlEl.classList.remove('activeAside');
|
||
}, 300);
|
||
if (this.basketSubmitted) {
|
||
const rightAsideProdInfo = document.querySelector('.right-aside__prod-info');
|
||
const navRightAside = document.querySelector('.right-aside__nav');
|
||
const customAdded = document.getElementById('quick_view_basket');
|
||
rightAsideProdInfo.style.position = 'fixed';
|
||
navRightAside.style.position = 'static';
|
||
this.basketSubmitted = false;
|
||
prodInfoContainer.classList.remove("hidden");
|
||
customAdded.classList.add("hidden");
|
||
}
|
||
}
|
||
|
||
// Set up main product quick view handler on search area
|
||
renderProduct(asideProdMenu, prodInfoContainer, customAdded) {
|
||
document.getElementById("search")?.addEventListener("click", async (e) => {
|
||
const hoverBtn = e.target.closest(".hover-btn");
|
||
if (!hoverBtn) return;
|
||
e.stopPropagation();
|
||
this.htmlEl.classList.add("activeAside");
|
||
asideProdMenu.classList.add("active");
|
||
if (prodInfoContainer.hasChildNodes()) {
|
||
prodInfoContainer.innerHTML = "";
|
||
}
|
||
try {
|
||
this.idmProductId = Number(e.target.closest(".product").dataset.product_id);
|
||
await this.fetchQuickViewProduct(this.idmProductId, prodInfoContainer, customAdded);
|
||
} catch (err) {
|
||
console.error(err);
|
||
}
|
||
// Wait for dynamic HTML elements before binding option listeners
|
||
const interval2 = setInterval(() => {
|
||
const sizesContainer = document.querySelector('.idm-sizes');
|
||
const versionsContainer = document.querySelector('.idm-versions');
|
||
if (sizesContainer && versionsContainer) {
|
||
clearInterval(interval2);
|
||
this.bindOption(prodInfoContainer, sizesContainer, 'currentSize', this.product.sizes, 'currentSize', 'size-choose', 'size', true);
|
||
this.bindOption(prodInfoContainer, versionsContainer, 'currentVersion', this.product.group.versions, 'currentVersion', 'version-choose', 'product', false);
|
||
}
|
||
}, 100);
|
||
});
|
||
}
|
||
|
||
// Fetch product data and build modal quick view HTML
|
||
async fetchQuickViewProduct(idmProductId, prodInfoContainer, customAdded){
|
||
const query = `
|
||
query {
|
||
product(productId: ${idmProductId}) {
|
||
product{
|
||
id
|
||
name
|
||
link
|
||
icon
|
||
enclosuresImages{ url }
|
||
producer { id name link }
|
||
sizes {
|
||
id name code amount
|
||
price {
|
||
omnibusPrice {gross {formatted}}
|
||
crossedPrice {gross {formatted}}
|
||
beforeRebate {gross {formatted}}
|
||
beforeRebateDetails {youSavePercent}
|
||
price {gross {formatted}}
|
||
}
|
||
availability{description}
|
||
}
|
||
group{
|
||
id
|
||
versions{
|
||
id name productIcon link iconSmall
|
||
}
|
||
}
|
||
price {
|
||
price { gross { formatted } }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
`;
|
||
const response = await fetch("/graphql/v1/", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ query }),
|
||
});
|
||
const data = await response.json();
|
||
this.product = data.data.product.product;
|
||
this.size = this.product?.sizes?.[0];
|
||
prodInfoContainer.innerHTML = "";
|
||
this.htmlQuickView = `
|
||
<div class="img__container">
|
||
<img src="${
|
||
this.product?.enclosuresImages?.[0]?.url
|
||
? this.product.enclosuresImages[0].url
|
||
: this.product.icon
|
||
}" />
|
||
</div>
|
||
<div class="product-info__part">
|
||
<div class="names-info">
|
||
<p class="producer-name">${this.product.producer.name}</p>
|
||
<h5 class="product-name">${this.product.name}</h5>
|
||
</div>
|
||
<div class="price-info">
|
||
<div class="price__container">
|
||
<h5 class="idm__quick-view-price">
|
||
${this.product?.sizes?.[0]?.price?.price?.gross?.formatted}
|
||
${
|
||
this.product?.sizes?.[0]?.price?.crossedPrice?.gross?.formatted
|
||
? `<span class="price-crossed">${this.product?.sizes?.[0]?.price?.crossedPrice?.gross?.formatted}</span>`
|
||
: ""
|
||
}
|
||
<span class="price-note">brutto / szt</span>
|
||
</h5>
|
||
<span class="price-omnibus">
|
||
${this.product?.sizes?.[0]?.price?.omnibusPrice?.gross?.formatted
|
||
? `Najniższa cena z 30 dni przed obniżką: ${this.product?.sizes?.[0]?.price?.omnibusPrice?.gross?.formatted}`
|
||
: ""}
|
||
</span>
|
||
</div>
|
||
<div class="availability__container">
|
||
${this.size.amount > 3 ?
|
||
`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="10" fill="#C9EAD4"/>
|
||
<circle cx="12" cy="12" r="8" fill="#10A443"/>
|
||
</svg>
|
||
`
|
||
: this.size.amount > 0 ?
|
||
`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="10" fill="#FFE8BF"/>
|
||
<circle cx="12" cy="12" r="8" fill="#D28C13"/>
|
||
</svg>
|
||
`
|
||
: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="10" fill="#FFBEC7"/>
|
||
<circle cx="12" cy="12" r="8" fill="#ED001F"/>
|
||
</svg>
|
||
`
|
||
}
|
||
<p class="availability">
|
||
${this.product?.sizes?.[0]?.availability?.description}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="all-sizes__container">
|
||
<div class="idm-sizes__container">
|
||
${
|
||
this.product.sizes && this.product.sizes.length > 1
|
||
? `<input type="hidden" name="currentSize" value="">
|
||
<p>Size: <span class="size-choose">${this.product.sizes[0].name}</span></p>` +
|
||
'<div class="idm-sizes">' +
|
||
this.product.sizes
|
||
.filter((s) => s.id !== "uniw")
|
||
.map((s, i) =>
|
||
`<button data-id="${s.id}" class="product-size${i === 0 ? ' active' : ''}">${s.name}</button>`
|
||
)
|
||
.join("") +
|
||
'</div>'
|
||
: ""
|
||
}
|
||
</div>
|
||
<div class="idm-versions__container">
|
||
${
|
||
this.product.group.versions && this.product.group.versions.length > 1
|
||
? `<input type="hidden" name="currentVersion" value="">
|
||
<p>Type: <span class="version-choose">${this.product.group.versions.filter((s)=>s.id === this.product.id)?.[0]?.name}</span></p>` +
|
||
'<div class="idm-versions">' +
|
||
this.product.group.versions
|
||
.filter((s) => s.id !== "uniw")
|
||
.sort((a, b) => a.name.localeCompare(b.name))
|
||
.map((s, i) =>
|
||
`<button data-id="${s.id}" class="product-version${this.product.id === s.id ? ' active' : ''}"><img src="${s.iconSmall}"></button>`
|
||
)
|
||
.join("") +
|
||
'</div>'
|
||
: ""
|
||
}
|
||
</div>
|
||
</div>
|
||
<div class='idm-products-banner__add-to-basket'>
|
||
${this.idmAddToBasket(this.product, this.size)}
|
||
</div>
|
||
<div class="view-details__container">
|
||
<a class="view-details" href="${this.product.link}">View Details</a>
|
||
</div>
|
||
</div>
|
||
`;
|
||
prodInfoContainer.insertAdjacentHTML("afterbegin", this.htmlQuickView);
|
||
// Button state may depend on async HTML render - poll for element
|
||
// const interval2 = setInterval(() => {
|
||
// const addToBasketButton = document.querySelector(
|
||
// ".btn.--solid.--medium.idm-products-banner__add-to-basket-button"
|
||
// );
|
||
// if (addToBasketButton) {
|
||
// clearInterval(interval2);
|
||
// if (!this.size.amount) {
|
||
// addToBasketButton.classList.add("inactive");
|
||
// } else {
|
||
// addToBasketButton.classList.remove("inactive");
|
||
// }
|
||
// }
|
||
// }, 100);
|
||
if (!this.product) throw new Error("err");
|
||
return this.product;
|
||
}
|
||
// Generic binding for size/version choices, handles UI and fetches updates as needed
|
||
bindOption(prodInfoContainer, container, inputName, collection, productProp, labelClass, basketHiddenInput, updatePrice = false) {
|
||
prodInfoContainer.addEventListener('click', async (e) => {
|
||
const itemEl = e.target.closest('button');
|
||
if (!itemEl || !container.contains(itemEl)) return;
|
||
container.querySelectorAll('button.active').forEach(btn => btn.classList.remove('active'));
|
||
itemEl.classList.add('active');
|
||
const itemId = itemEl.dataset.id;
|
||
let itemObj = collection.find(item => String(item.id) === String(itemId));
|
||
this.product[productProp] = itemObj;
|
||
const labelEl = container.closest('.product-info__part').querySelector(`.${labelClass}`);
|
||
if (labelEl) labelEl.textContent = itemEl.textContent || itemObj.name;
|
||
const hiddenInput = container.closest('.product-info__part')?.querySelector(`input[name="${inputName}"]`);
|
||
if (hiddenInput) hiddenInput.value = this.escapeHtml(itemObj.id);
|
||
const hiddenBasketInputEl = document.querySelector('.idm-products-banner__add-to-basket-form')
|
||
?.querySelector(`input[name="${basketHiddenInput}"]`);
|
||
if (hiddenBasketInputEl) hiddenBasketInputEl.value = this.escapeHtml(itemObj.id);
|
||
const quickViewPriceEl = container.closest('.product-info__part').querySelector('.idm__quick-view-price');
|
||
const quickViewOmnibusEl = container.closest('.product-info__part').querySelector('.price-omnibus');
|
||
if (!updatePrice) {
|
||
// When switching product version, fetch new product data and re-bind
|
||
const idmProductId = Number(itemId);
|
||
const newProduct = await this.fetchQuickViewProduct(idmProductId, prodInfoContainer);
|
||
this.bindOption(
|
||
prodInfoContainer,
|
||
prodInfoContainer.querySelector('.idm-versions'),
|
||
'currentVersion',
|
||
newProduct.group.versions,
|
||
'currentVersion',
|
||
'version-choose',
|
||
'product',
|
||
false
|
||
);
|
||
this.bindOption(
|
||
prodInfoContainer,
|
||
prodInfoContainer.querySelector('.idm-sizes'),
|
||
'currentSize',
|
||
newProduct.sizes,
|
||
'currentSize',
|
||
'size-choose',
|
||
'size',
|
||
true
|
||
);
|
||
return;
|
||
}
|
||
|
||
// Fast price info update just for size pick
|
||
if (updatePrice && itemObj.price && quickViewPriceEl && quickViewOmnibusEl) {
|
||
const crossedPriceVal = itemObj.price.crossedPrice?.gross?.formatted;
|
||
const regularPriceVal = itemObj.price.price?.gross?.formatted;
|
||
const omnibusPriceVal = itemObj.price.omnibusPrice?.gross?.formatted;
|
||
const wrapper = document.querySelector('.idm-products-banner__qty');
|
||
this.size = itemObj
|
||
this.idmAddToBasket(this.product, this.size)
|
||
this.amountToBasket(wrapper)
|
||
quickViewPriceEl.innerHTML = `
|
||
${regularPriceVal}
|
||
${crossedPriceVal ? `<span class="price-crossed">${crossedPriceVal}</span> ` : ""}
|
||
<span class="price-note">brutto / szt</span>
|
||
`;
|
||
quickViewOmnibusEl.innerHTML = omnibusPriceVal
|
||
? `Najniższa cena z 30 dni przed obniżką: ${omnibusPriceVal}`
|
||
: "";
|
||
}
|
||
});
|
||
}
|
||
|
||
// Handles add-to-basket GraphQL mutation and in-app UI transitions
|
||
addToBasketQuery() {
|
||
const addToBasketButton = document.querySelector(
|
||
".btn.--solid.--medium.idm-products-banner__add-to-basket-button"
|
||
);
|
||
const customAdded = document.getElementById('quick_view_basket');
|
||
const prodInfoContainer = document.querySelector('.prod-info__container');
|
||
const addToBasketForm = document.querySelector(".idm-products-banner__add-to-basket-form");
|
||
addToBasketForm.addEventListener("submit", async (el) => {
|
||
el.preventDefault();
|
||
const qtyElement = document.querySelector('.idm-products-banner__qty-input');
|
||
const sellBy = qtyElement.value;
|
||
try {
|
||
const query = `
|
||
mutation {
|
||
addProductsToBasket(ProductInput: {
|
||
id: ${Number(this.product.id)},
|
||
size: "${this.size.id}",
|
||
quantity: ${Number(sellBy)}
|
||
}) {
|
||
status
|
||
}
|
||
}
|
||
`;
|
||
const res = await fetch("/graphql/v1/", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ query }),
|
||
});
|
||
if (!res.ok && res.status !== 301 && res.status !== 302) {
|
||
throw new Error(`Błąd HTTP: ${res.status}`);
|
||
}
|
||
const result = await res.json();
|
||
const status = result?.data?.addProductsToBasket?.status;
|
||
if (status && (status.toLowerCase() === 'success' || status === 'SUCCESS')) {
|
||
this.updateBasketAfterAdd();
|
||
} else {
|
||
throw new Error(`Basket update failed — status = ${status}`);
|
||
}
|
||
await this.renderCartItemsJS();
|
||
} catch (error) {
|
||
if (typeof Alertek !== 'undefined') {
|
||
Alertek.show_alert("Coś poszło nie tak");
|
||
} else {
|
||
alert("Coś poszło nie tak");
|
||
}
|
||
}
|
||
|
||
// Hide quickview and show confirmation after adding
|
||
const rightAsideProdInfo = document.querySelector('.right-aside__prod-info');
|
||
const navRightAside = document.querySelector('.right-aside__nav');
|
||
navRightAside.style.position = 'absolute';
|
||
prodInfoContainer.classList.add("hidden");
|
||
customAdded.classList.remove("hidden");
|
||
this.basketSubmitted = true;
|
||
await this.getCartItems();
|
||
return true;
|
||
});
|
||
return false;
|
||
}
|
||
|
||
updateBasketAfterAdd() {
|
||
if (typeof menu_basket_cache === 'function') {
|
||
menu_basket_cache(false);
|
||
} else {
|
||
console.warn("menu_basket_cache is not defined.");
|
||
}
|
||
}
|
||
|
||
// Dynamically render add-to-basket UI form and connect handlers
|
||
idmAddToBasket(product, size) {
|
||
const getElAmmountInCart = app_shop.vars.basket;
|
||
|
||
const getThisProduct = getElAmmountInCart.products.filter(
|
||
(s) => s.id === String(this.product.id) && s.size === String(this.size.id)
|
||
);
|
||
|
||
|
||
const sellBy = size?.unitSellby || 1;
|
||
const precision = size?.unitPrecision || 0;
|
||
let max = (typeof size?.amount === 'number' && size.amount > 0) ? size.amount : '';
|
||
const amountInCart = getThisProduct?.[0]?.count ? getThisProduct[0].count : 0;
|
||
|
||
let inactive = ""
|
||
if (amountInCart && amountInCart >= max){
|
||
inactive = "inactive";
|
||
max = '';
|
||
}else if(amountInCart){
|
||
max -= amountInCart;
|
||
}
|
||
|
||
console.log("getThisProduct", size.amount, "max", max)
|
||
|
||
const buttonsToRender = `
|
||
<form class='idm-products-banner__add-to-basket-form' method='post' action="/basketchange.php">
|
||
<input type='hidden' name='mode' value='1'>
|
||
<input type='hidden' name='product' value=${product.id}>
|
||
<input type='hidden' name='size' value=${size.id}>
|
||
<div class='idm-products-banner__qty'
|
||
data-sell-by='${this.escapeHtml(String(sellBy))}'
|
||
data-precision='${this.escapeHtml(String(precision))}'
|
||
data-max='${this.escapeHtml(String(max))}'>
|
||
<button type='button' class='idm-products-banner__qty-decrease'>−</button>
|
||
<input type='number'
|
||
name='number'
|
||
class='idm-products-banner__qty-input'
|
||
value='${this.escapeHtml(String(sellBy))}'
|
||
step='${this.escapeHtml(String(sellBy))}'
|
||
min='${this.escapeHtml(String(sellBy))}'
|
||
max='${this.escapeHtml(String(max))}'>
|
||
<button type='button' class='idm-products-banner__qty-increase'>+</button>
|
||
</div>
|
||
<button type='submit' class='btn --solid --medium idm-products-banner__add-to-basket-button ${inactive}'>
|
||
<span>Do koszyka</span>
|
||
</button>
|
||
</form>`;
|
||
|
||
// Wait for container before rendering/attaching qty logic
|
||
const interval = setInterval(() => {
|
||
const container = document.querySelector('.idm-products-banner__add-to-basket');
|
||
if (container) {
|
||
clearInterval(interval);
|
||
container.innerHTML = buttonsToRender;
|
||
this.addToBasketQuery();
|
||
}
|
||
}, 100);
|
||
const interval2 = setInterval(() => {
|
||
const wrapper = document.querySelector('.idm-products-banner__qty');
|
||
if (wrapper) {
|
||
clearInterval(interval2);
|
||
this.amountToBasket(wrapper, max);
|
||
}
|
||
}, 100);
|
||
return '';
|
||
}
|
||
|
||
// Quantity +/- and validation for add to basket
|
||
amountToBasket(wrapper, max){
|
||
wrapper.addEventListener('click', async (e) => {
|
||
if (!wrapper) return;
|
||
|
||
const input = wrapper.querySelector('.idm-products-banner__qty-input');
|
||
const step = parseFloat(wrapper.dataset.sellBy || '1');
|
||
const precision = parseInt(wrapper.dataset.precision || '0');
|
||
max = parseFloat(max || '999999');
|
||
|
||
let current = parseFloat(input.value) || 0;
|
||
if (e.target.classList.contains('idm-products-banner__qty-increase')) {
|
||
current += step;
|
||
if (current > max) current = max;
|
||
} else if (e.target.classList.contains('idm-products-banner__qty-decrease')) {
|
||
current -= step;
|
||
if (current < step) current = step;
|
||
}
|
||
input.value = current.toFixed(precision);
|
||
});
|
||
document.addEventListener('blur', (e) => {
|
||
if (!e.target.classList.contains('idm-products-banner__qty-input')) return;
|
||
const input = e.target;
|
||
const wrapper = input.closest('.idm-products-banner__qty');
|
||
const step = parseFloat(wrapper.dataset.sellBy || '1');
|
||
const precision = parseInt(wrapper.dataset.precision || '0');
|
||
const max = parseFloat(wrapper.dataset.max || '999999');
|
||
let val = parseFloat(input.value);
|
||
if (isNaN(val) || val < step) {
|
||
val = step;
|
||
} else if (val > max) {
|
||
val = max;
|
||
} else {
|
||
val = Math.round(val / step) * step;
|
||
}
|
||
input.value = val.toFixed(precision);
|
||
}, true);
|
||
}
|
||
|
||
// Returns after basket global variable is updated, for UI sync
|
||
getCartItems() {
|
||
const cartItems = app_shop.vars.basket;
|
||
return new Promise((resolve) => {
|
||
const interval = setInterval(() => {
|
||
const newCartItems = app_shop.vars.basket;
|
||
if (cartItems.productsNumber !== newCartItems.productsNumber) {
|
||
clearInterval(interval)
|
||
resolve(newCartItems);
|
||
}
|
||
}, 100);
|
||
});
|
||
}
|
||
|
||
// Rebuilds basket preview after add
|
||
async renderCartItemsJS() {
|
||
const container = document.getElementById('cartItemsContainer');
|
||
container.innerHTML = '';
|
||
const products = (await this.getCartItems()).products;
|
||
|
||
products.forEach(product => {
|
||
const {
|
||
id,
|
||
link,
|
||
name,
|
||
icon,
|
||
size_name,
|
||
count,
|
||
price_unit,
|
||
prices
|
||
} = product;
|
||
const block = document.createElement('div');
|
||
block.className = 'added__block';
|
||
block.innerHTML = `
|
||
<div class="added__product product">
|
||
<a class="added__icon product__icon m-0 d-flex justify-content-center align-items-center"
|
||
data-product-id="${id}" href="${link}" title="${name}">
|
||
<img class="b-lazy" src="${icon}" alt="${name}">
|
||
</a>
|
||
<div class="added__info">
|
||
<h3 class="added__name_h3 d-flex">
|
||
<a class="added__name" href="${link}" title="${name}">
|
||
${name}
|
||
</a>
|
||
</h3>
|
||
<div class="added__prices">
|
||
<div class="added__price_single">
|
||
${Number(prices.gros).toFixed(2).replace('.', ',')} zł
|
||
<span class="added__price_sellby">/ ${price_unit}</span>
|
||
</div>
|
||
<strong class="price">
|
||
<span class="price__unit">
|
||
${Number(prices.worth_gros).toFixed(2).replace('.', ',')} zł
|
||
</span>
|
||
<span class="price_vat">brutto</span>
|
||
</strong>
|
||
</div>
|
||
<ul class="added__params">
|
||
<li class="added__params_number">Ilość: ${count}</li>
|
||
${size_name && size_name !== 'UNIWERSALNY' ? `
|
||
<li class="added__params_size">Rozmiar: ${size_name}</li>
|
||
` : ''}
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
`;
|
||
this.updateShippingProgressBar()
|
||
container.appendChild(block);
|
||
});
|
||
}
|
||
|
||
// Updates shipping progress bar UI according to basket total
|
||
updateShippingProgressBar() {
|
||
const basket = app_shop.vars.basket;
|
||
if (!basket || !basket.shippingLimitFree) return;
|
||
const totalWorth = parseFloat(basket.worth || 0);
|
||
const shippingLimit = parseFloat(basket.shippingLimitFree || 0);
|
||
const toShippingFree = parseFloat(basket.toShippingFree || 0);
|
||
const toShippingFreeFormatted = basket.toShippingFree_formatted || "0,00 zł";
|
||
const barEl = document.getElementById("shipping-bar");
|
||
const barCompletion = document.getElementById("shipping-bar-completion");
|
||
const amountEl = document.getElementById("shipping-left-amount");
|
||
const freeEl = document.getElementById("shipping-bar-free");
|
||
const notFreeText = document.getElementById("shipping-text-not-free");
|
||
if (shippingLimit > 0 && toShippingFree > 0) {
|
||
const percentage = Math.min(
|
||
100,
|
||
((shippingLimit - toShippingFree) / shippingLimit) * 100
|
||
);
|
||
barCompletion.style.width = percentage + "%";
|
||
amountEl.textContent = toShippingFreeFormatted;
|
||
barEl.style.display = "block";
|
||
freeEl.style.display = "none";
|
||
} else {
|
||
const shippingfreeBar = document.getElementById('shipping-bar-free').querySelector('.added__shippingfree_bar');
|
||
shippingfreeBar.classList.add('enough');
|
||
barEl.style.display = "none";
|
||
freeEl.style.display = "block";
|
||
}
|
||
}
|
||
}
|
||
|
||
// Initialize quick view system
|
||
const quickView = new ProductQuickView();
|
||
quickView.init();
|