From 7f3edddbc89c1049e06ef1f76fac4c1d095861ed Mon Sep 17 00:00:00 2001 From: Mykola Zahorulko Date: Thu, 14 Aug 2025 10:05:32 +0200 Subject: [PATCH] Add product banner feature --- slick/index.xslt | 43 ++++ slick/src/main.js | 489 ++++++++++++++++++++++++++++++++++++++++ slick/src/style.less | 290 ++++++++++++++++++++++++ swiper/index.xslt | 49 ++++ swiper/src/main.js | 507 ++++++++++++++++++++++++++++++++++++++++++ swiper/src/style.less | 305 +++++++++++++++++++++++++ 6 files changed, 1683 insertions(+) create mode 100644 slick/index.xslt create mode 100644 slick/src/main.js create mode 100644 slick/src/style.less create mode 100644 swiper/index.xslt create mode 100644 swiper/src/main.js create mode 100644 swiper/src/style.less diff --git a/slick/index.xslt b/slick/index.xslt new file mode 100644 index 0000000..df086c2 --- /dev/null +++ b/slick/index.xslt @@ -0,0 +1,43 @@ + + + +
+
+

+

+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
diff --git a/slick/src/main.js b/slick/src/main.js new file mode 100644 index 0000000..7b792e2 --- /dev/null +++ b/slick/src/main.js @@ -0,0 +1,489 @@ +/////////////////////////////////////////////////////////// +// dodatek po stronie panelu +/////////////////////////////////////////////////////////// + +const tytul = "TYTUL"; +const tekst = "TEKST"; +const produkty = [173632, 75469, 85452, 25065, 4147]; + +window.idmInteractiveBannerObj = { + idmInteractiveBannerIds: typeof produkty !== "undefined" ? produkty : null, + idmInteractiveBannerTitle: typeof tytul !== "undefined" ? tytul : "", + idmInteractiveBannerText: typeof tekst !== "undefined" ? tekst : "", +}; + +/////////////////////////////////////////////////////////// +// pobranie zmiennych, obiekty mapujące +/////////////////////////////////////////////////////////// + +const idmInteractiveBannerIds = window.idmInteractiveBannerObj?.idmInteractiveBannerIds || null; +const idmInteractiveBannerTitle = window.idmInteractiveBannerObj?.idmInteractiveBannerTitle || ""; +const idmInteractiveBannerText = window.idmInteractiveBannerObj?.idmInteractiveBannerText || ""; + +const IDM_ZONE_TEXT_MAP = { + promotion: `${}`, + discount: `${}`, + bestseller: `${}`, + new: `${}`, +}; +const IDM_ZONE_CLASS_MAP = { + promotion: "idm-products-banner__label--promo", + discount: "idm-products-banner__label--promo", + bestseller: "idm-products-banner__label--bestseller", + new: "idm-products-banner__label--new", +}; + +/////////////////////////////////////////////////////////// +// ustawienie elementów headera +/////////////////////////////////////////////////////////// + +function idmSetBannerTitle(titleElement) { + if (idmInteractiveBannerTitle) { + titleElement.textContent = idmInteractiveBannerTitle; + } +} + +function idmSetBannerText(textElement) { + if (idmInteractiveBannerText) { + textElement.textContent = idmInteractiveBannerText; + } +} + +/////////////////////////////////////////////////////////// +// ulubione +/////////////////////////////////////////////////////////// + +// uwaga, trzeba odkryć komponent w układzie Lista zakupowa - Javascript + CSS + XSLT +function idmAttachFavoritesEvents() { + document.querySelectorAll(".idm-products-banner__favorites-icon").forEach(el => { + el.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + const icon = e.target.closest(".idm-products-banner__favorites-icon"); + app_shop.fn.shoppingList.addProductToList([ + [icon.getAttribute("data-id"), icon.getAttribute("data-size")] + ]); + }); + }); +} + +/////////////////////////////////////////////////////////// +// dodanie do porównania +/////////////////////////////////////////////////////////// + +function idmHandleAddToCompare(e) { + const compareBtnEl = e.target.closest(".idm-products-banner__compare-btn"); + if (!compareBtnEl || !compareBtnEl?.dataset?.compareId) return; + + e.preventDefault(); + idmAddToCompare(compareBtnEl, compareBtnEl.dataset.compareId); +} + +async function idmAddToCompare(btnEl, compareId) { + try { + btnEl.classList.add("--loading"); + const compareUrl = `/${app_shop?.vars?.language?.symbol || "de"}/settings.html?comparers=add&product=${compareId}`; + + const res = await fetch(compareUrl); + console.log(res); + if (!res.ok) throw new Error(`${}`); + + btnEl.classList.add("--success"); + + const compareContainerQuery = "#menu_compare_product"; + if (document.querySelector(compareContainerQuery)) { + app_shop.fn.load( + window.location.pathname, + [[compareContainerQuery, compareContainerQuery]], + function () {}, + "?set_render=content" + ); + } + + setTimeout(() => { + btnEl.classList.remove("--success"); + }, 2000); + } catch (err) { + console.error(err); + Alertek.Error(`${}`); + } finally { + btnEl.classList.remove("--loading"); + } +} + +/////////////////////////////////////////////////////////// +// produkty +/////////////////////////////////////////////////////////// + +function idmGetGraphQLQuery(ids) { + return JSON.stringify({ + query: `{ + products(searchInput: {productsId: ${JSON.stringify(ids)}}, settingsInput: {limit: 50}) { + products { + id + type + name + zones + icon + link + sizes { id amount } + awardedParameters { name values { name } } + price { + rebateCodeActive + price { gross { value formatted } } + omnibusPrice { gross { value formatted } } + max { gross { value formatted } } + beforeRebate { gross { value formatted } } + } + unit { + id + name + sellBy + } + opinion { rating count } + } + } + }` + }); +} + +// helpery +const escapeHtml = (v = "") => + String(v) + .replace(/&/g, "&").replace(//g, ">").replace(/"/g, """) + .replace(/'/g, "'"); + +const slot = (cond, html) => (cond ? html : ""); + +// label +function idmMarkupZoneLabel(zone) { + if (!zone) return ""; + return ` + + + ${IDM_ZONE_TEXT_MAP[zone] || ""} + + `; +} + +// porównanie +function idmMarkupCompareBtn(productId, options) { + if (!options?.compare) return ""; + return ` +
+ +
`; +} + +// ulubione +function idmMarkupFavoritesIcon(productId, sizeId, options) { + if (!options?.favorites) return ""; + return ` +
+ + + +
`; +} + +// ikona +function idmMarkupImage(link, icon) { + return ` + + + `; +} + +// nazwa +function idmMarkupName(name, link) { + return ` + `; +} + +// ceny +function idmMarkupPrices(priceObj) { + const current = priceObj?.price?.gross?.formatted || ""; + const max = priceObj?.max?.gross?.formatted || ""; + const hasOmnibus = priceObj?.omnibusPrice?.gross?.formatted; + + let mainPrice = ""; + if (current && max && max !== current) { + mainPrice = `${escapeHtml(current)} ${escapeHtml(max)}`; + } else if (current) { + mainPrice = escapeHtml(current); + } else if (max) { + mainPrice = `${escapeHtml(max)}`; + } + + const oldPrice = hasOmnibus + ? `${} ${escapeHtml(current || max)}` + : ""; + + return ` +
+
+
+ ${mainPrice} +

${oldPrice}

+
+
+
`; +} + +// dodawanie do koszyka +function idmMarkupAddToBasket(product, size, options) { + if (!options?.addToBasket) return ""; + const can = product?.type === "product" && product?.sizes?.length === 1 && size; + if (!can) return ""; + + const sellBy = size?.unitSellby || 1; + const precision = size?.unitPrecision || 0; + const max = (typeof size?.amount === "number" && size.amount > 0) ? size.amount : ""; + + return ` +
+
+ + + + +
+ + + + + +
+ + +
+
`; +} + +// link więcej opcji +function idmMarkupMoreOptions(link) { + return ` + `; +} + +// assembler produktu +function idmRenderProductCard(product, options = {}) { + const zone = product?.zones?.[0]; + const size = product?.sizes?.[0]; + + const headIcons = ` + ${idmMarkupCompareBtn(product.id, options)} + ${idmMarkupFavoritesIcon(product.id, size?.id, options)} + ${idmMarkupZoneLabel(zone)} + `; + + const addSection = + (options?.addToBasket && product?.type === "product" && product?.sizes?.length === 1 && size) + ? idmMarkupAddToBasket(product, size, options) + : idmMarkupMoreOptions(product.link); + + return ` +
+
+ ${headIcons} + ${idmMarkupImage(product.link, product.icon)} +
+ +
+ ${idmMarkupName(product.name, product.link)} + ${idmMarkupPrices(product.price)} + ${addSection} +
+
`; +} + +// tworzenie, ustawienie opcji +function idmCreateProductMarkup(products, options = { compare: true, favorites: true, addToBasket: true }) { + return products.map(p => idmRenderProductCard(p, options)).join(""); +} + +async function idmRenderBannerProducts(container) { + const skeletons = document.querySelector(".idm-products-banner__skeletons"); + + if (!idmInteractiveBannerIds) return; + + try { + container.innerHTML = ""; + + const response = await fetch("/graphql/v1/", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: idmGetGraphQLQuery(idmInteractiveBannerIds), + }); + + const json = await response.json(); + const productsData = json?.data?.products?.products; + + console.log(productsData); + + if (!productsData) return; + + const sorted = idmInteractiveBannerIds + .map(id => productsData.find(p => p.id === id)) + .filter(Boolean); + + container.innerHTML = idmCreateProductMarkup(sorted); + idmInitSlickSlider(container); + idmAttachFavoritesEvents(); + + // +/- i walidacja inputów ilości + document.addEventListener("click", (e) => { + const wrapper = e.target.closest(".idm-products-banner__qty"); + 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"); + const max = parseFloat(wrapper.dataset.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); + + // ukrycie skeletona + skeletons?.classList.add("idm_hidden"); + container.classList.remove("idm_hidden"); + + // event porównania (delegacja) + document.addEventListener("click", idmHandleAddToCompare); + } catch (err) { + console.error("Błąd pobierania produktów:", err); + } +} + +/////////////////////////////////////////////////////////// +// slider i wyrównanie wysokości +/////////////////////////////////////////////////////////// + +function idmInitSlickSlider(container) { + $(container).on("init", function () { + app_shop.fn.setHeight({ + selectors: [ + ".idm-products-banner .idm-products-banner__product-img-container", + ".idm-products-banner .idm-products-banner__product-name", + ".idm-products-banner .idm-products-banner__price", + ".idm-products-banner .idm-products-banner__price-omnibus", + ".idm-products-banner .idm-products-banner__add-to-basket", + ".idm-products-banner .product" + ], + container: ".idm-products-banner__products .slick-track" + }); + }); + + $(container).slick({ + slidesToShow: 2, + slidesToScroll: 1, + autoplay: false, + dots: false, + arrows: true, + prevArrow: '', + nextArrow: '', + responsive: [ + { breakpoint: 979, settings: { slidesToShow: 2 } }, + { breakpoint: 757, settings: { slidesToShow: 2 } }, + ], + }); +} + +/////////////////////////////////////////////////////////// +// start działania +/////////////////////////////////////////////////////////// + +function idmWaitForBannerElements() { + let attempts = 0; + const interval = setInterval(() => { + let initialized = 0; + + const bannerTitle = document.querySelector(".idm-products-banner__title"); + if (bannerTitle) { + idmSetBannerTitle(bannerTitle); + initialized++; + } + + const bannerText = document.querySelector(".idm-products-banner__text"); + if (bannerText) { + idmSetBannerText(bannerText); + initialized++; + } + + const bannerContainer = document.querySelector(".idm-products-banner__products"); + if (bannerContainer) { + idmRenderBannerProducts(bannerContainer); + initialized++; + } + + const bannerHeader = document.querySelector(".idm-products-banner__header"); + if (bannerHeader) { + initialized++; + } + + if (initialized >= 4) { + clearInterval(interval); + } else if (++attempts >= 10) { + clearInterval(interval); + console.warn("Niektóre wymagane elementy DOM nie zostały znalezione"); + } + }, 500); +} + +document.addEventListener("DOMContentLoaded", function () { + idmWaitForBannerElements(); +}); diff --git a/slick/src/style.less b/slick/src/style.less new file mode 100644 index 0000000..424ef3d --- /dev/null +++ b/slick/src/style.less @@ -0,0 +1,290 @@ +.idm-products-banner { + display: flex; + flex-direction: column; + margin: 3rem 0; + background: #eaebe9; + + &__header { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 42rem; + background: #000; + } + + &__title { + color: #fff; + font-size: 4rem; + } + + &__text { + color: #fff; + font-size: 2rem; + } + + &__products { + width: 100%; + + &.idm_hidden { + display: none; + } + } + + &__product { + padding: 1rem; + background: none; + text-align: center; + } + + &__product-name-link { + text-decoration: none; + } + + &__product-name { + color: #333; + margin: 1rem 0; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + + &__price-omnibus { + font-size: 1.2rem; + margin-top: 1rem; + } + + &__price-max { + color: #acacac; + margin-left: 1rem; + } + + &__product-img-container { + position: relative; + } + + &__products-img { + width: 100%; + } + + .slick-prev.slick-arrow { + &::before { + content: "\f104"; + font-family: "FONTAWESOME"; + } + } + + .slick-next.slick-arrow { + &::before { + content: "\f105"; + font-family: "FONTAWESOME"; + } + } + + &__favorites-icon { + position: absolute; + top: 2rem; + right: 1rem; + z-index: 100; + cursor: pointer; + width: 3rem; + height: 3rem; + + &:hover { + opacity: 0.6; + } + } + + &__label-icons { + position: absolute; + bottom: 0; + left: 0; + text-align: left; + } + + &__label { + padding: 0.4rem 0.8rem; + text-transform: uppercase; + background: #000; + color: #fff; + + &--promo { } + &--new { } + &--bestseller { } + } + + &__compare-btn { + position: absolute; + top: 2rem; + left: 2rem; + background: #fff; + + &.--success { + svg { + display: none; + } + &::before { + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='25' height='25' viewBox='0 0 25 25' fill='none'%3E%3Cpath d='M10.0516 18.5L4.35156 12.8L5.77656 11.375L10.0516 15.65L19.2266 6.475L20.6516 7.9L10.0516 18.5Z' fill='%23009263'/%3E%3C/svg%3E"); + width: 3rem; + height: 3rem; + } + } + + &.--loading:not(.--success) { } + + &:not(.--success):hover { + opacity: 0.6; + } + } + + &__compare-icon { } + + &__add-to-basket { + &_btn { + background: #000; + color: #fff; + padding: 1rem 2rem; + width: 100%; + border-radius: 0.6rem; + margin-top: 1rem; + text-transform: uppercase; + } + } + + &__skeletons { + width: 100%; + display: flex; + + @media (min-width: 757px) { + width: 50%; + } + + &.idm_hidden { + display: none; + } + + .idm-products-banner__product.skeleton { + width: 50%; + } + } + + &__skeleton-img { + width: 100%; + background: #ddd; + aspect-ratio: 1; + } + + &__skeleton-text { + &--name { + width: 100%; + height: 2rem; + background: #ddd; + margin-top: 2rem; + } + &--price { + width: 50%; + height: 2rem; + background: #ddd; + margin: 2rem auto; + } + } + + @keyframes skeletonPulse { + 0% { background-color: #ddd; } + 50% { background-color: #eaeaea; } + 100% { background-color: #ddd; } + } + + &__skeleton-img, + &__skeleton-text { + animation: skeletonPulse 1.5s ease-in-out infinite; + } + + &__product-prices { } + &__product-price-container { } + &__product-prices-box { } + &__price { } + + &__qty { + display: flex; + justify-content: space-between; + width: 100%; + align-items: center; + gap: 1rem; + } + + &__qty-input { + height: 4rem; + text-align: center; + border: 1px solid #ccc; + width: 60%; + max-width: unset; + min-width: unset; + } + + &__qty button { + background: #000; + height: 4rem; + width: 4rem; + color: #fff; + border-radius: 0.5rem; + min-width: 4rem; + } + + &__add-to-basket-form { + display: flex; + flex-direction: column; + gap: 2rem; + width: 100%; + } + + .btn.--solid.--medium.idm-products-banner__add-to-basket-button { + height: 4rem; + background: #000; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #000; + } + + .btn.--solid.--medium.idm-products-banner__add-to-basket-link { + width: 100%; + height: 4rem; + display: flex; + align-items: center; + justify-content: center; + background: #000; + border: 1px solid #000; + margin-top: auto; + } + + &__add-to-basket { + display: flex; + align-items: end; + flex-direction: column; + width: 80%; + margin: 0 auto; + } + + @media (min-width: 757px) { + flex-direction: row; + align-items: center; + + &__header { + width: 50%; + } + + &__title { + font-size: 5rem; + } + + &__products { + width: 50%; + } + + &__product { + &-img_container { } + } + } +} diff --git a/swiper/index.xslt b/swiper/index.xslt new file mode 100644 index 0000000..12654cd --- /dev/null +++ b/swiper/index.xslt @@ -0,0 +1,49 @@ + + + +
+
+

+

+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
diff --git a/swiper/src/main.js b/swiper/src/main.js new file mode 100644 index 0000000..02025b2 --- /dev/null +++ b/swiper/src/main.js @@ -0,0 +1,507 @@ +/////////////////////////////////////////////////////////// +// dodatek po stronie panelu +/////////////////////////////////////////////////////////// + +const tytul = "TYTUL"; +const tekst = "TEKST"; +const produkty = [173632, 75469, 85452, 25065, 4147]; + +window.idmInteractiveBannerObj = { + idmInteractiveBannerIds: typeof produkty !== "undefined" ? produkty : null, + idmInteractiveBannerTitle: typeof tytul !== "undefined" ? tytul : "", + idmInteractiveBannerText: typeof tekst !== "undefined" ? tekst : "", +}; + +/////////////////////////////////////////////////////////// +// pobranie zmiennych, obiekty mapujące +/////////////////////////////////////////////////////////// + +const idmInteractiveBannerIds = window.idmInteractiveBannerObj?.idmInteractiveBannerIds || null; +const idmInteractiveBannerTitle = window.idmInteractiveBannerObj?.idmInteractiveBannerTitle || ""; +const idmInteractiveBannerText = window.idmInteractiveBannerObj?.idmInteractiveBannerText || ""; + +const IDM_ZONE_TEXT_MAP = { + promotion: `${}`, + discount: `${}`, + bestseller: `${}`, + new: `${}`, +}; +const IDM_ZONE_CLASS_MAP = { + promotion: "idm-products-banner__label--promo", + discount: "idm-products-banner__label--promo", + bestseller: "idm-products-banner__label--bestseller", + new: "idm-products-banner__label--new", +}; + +/////////////////////////////////////////////////////////// +// ustawienie elementów headera +/////////////////////////////////////////////////////////// + +function idmSetBannerTitle(titleElement) { + if (idmInteractiveBannerTitle) { + titleElement.textContent = idmInteractiveBannerTitle; + } +} + +function idmSetBannerText(textElement) { + if (idmInteractiveBannerText) { + textElement.textContent = idmInteractiveBannerText; + } +} + +/////////////////////////////////////////////////////////// +// ulubione +/////////////////////////////////////////////////////////// + +// uwaga, trzeba odkryć komponent w układzie Lista zakupowa - Javascript + CSS + XSLT +function idmAttachFavoritesEvents() { + document.querySelectorAll(".idm-products-banner__favorites-icon").forEach(el => { + el.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + const icon = e.target.closest(".idm-products-banner__favorites-icon"); + app_shop.fn.shoppingList.addProductToList([ + [icon.getAttribute("data-id"), icon.getAttribute("data-size")] + ]); + }); + }); +} + +/////////////////////////////////////////////////////////// +// dodanie do porównania +/////////////////////////////////////////////////////////// + +function idmHandleAddToCompare(e) { + const compareBtnEl = e.target.closest(".idm-products-banner__compare-btn"); + if (!compareBtnEl || !compareBtnEl?.dataset?.compareId) return; + + e.preventDefault(); + idmAddToCompare(compareBtnEl, compareBtnEl.dataset.compareId); +} + +async function idmAddToCompare(btnEl, compareId) { + try { + btnEl.classList.add("--loading"); + const compareUrl = `/${app_shop?.vars?.language?.symbol || "de"}/settings.html?comparers=add&product=${compareId}`; + + const res = await fetch(compareUrl); + console.log(res); + if (!res.ok) throw new Error(`${}`); + + btnEl.classList.add("--success"); + + const compareContainerQuery = "#menu_compare_product"; + if (document.querySelector(compareContainerQuery)) { + app_shop.fn.load( + window.location.pathname, + [[compareContainerQuery, compareContainerQuery]], + function () {}, + "?set_render=content" + ); + } + + setTimeout(() => { + btnEl.classList.remove("--success"); + }, 2000); + } catch (err) { + console.error(err); + Alertek.Error(`${}`); + } finally { + btnEl.classList.remove("--loading"); + } +} + +/////////////////////////////////////////////////////////// +// produkty +/////////////////////////////////////////////////////////// + +function idmGetGraphQLQuery(ids) { + return JSON.stringify({ + query: `{ + products(searchInput: {productsId: ${JSON.stringify(ids)}}, settingsInput: {limit: 50}) { + products { + id + type + name + zones + icon + link + sizes { id amount } + awardedParameters { name values { name } } + price { + rebateCodeActive + price { gross { value formatted } } + omnibusPrice { gross { value formatted } } + max { gross { value formatted } } + beforeRebate { gross { value formatted } } + } + unit { + id + name + sellBy + } + opinion { rating count } + } + } + }` + }); +} + +// helpery +const escapeHtml = (v = "") => + String(v) + .replace(/&/g, "&").replace(//g, ">").replace(/"/g, """) + .replace(/'/g, "'"); + +const slot = (cond, html) => (cond ? html : ""); + +// label +function idmMarkupZoneLabel(zone) { + if (!zone) return ""; + return ` + + + ${IDM_ZONE_TEXT_MAP[zone] || ""} + + `; +} + +// porównanie +function idmMarkupCompareBtn(productId, options) { + if (!options?.compare) return ""; + return ` +
+ +
`; +} + +// ulubione +function idmMarkupFavoritesIcon(productId, sizeId, options) { + if (!options?.favorites) return ""; + return ` +
+ + + +
`; +} + +// ikona +function idmMarkupImage(link, icon) { + return ` + + + `; +} + +// nazwa +function idmMarkupName(name, link) { + return ` + `; +} + +// ceny +function idmMarkupPrices(priceObj) { + const current = priceObj?.price?.gross?.formatted || ""; + const max = priceObj?.max?.gross?.formatted || ""; + const hasOmnibus = priceObj?.omnibusPrice?.gross?.formatted; + + let mainPrice = ""; + if (current && max && max !== current) { + mainPrice = `${escapeHtml(current)} ${escapeHtml(max)}`; + } else if (current) { + mainPrice = escapeHtml(current); + } else if (max) { + mainPrice = `${escapeHtml(max)}`; + } + + const oldPrice = hasOmnibus + ? `${} ${escapeHtml(current || max)}` + : ""; + + return ` +
+
+
+ ${mainPrice} +

${oldPrice}

+
+
+
`; +} + +// dodawanie do koszyka +function idmMarkupAddToBasket(product, size, options) { + if (!options?.addToBasket) return ""; + const can = product?.type === "product" && product?.sizes?.length === 1 && size; + if (!can) return ""; + + const sellBy = size?.unitSellby || 1; + const precision = size?.unitPrecision || 0; + const max = (typeof size?.amount === "number" && size.amount > 0) ? size.amount : ""; + + return ` +
+
+ + + + +
+ + + + + +
+ + +
+
`; +} + +// link więcej opcji +function idmMarkupMoreOptions(link) { + return ` + `; +} + +// assembler produktu +function idmRenderProductCard(product, options = {}) { + const zone = product?.zones?.[0]; + const size = product?.sizes?.[0]; + + const headIcons = ` + ${idmMarkupCompareBtn(product.id, options)} + ${idmMarkupFavoritesIcon(product.id, size?.id, options)} + ${idmMarkupZoneLabel(zone)} + `; + + const addSection = + (options?.addToBasket && product?.type === "product" && product?.sizes?.length === 1 && size) + ? idmMarkupAddToBasket(product, size, options) + : idmMarkupMoreOptions(product.link); + + return ` +
+
+ ${headIcons} + ${idmMarkupImage(product.link, product.icon)} +
+ +
+ ${idmMarkupName(product.name, product.link)} + ${idmMarkupPrices(product.price)} + ${addSection} +
+
`; +} + +// tworzenie, ustawienie opcji +function idmCreateProductMarkup(products, options = { compare: true, favorites: true, addToBasket: true }) { + return products.map(p => idmRenderProductCard(p, options)).join(""); +} + +async function idmRenderBannerProducts(container) { + const skeletons = document.querySelector(".idm-products-banner__skeletons"); + + if (!idmInteractiveBannerIds) return; + + try { + container.innerHTML = ""; + + const response = await fetch("/graphql/v1/", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: idmGetGraphQLQuery(idmInteractiveBannerIds), + }); + + const json = await response.json(); + const productsData = json?.data?.products?.products; + + console.log(productsData); + + if (!productsData) return; + + const sorted = idmInteractiveBannerIds + .map(id => productsData.find(p => p.id === id)) + .filter(Boolean); + + container.innerHTML = idmCreateProductMarkup(sorted); + idmInteractiveBannerSliderSwiper.init(); + idmAttachFavoritesEvents(); + + // +/- i walidacja inputów ilości + document.addEventListener("click", (e) => { + const wrapper = e.target.closest(".idm-products-banner__qty"); + 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"); + const max = parseFloat(wrapper.dataset.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); + + // ukrycie skeletona + skeletons?.classList.add("idm_hidden"); + container.closest('.idm-products-banner__products-container').classList.remove("idm_hidden"); + + // event porównania + document.addEventListener("click", idmHandleAddToCompare); + } catch (err) { + console.error("Błąd pobierania produktów:", err); + } +} + +/////////////////////////////////////////////////////////// +// slider i wyrównanie wysokości +/////////////////////////////////////////////////////////// + +const idmInteractiveBannerSliderSwiperOptions = { + loop: false, + autoHeight: false, + centeredSlides: false, + spaceBetween: 0, + simulateTouch: true, + cssMode: false, + slidesPerView: 2, + navigation: { + nextEl: ".swiper-button-next", + prevEl: ".swiper-button-prev", + }, + pagination: { + el: ".swiper-pagination", + clickable: true, + }, + breakpoints: { + 757: { + slidesPerView: 2, + autoHeight: true, + }, + 979: { + slidesPerView: 2, + }, + }, +}; + +const idmInteractiveBannerSliderSwiper = new HotspotSlider({ + selector: ".idm-products-banner__products-container", + options: idmInteractiveBannerSliderSwiperOptions, + hotspotName: "idmInteractiveBannerSliderSwiper", + callbackAfter: () => { + console.log('juz') + app_shop.fn.setHeight({ + selectors: [ + ".idm-products-banner .idm-products-banner__product-img-container", + ".idm-products-banner .idm-products-banner__product-name", + ".idm-products-banner .idm-products-banner__price", + ".idm-products-banner .idm-products-banner__price-omnibus", + ".idm-products-banner .idm-products-banner__add-to-basket", + ".idm-products-banner .product" + ], + container: ".idm-products-banner__products" + }); + console.log('juz2') + } +}); + +/////////////////////////////////////////////////////////// +// start działania +/////////////////////////////////////////////////////////// + +function idmWaitForBannerElements() { + let attempts = 0; + const interval = setInterval(() => { + let initialized = 0; + + const bannerTitle = document.querySelector(".idm-products-banner__title"); + if (bannerTitle) { + idmSetBannerTitle(bannerTitle); + initialized++; + } + + const bannerText = document.querySelector(".idm-products-banner__text"); + if (bannerText) { + idmSetBannerText(bannerText); + initialized++; + } + + const bannerContainer = document.querySelector(".idm-products-banner__products"); + if (bannerContainer) { + idmRenderBannerProducts(bannerContainer); + initialized++; + } + + const bannerHeader = document.querySelector(".idm-products-banner__header"); + if (bannerHeader) { + initialized++; + } + + if (initialized >= 4) { + clearInterval(interval); + } else if (++attempts >= 10) { + clearInterval(interval); + console.warn("Niektóre wymagane elementy DOM nie zostały znalezione"); + } + }, 500); +} + +document.addEventListener("DOMContentLoaded", function () { + idmWaitForBannerElements(); +}); diff --git a/swiper/src/style.less b/swiper/src/style.less new file mode 100644 index 0000000..c41ccf0 --- /dev/null +++ b/swiper/src/style.less @@ -0,0 +1,305 @@ +.idm-products-banner { + display: flex; + flex-direction: column; + margin: 3rem 0; + background: #eaebe9; + + &__header { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 42rem; + background: #000; + } + + &__title { + color: #fff; + font-size: 4rem; + } + + &__text { + color: #fff; + font-size: 2rem; + } + + &__products-container { + width: 100%; + &.idm_hidden { + display: none; + } + } + + &__products { + width: 100%; + } + + &__product { + padding: 1rem; + background: none; + text-align: center; + } + + &__product-name-link { + text-decoration: none; + } + + &__product-name { + color: #333; + margin: 1rem 0; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + + &__price-omnibus { + font-size: 1.2rem; + margin: 1rem 0; + } + + &__price-max { + color: #acacac; + margin-left: 1rem; + } + + &__product-img-container { + position: relative; + } + + &__products-img { + width: 100%; + } + + .swiper-button-prev { + &::before { + content: "\f104"; + font-family: "FONTAWESOME"; + } + } + + .swiper-button-next { + &::before { + content: "\f105"; + font-family: "FONTAWESOME"; + } + } + + &__favorites-icon { + position: absolute; + top: 2rem; + right: 1rem; + z-index: 100; + cursor: pointer; + width: 3rem; + height: 3rem; + + &:hover { + opacity: 0.6; + } + } + + &__label-icons { + position: absolute; + bottom: 0; + left: 0; + text-align: left; + } + + &__label { + padding: 0.4rem 0.8rem; + text-transform: uppercase; + background: #000; + color: #fff; + + &--promo { } + &--new { } + &--bestseller { } + } + + &__compare-btn { + position: absolute; + top: 2rem; + left: 2rem; + background: #fff; + + &.--success { + svg { + display: none; + } + &::before { + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='25' height='25' viewBox='0 0 25 25' fill='none'%3E%3Cpath d='M10.0516 18.5L4.35156 12.8L5.77656 11.375L10.0516 15.65L19.2266 6.475L20.6516 7.9L10.0516 18.5Z' fill='%23009263'/%3E%3C/svg%3E"); + width: 3rem; + height: 3rem; + } + } + + &.--loading:not(.--success) { } + + &:not(.--success):hover { + opacity: 0.6; + } + } + + &__compare-icon { } + + &__add-to-basket { + &_btn { + background: #000; + color: #fff; + padding: 1rem 2rem; + width: 100%; + border-radius: 0.6rem; + margin-top: 1rem; + text-transform: uppercase; + } + } + + &__skeletons { + width: 100%; + display: flex; + + @media (min-width: 757px) { + width: 50%; + } + + &.idm_hidden { + display: none; + } + + .idm-products-banner__product.skeleton { + width: 50%; + } + } + + &__skeleton-img { + width: 100%; + background: #ddd; + aspect-ratio: 1; + } + + &__skeleton-text { + &--name { + width: 100%; + height: 2rem; + background: #ddd; + margin-top: 2rem; + } + &--price { + width: 50%; + height: 2rem; + background: #ddd; + margin: 2rem auto; + } + } + + @keyframes skeletonPulse { + 0% { background-color: #ddd; } + 50% { background-color: #eaeaea; } + 100% { background-color: #ddd; } + } + + &__skeleton-img, + &__skeleton-text { + animation: skeletonPulse 1.5s ease-in-out infinite; + } + + &__product-prices { } + &__product-price-container { } + &__product-prices-box { + color: #333; + } + &__price { } + + &__qty { + display: flex; + justify-content: space-between; + width: 100%; + align-items: center; + gap: 1rem; + } + + &__qty-input { + height: 3rem; + text-align: center; + border: 1px solid #ccc; + width: 60%; + max-width: unset; + min-width: unset; + } + + &__qty button { + background: #000; + height: 3rem; + width: 3rem; + color: #fff; + border-radius: 0.5rem; + min-width: 3rem; + } + + &__add-to-basket-form { + display: flex; + flex-direction: column; + gap: 2rem; + width: 100%; + } + + .btn.--solid.--medium.idm-products-banner__add-to-basket-button { + height: 4rem; + background: #000; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #000; + } + + .btn.--solid.--medium.idm-products-banner__add-to-basket-link { + width: 100%; + height: 4rem; + display: flex; + align-items: center; + justify-content: center; + background: #000; + border: 1px solid #000; + margin-top: auto; + } + + &__add-to-basket { + display: flex; + align-items: end; + flex-direction: column; + width: 80%; + margin: 0 auto; + } + + @media (min-width: 757px) { + flex-direction: row; + align-items: center; + + &__header { + width: 50%; + } + + &__title { + font-size: 5rem; + } + + &__products-container { + width: 50%; + } + + &__product { + &-img_container { } + } + } + + &__qty-input { + height: 4rem; + } + + &__qty button { + height: 4rem; + width: 4rem; + min-width: 4rem; + } +}