diff --git a/README.md b/README.md
index 2561d7e..0c02524 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
# Ramki rekomendacji #
Funkcje js składające się na customowe ramki rekomendacji
+## UWAGI PRZEDWDROŻENIOWE ##
+- kod zawiera app_shop.fn.idmSetHeight używany do wyrównywania wysokości
+- kod zawiera app_shop.fn.idmGetOmnibusDetails który jest przerobionym kodem idosella app_shop.fn.getOmnibusDetails używanym w zwykłych ramkach rekomendacji
+
### Pliki ###
- bundle.js - całość
- 1graphQL.js - graphQL + literały
@@ -9,7 +13,7 @@ Funkcje js składające się na customowe ramki rekomendacji
- 4init.js - obiekt z ogólnymi ustawieniami Hotspota + init swipera
- 5ainsertHotspotHTML.js - wstawienie ramki po kodzie html
- 5binsertHotspotObject.js - wstawienie ramki po obiekcie js
-- 6style.js - wstawienie styli na koniec body(do przeniesienia do css komponentu)
+- 6style.css -
### Użycie ###
1. Wstawienie całego kodu do komponentu/dodatku
@@ -75,6 +79,45 @@ idmInsertAllHTMLHotspots();
```
+### LISTA GLOBALNYCH FUNKCJI, ZMIENNYCH ###
+##### INIT #####
+- priceQuery
+- productQuery
+- IDM_PRODUCTS_GQL
+- IDM_HOTSPOTS_GQL
+- IDM_PRODUCT_GQL
+- IDM_HOTSPOT_ADD_TO_BASKET
+- idmHotspotTextObject
+#### FUNKCJE ####
+- app_shop.fn.idmGetOmnibusDetails
+- idmHandleAddToBasket
+- idmRangeMaxAlert
+- idmRangeMinAlert
+- idmQuantityButtonClick
+- idmQuantityInputChange
+- idmGetHotspotData
+- idmGetQueryData
+- idmObserveOnce
+- app_shop.fn.idmSetHeight
+
+
+#### MARKUP ####
+- idmPrepareProductsMarkup
+- idmPrepareSingleProductMarkup
+- idmPrepareHotspotImgMarkup
+- idmPrepareHotspotPriceMarkup
+- idmPrepareHotspotAddToBasketMarkup
+
+#### INIT ####
+- idmPriceType
+- idmGeneralHotspotObjData
+- idmHotspotInit
+
+#### INSERT ####
+- idmInsertHotspotElement
+- idmInsertAllHTMLHotspots
+- idmInsertHotspotObject
+- idmInsertAllObjectHotspots
Created by • **[IdoMods](https://idomods.pl/)** • 2025
\ No newline at end of file
diff --git a/bundle.js b/bundle.js
index de84d36..46e75ac 100644
--- a/bundle.js
+++ b/bundle.js
@@ -1,1081 +1,1118 @@
-///////////////////////////////////////////////
-// GraphQL
-// ogolne
-const priceQuery = `price {
- rebateCodeActive
- price {
- gross {
- value
- formatted
- }
- }
- omnibusPrice {
- gross {
- value
- formatted
- }
- }
- omnibusPriceDetails {
- unit {
- gross {
- value
- formatted
- }
- }
- youSavePercent
- omnibusPriceIsHigherThanSellingPrice
- newPriceEffectiveUntil {
- formatted
- }
- }
- max {
- gross {
- value
- formatted
- }
- }
- unit {
- gross {
- value
- formatted
- }
- }
- unitConvertedPrice {
- gross {
- value
- formatted
- }
- }
- youSavePercent
- beforeRebate {
- gross {
- value
- formatted
- }
- }
- beforeRebateDetails {
- youSavePercent
- unit {
- gross {
- value
- formatted
- }
- }
- }
- advancePrice {
- gross {
- value
- formatted
- }
- }
- suggested {
- gross {
- value
- formatted
- }
- }
- rebateNumber {
- number
- gross {
- value
- formatted
- }
- }
- }`;
-
-const productQuery = `id
- type
- name
- zones
- icon
- iconSecond
- iconSmall
- iconSmallSecond
- link
- zones
- producer{
- name
- }
- category{
- name
- }
- sizes{
- id
- amount
- name
- ${priceQuery}
- }
- group{
- id
- name
- link
- versions{
- id
- name
- icon
- iconSecond
- iconSmall
- iconSmallSecond
- }
- }
- awardedParameters {
- name
- id
- description
- values {
- name
- id
- }
- }
- enclosuresImages {
- position
- url
- }
- points
- unit{
- id, name, singular, plural, fraction, sellBy, precision, unitConvertedFormat
- }
- ${priceQuery}`;
-// 1. products
-const IDM_PRODUCTS_GQL = (args) => JSON.stringify({
- query: `{
- products(${args}){
- took
- products{
- ${productQuery}
- }
- }
-}`
-});
-
-// 2. hotspots
-const IDM_HOTSPOTS_GQL = (args) => JSON.stringify({
- query: `{
- hotspots(${args}){
- took
- products{
- ${productQuery}
- }
- }
-}`
-});
-
-// 3. single product
-const IDM_PRODUCT_GQL = (args) => JSON.stringify({
- query: `{
- product(${args}){
- product{
- ${productQuery}
- }
- }
- }`
-});
-// ADD TO BASKET
-const IDM_HOTSPOT_ADD_TO_BASKET = (t, e, a) => JSON.stringify({
- query: `mutation {\n addProductsToBasket(ProductInput: {id: ${t}, size: "${e}", quantity: ${a}}) {\n status\n results {\n status\n error {\n code\n message\n }\n productCode\n productId\n sizeId\n quantity\n quantityAvailable\n }\n clientDetailsInBasket {\n id\n login\n firstname\n lastname\n participationPartnerProgram\n usesVat\n email\n isWholesaler\n isWholesalerOrder\n clientIdUpc\n }\n }\n }`
-});
-
-
-///////////////////////////////////////////////////////////
-// TEXT
-const idmHotspotTextObject = {
- ["Kod rabatowy"]: "Kod rabatowy",
- ["Okazja"]: "Okazja",
- ["Promocja"]: "Promocja",
- ["Bestseller"]: "Bestseller",
- ["Nowość"]: "Nowość",
- ["Ilość"]: "Ilość",
- ["Zwiększ ilość"]: "Zwiększ ilość",
- ["Zmniejsz ilość"]: "Zmniejsz ilość",
- ["Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki"]: "Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki",
- ["Cena regularna"]: "Cena regularna",
- ["Cena bez kodu"]: "Cena bez kodu",
- ["Cena nadchodząca od"]: "Cena nadchodząca od",
- ["Coś poszło nie tak podczas dodawania do koszyka. Spróbuj ponownie lub odśwież stronę"]: "Coś poszło nie tak podczas dodawania do koszyka. Spróbuj ponownie lub odśwież stronę",
- ["Nie znaleziono produktów"]: "Nie znaleziono produktów",
- ["Błąd przy pobieraniu danych"]: "Błąd przy pobieraniu danych",
- ["Kliknij, by przejść do formularza kontaktu"]: "Kliknij, by przejść do formularza kontaktu",
- ["Cena na telefon"]: "Cena na telefon",
- ["Dodany"]: "Dodany",
- ["Wystąpił błąd"]: "Wystąpił błąd",
- ["Do koszyka"]: "Do koszyka",
- ["Maksymalna liczba sztuk tego towaru które możesz dodać do koszyka to:"]: "Maksymalna liczba sztuk tego towaru które możesz dodać do koszyka to:",
- ["Minimalna liczba sztuk tego towaru które możesz dodać do koszyka to:"]: "Minimalna liczba sztuk tego towaru które możesz dodać do koszyka to:",
- ["Wystąpił błąd z inicjalizacją. Proszę odśwież stronę"]: "Wystąpił błąd z inicjalizacją. Proszę odśwież stronę",
- ["Nie znaleziono kontenera"]: "Nie znaleziono kontenera",
- ["Nie znaleziono metody graphql"]: "Nie znaleziono metody graphql",
-}
-//////////////////////////////////////////////////////////////////////////\\\\\\\\\\\\
-// IDOSELL omnibus details
-
-// omnibusDetailsTxt - nadpisać na własny obiekt
-
-app_shop.fn.idmGetOmnibusDetails = (options) => {
- const {
- productData, sizeId, priceType = app_shop.vars.priceType,
- } = options || {};
- if (!productData) return false;
- const sizeData = productData.sizes.find((size) => size.id === sizeId) || productData;
- if (!sizeData?.price) return false;
- const classes = {
- add: [],
- remove: ['--omnibus', '--omnibus-short', '--omnibus-code', '--omnibus-code-short', '--omnibus-new-price', '--omnibus-higher'],
- };
- const activeLabel = {};
-
- const omnibusPrice = sizeData.price?.omnibusPriceDetails?.unit?.[priceType]?.formatted || sizeData.price.omnibusPrice[priceType]?.formatted;
- if (!omnibusPrice) {
- return {
- classes,
- };
- }
- // Omnibus
- classes.add.push('--omnibus');
- classes.remove = classes.remove.filter((item) => item !== '--omnibus');
-
- const sellBy = productData?.unit?.sellBy;
-
- const unitMaxPrice = sizeData?.price?.unit?.[priceType]?.formatted && sizeData.price.max?.[priceType]?.value ? format_price(parseFloat(sizeData.price.max?.[priceType]?.value) * parseFloat(sellBy), {
- mask: app_shop.vars.currency_format,
- currency: app_shop.vars?.currency?.symbol,
- currency_space: app_shop.vars.currency_space,
- currency_before_price: app_shop.vars.currency_before_value,
- }) : false;
-
- const maxPrice = unitMaxPrice || sizeData.price.max?.[priceType]?.formatted;
- // Skrócona wersja omnibusa
- if (!maxPrice || maxPrice === omnibusPrice) {
- classes.add.push('--omnibus-short');
- classes.remove = classes.remove.filter((item) => item !== '--omnibus-short');
- }
- // Aktywny kod rabatowy
- if (app_shop.vars.omnibus?.rebateCodeActivate && sizeData.price?.rebateCodeActive) {
- classes.add.push('--omnibus-code');
- activeLabel.rebateCodeActive = `${idmHotspotTextObject["Kod rabatowy"]}`;
- classes.remove = classes.remove.filter((item) => item !== '--omnibus-code');
- }
- // Skrócona wersja omnibusa, gdy aktywny kod rabatowy
- const beforeRebatePrice = sizeData.price.beforeRebateDetails?.unit?.[priceType]?.formatted || sizeData.price.beforeRebate[priceType]?.formatted;
- if (app_shop.vars.omnibus?.rebateCodeActivate && beforeRebatePrice === omnibusPrice && sizeData.price?.rebateCodeActive) {
- classes.add.push('--omnibus-code-short');
- classes.remove = classes.remove.filter((item) => item !== '--omnibus-code-short');
- }
- // Nadchodząca cena
- const newDate = sizeData.price.omnibusPriceDetails?.newPriceEffectiveUntil?.formatted;
- if (newDate && maxPrice) {
- classes.add.push('--omnibus-new-price');
- classes.remove = classes.remove.filter((item) => item !== '--omnibus-new-price');
- }
- // Cena omnibusa wyższa niż cena sprzedaży
- const higher = sizeData.price.omnibusPriceDetails?.omnibusPriceIsHigherThanSellingPrice;
- if (higher) {
- classes.add.push('--omnibus-higher');
- classes.remove = classes.remove.filter((item) => item !== '--omnibus-higher');
- }
- // label okazja
- if ((!higher || newDate) && !activeLabel?.rebateCodeActive) {
- activeLabel.bargain = `${idmHotspotTextObject["Okazja"]}`;
- }
- // label promocja
- if (Object.keys(activeLabel)?.length === 0) {
- activeLabel.bargain = `${idmHotspotTextObject["Promocja"]}`;
- }
-
- // labele zones
- if(productData.zones.find(zone => zone ==="bestseller")) activeLabel.bestseller = `${idmHotspotTextObject["Bestseller"]}`;
- if(productData.zones.find(zone => zone ==="news")) activeLabel.news = `${idmHotspotTextObject["Nowość"]}`;
-
-
-
- let omnibusPercentSign = '';
- if (higher) {
- omnibusPercentSign = '-';
- } else if (sizeData.price.omnibusPriceDetails?.youSavePercent !== 0) {
- omnibusPercentSign = '+';
- }
- const omnibusPercent = `${omnibusPercentSign}${sizeData.price.omnibusPriceDetails?.youSavePercent}%`;
- const omnibus = {
- price: omnibusPrice,
- visible: true,
- percent: omnibusPercent,
- html: `${idmHotspotTextObject['Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki']}: ${omnibusPrice}${omnibusPercent}`,
- };
-
- const max = (maxPrice) ? {
- max: {
- price: maxPrice,
- visible: true,
- percent: `-${sizeData.price.youSavePercent}%`,
- html: `${idmHotspotTextObject['Cena regularna']}:
- ${maxPrice}-${sizeData.price.youSavePercent}%`,
- },
- } : {};
-
- const beforeRebate = (beforeRebatePrice) ? {
- beforeRebate: {
- price: beforeRebatePrice,
- visible: !!classes.add.includes('--omnibus-code'),
- percent: `-${sizeData.price.beforeRebateDetails?.youSavePercent}%`,
- html: `${idmHotspotTextObject['Cena bez kodu']}:
- ${beforeRebatePrice}
- -${sizeData.price.beforeRebateDetails?.youSavePercent}%`,
- },
- } : {};
-
- const newPriceEffectiveUntil = (newDate) ? {
- newPriceEffectiveUntil: {
- date: newDate,
- price: maxPrice,
- visible: !!classes.add.includes('--omnibus-new-price'),
- html: `${idmHotspotTextObject['Cena nadchodząca od']}
- ${newDate}:
- ${maxPrice}`,
- },
- } : {};
-
-
- return {
- classes,
- omnibus,
- ...max,
- ...beforeRebate,
- ...newPriceEffectiveUntil,
- activeLabel,
- };
-};
-
-//////////////////////////////////////////////////////////////////////////
-// EVENTY
-// dodawanie do koszyka
-async function idmHandleAddToBasket(e){
- const formEl = e.target.closest("form.add_to_basket");
- if(!formEl) return;
- try{
- // pobieranie danych i elementów
- formEl.classList.add("--loading")
- const buttonEl = formEl.querySelector(".add_to_basket__button");
- e.preventDefault();
-
- const id = formEl.querySelector("input[name='product']")?.value;
- const size = formEl.querySelector("input[type='hidden'][name='size']")?.value;
- const number = formEl.querySelector("input[name='number']")?.value;
-
- // dodanie do koszyka
- const res = await fetch(`/graphql/v1/`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: IDM_HOTSPOT_ADD_TO_BASKET(id, size, number)
- });
- const data = await res.json();
-
- // Błąd
- if(data?.data?.addProductsToBasket?.status !== "success") throw new Error(data);
- else{
- // Obsługiwanie sukcesu
- app_shop.graphql.trackingEvents(res);
- buttonEl.classList.add("--success");
-
- // Dodawanie do koszyka na stronie basketedit.php będzie wymagał innego indywidualnego kodu!!!!!
- buttonEl.innerHTML = `${buttonEl.dataset.success}`;
- setTimeout(()=>{
- buttonEl.innerHTML = `${buttonEl.dataset.text}`;
- app_shop.fn?.menu_basket_cache?.();
- buttonEl.classList.remove("--success");
- }, 3000);
- }
- }catch(err){
- console.error(err);
- Alertek.Error(idmHotspotTextObject["Coś poszło nie tak podczas dodawania do koszyka. Spróbuj ponownie lub odśwież stronę"]);
- buttonEl.innerHTML = `${buttonEl.dataset.error}`;
- buttonEl.classList.add("--error")
- setTimeout(()=>{
- buttonEl.classList.remove("--error")
- buttonEl.innerHTML = `${buttonEl.dataset.text}`;
- }, 3000);
- }finally{
- formEl.classList.remove("--loading")
- }
-}
-
-///qty
-const idmRangeMaxAlert = (max)=> Alertek.Error(`${idmHotspotTextObject["Maksymalna liczba sztuk tego towaru które możesz dodać do koszyka to:"]} ${max}`)
-const idmRangeMinAlert = (min)=> Alertek.Error(`${idmHotspotTextObject["Minimalna liczba sztuk tego towaru które możesz dodać do koszyka to:"]} ${min}`)
-
-function idmQuantityButtonClick(e){
- if(e.target.classList.contains("idm-products-banner__qty-input")) return e.target.select();
- const wrapper = e.target.closest(".idm-products-banner__qty");
-
- 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;
- idmRangeMaxAlert(max)
- }
- } else if (e.target.classList.contains("idm-products-banner__qty-decrease")) {
- current -= step;
- if (current < step){
- current = step;
- idmRangeMinAlert(step)
- }
- }
- input.value = current.toFixed(precision);
-}
-function idmQuantityInputChange(e){
- if(e.target.value > +e.target.max){
- idmRangeMaxAlert(e.target.max)
- e.target.value = +e.target.max
- }
- if(e.target.value < +e.target.min){
- idmRangeMinAlert(e.target.min)
- e.target.value = +e.target.min;
- }
-}
-
-//////////////////////////////////////////////
-// DANE
-// dwie funkcje zamiast jednej
-async function idmGetHotspotData(query, graphFn){
- try{
- const res = await fetch(`/graphql/v1/`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: graphFn ? graphFn(query) : IDM_PRODUCTS_GQL(query)
- });
- const data = await res.json();
- const products = data[graphFn === IDM_HOTSPOTS_GQL ? "hotspots" : "data"]?.products?.products;
- if(!products) throw new Error(idmHotspotTextObject["Nie znaleziono produktów"]);
-
- console.log(data);
- return products;
- }catch(err){
- console.error(idmHotspotTextObject["Błąd przy pobieraniu danych"], err);
- return null;
- }
-}
-
-
-function idmGetQueryData({
- productsID,
- productsMenu,
- hotspotsType
-}){
- let graphFn, query;
-
- if(productsID){
- graphFn = IDM_PRODUCTS_GQL;
- query = `searchInput: {productsId: [${productsID}]}`;
- }else if(productsMenu){
- graphFn = IDM_PRODUCTS_GQL;
- query = `searchInput: {navigation: ${productsMenu}}`;
- }else if(hotspotsType){
- graphFn = IDM_HOTSPOTS_GQL;
- query = `searchInput: {hotspot: ${hotspotsType}, limit: 16}`;
- }
-
- return {graphFn, query}
-}
-
-//////////////////////////////////////////////
-// LAZY LOADING
-function idmObserveOnce(element, callback, options = { root: null, rootMargin: "0px", threshold: 0.1 }) {
- if (!element) return;
-
- const observer = new IntersectionObserver((entries, obs) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- callback(entry); // run your callback
- obs.disconnect(); // stop observing after first trigger
- }
- });
- }, options);
-
- observer.observe(element);
-}
-//////////////////////////////////////////////////////////////////////////
-// Markup
-
-// Funkcja przygotująca markup dla wszystkich produktów
-function idmPrepareProductsMarkup(products, addToBasket){
- let markup = "";
- products.forEach((prod)=>{
- markup += idmPrepareSingleProductMarkup(prod, addToBasket);
- })
- return markup;
-}
-
-// funkcja przygotowująca markup dla wybranego produktu
-function idmPrepareSingleProductMarkup(prod, addToBasket){
- const prodExchangedData = app_shop.fn?.idmGetOmnibusDetails({productData: prod});
-
- // pobranie labelek
- let labelHTMLMarkup = "";
- if(typeof prodExchangedData.activeLabel === "object") Object.entries(prodExchangedData.activeLabel).forEach(([key,value])=>{
- labelHTMLMarkup += value;
- })
-
- // markup pojedynczego produktu
- let singleMarkup = "";
- singleMarkup += `
-
`;
-
- return singleMarkup;
-}
-
-// markup zdjęcia
-function idmPrepareHotspotImgMarkup(prod){
- let markup = "";
- if(prod.iconSmallSecond !== undefined && prod.iconSecond !== undefined) markup +=`
-
-
-
-
-
- `;
- else if(prod?.iconSmall !== undefined) markup += `
-
-
-
- `;
- else markup += `
`
- return markup;
-}
-
-// markup cen
-function idmPrepareHotspotPriceMarkup(prod, prodExchangedData){
- const price = prod.price.price[idmPriceType];
- const unit = prod.unit;
- const pointsPrice = prod?.points;
- const convertedPrice = prod.price?.unitConvertedPrice?.[idmPriceType]?.formatted;
-
- return `
-
- ${price.formatted}
-
- /
- ${unit?.sellBy}
- ${unit?.sellBy > 1 ? unit?.plural : unit?.singular}
-
- ${convertedPrice ? `${convertedPrice}` : ""}
-
- ${pointsPrice ? `${pointsPrice} pkt.` : ""}
- ${price.value === 0 ? `${idmHotspotTextObject["Cena na telefon"]}` : ""}
- ${prodExchangedData?.beforeRebate?.visible ? `${prodExchangedData?.beforeRebate?.html}` : ""}
- ${prodExchangedData?.newPriceEffectiveUntil?.visible ? `${prodExchangedData?.newPriceEffectiveUntil?.html}` : ""}
- ${prodExchangedData?.omnibus?.visible ? `${prodExchangedData?.omnibus?.html}` : ""}
- ${prodExchangedData?.max?.visible ? `${prodExchangedData?.max?.html}` : ""}
- `;
-}
-
-// markup dodawania do koszyka
-function idmPrepareHotspotAddToBasketMarkup(prod, addToBasket){
- let markup = "";
- if(!addToBasket && typeof addToBasket !== "undefined" ||
- (typeof addToBasket === "undefined" && typeof idmGeneralHotspotObjData === "object" && !idmGeneralHotspotObjData?.options?.addToBasket) ||
- !addToBasket && typeof idmGeneralHotspotObjData === "undefined") return markup;
-
- // link do produktu jak nie jest to zwykły produkt
- if(prod.type !== "product") markup = `Zobacz produkt`;
- else if(addToBasket === "range"
- || typeof addToBasket === "undefined" && typeof idmGeneralHotspotObjData === "object" && idmGeneralHotspotObjData?.options?.addToBasket === "range") // +-
- markup = ``;
- else // Zwykłe dodanie do koszyka
- markup = `
- `;
- return markup;
-}
-////////////////////////////////////////////////////
-// INIT
-
-// brutto/netto
-const idmPriceType = app_shop?.vars?.priceType || "gross";
-
-
-// Zmienna trzymająca dane o ustawieniach customowych ramek rekomendacji na całym sklepie
-/**
- * Obiekt konfiguracyjny ogólnych ustawień hotspotów rekomendacji.
- *
- * @typedef {object} idmGeneralHotspotObjData
- * @property {object} options - Główne ustawienia hotspotów
- * @property {boolean} options.lazy - Czy wczytywać zawartość w trybie lazy (required).
- * @property {boolean|string} options.addToBasket - Zachowanie przy dodawaniu do koszyka:
- * - true = przycisk dodaj do koszyka
- * - false = brak przycisku
- * - "range" = dodaj z wyborem zakresu (required).
- * @property {boolean|object} options.swiper - Ustawienia slidera:
- * - true/false = włącz/wyłącz
- * - object = konfiguracja instancji Swiper (required jeśli obiekt).
- */
-const idmGeneralHotspotObjData = {
- options: {
- lazy: true,
- addToBasket: true, // true, false, "range"
- swiper: { // true, false, obiekt z opcjami swipera
- loop: false,
- autoHeight: false,
- spaceBetween: 16,
- slidesPerView: 1.4,
- centeredSlides: true,
- centeredSlidesBounds: true,
- breakpoints: {
- 550: {
- slidesPerView: 3,
- centeredSlides: true,
- centeredSlidesBounds: true,
- },
- 979: {
- slidesPerView: 4,
- centeredSlides: false,
- },
- }
- }
- }
-}
-
-
-// Funkcja inicjalizująca wybranego hotspota(addtobasket range - swiper)
-async function idmHotspotInit(id, options={}){
- try{
- const hotspotEl = document.getElementById(id);
- if(!hotspotEl) throw new Error("Nie znaleziono elementu");
-
- // add to basket + -
- if(options?.addToBasket === "range" ||
- typeof options?.addToBasket === "undefined" && typeof idmGeneralHotspotObjData === "object" && idmGeneralHotspotObjData?.options?.addToBasket === "range"){
-
- // obsługa i sprawdzanie clicków
- // hotspotEl.addEventListener("click", e=>{
- // const wrapper = e.target.closest(".idm-products-banner__qty");
- // if (!wrapper) return;
-
- // if(e.target.classList.contains("idm-products-banner__qty-input")) return e.target.select();
-
- // 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;
- // rangeMaxAlert(max)
- // }
- // } else if (e.target.classList.contains("idm-products-banner__qty-decrease")) {
- // current -= step;
- // if (current < step){
- // current = step;
- // rangeMinAlert(step)
- // }
- // }
- // input.value = current.toFixed(precision);
- // });
- // sprawdzanie na input
- // hotspotEl.querySelectorAll(".idm-products-banner__qty-input").forEach(inp=>{
- // inp.addEventListener("input", e=>{
- // if(e.target.value > +e.target.max){
- // rangeMaxAlert(e.target.max)
- // e.target.value = +e.target.max
- // }
- // if(e.target.value < +e.target.min){
- // rangeMinAlert(e.target.min)
- // e.target.value = +e.target.min;
- // }
- // });
- // })
-
- }
-
- // swiper || slick
- if(options?.swiper ||
- typeof options?.swiper === "undefined" && typeof idmGeneralHotspotObjData === "object" && idmGeneralHotspotObjData?.options?.swiper){
-
-
- // Opcje swipera
- let swiperOptions = typeof options.swiper === "object" ? options.swiper : "";
- if(typeof options.swiper === "object") swiperOptions = options.swiper;
- else if(typeof idmGeneralHotspotObjData === "object" && typeof idmGeneralHotspotObjData?.options?.swiper === "object"){
- swiperOptions = idmGeneralHotspotObjData?.options?.swiper;
- swiperOptions.navigation = {
- nextEl: `#${id} .idm-button-next`,
- prevEl: `#${id} .idm-button-prev`,
- }
- }else{
- swiperOptions = {
- loop: false,
- autoHeight: false,
- spaceBetween: 16,
- slidesPerView: 1.4,
- centeredSlides: true,
- centeredSlidesBounds: true,
- breakpoints: {
- 550: {
- slidesPerView: 3,
- centeredSlides: true,
- centeredSlidesBounds: true,
- },
- 979: {
- slidesPerView: 4,
- centeredSlides: false,
- },
- },
- navigation: {
- nextEl: `#${id} .idm-button-next`,
- prevEl: `#${id} .idm-button-prev`,
- },
- }
- }
-
- // Wywołanie swipera
- const selectedSwiper = new HotspotSlider({
- selector: `#${id} .swiper`,
- hotspotName: `${id}`,
- options: swiperOptions,
- });
- await selectedSwiper.init();
- }
- }catch(err){
- console.error(idmHotspotTextObject["Wystąpił błąd z inicjalizacją. Proszę odśwież stronę"], err);
- }
-}
-
-console.log("init")
-
-////////////////////////////////////////////////////
-// Funkcja init ELEMENT HTML
-async function idmInsertHotspotElement(selectedContainerEl){
-
- selectedContainerEl.classList.add("--init");
- const {graphFn, query} = idmGetQueryData({
- productsID: selectedContainerEl?.dataset?.productsId,
- productsMenu: selectedContainerEl?.dataset?.productsMenu,
- hotspotsType: selectedContainerEl.dataset.hotspotsType
- });
-
- if(!graphFn || !query){
- console.log(idmHotspotTextObject["Nie znaleziono metody graphql"], selectedContainerEl)
- return selectedContainerEl.remove();
- }
-
- // Funkcja od uzupełniania danych
- const idmFill = async ()=>{
- try{
- // pobranie danych o produktach
- const products = await idmGetHotspotData(query, graphFn);
- if(!products) throw new Error(idmHotspotTextObject["Nie znaleziono produktów"]);
-
-
- // wstawienie produktów zależnie czy w section jest .hotspot czy nie
- const hotspotInsideEl = selectedContainerEl.querySelector(".hotspot");
- if(hotspotInsideEl) hotspotInsideEl.innerHTML += `
-
- ${idmPrepareProductsMarkup(products, true)}
-
-
-
-
- `;
- else selectedContainerEl.innerHTML = `
-
- ${idmPrepareProductsMarkup(products, true)}
-
-
-
-
-
`;
-
- selectedContainerEl.classList.remove("idm-loading")
- // init swipera
- idmHotspotInit(selectedContainerEl.id)
- }catch(err){
- console.error(idmHotspotTextObject["Wystąpił błąd"], err);
- selectedContainerEl.remove();
- }
- }
-
-
- if(selectedContainerEl.dataset?.lazy ||
- !selectedContainerEl.dataset?.lazy && typeof idmGeneralHotspotObjData === "object" && idmGeneralHotspotObjData?.options?.lazy) idmObserveOnce(selectedContainerEl, idmFill);
- else idmFill();
-}
-
-
-
-
-
-// Zebranie wszystkich ramek HTML i wstawienie ich.
-async function idmInsertAllHTMLHotspots(){
- try{
- const reqArr = []
- document.querySelectorAll(".idm__hotspot:not(.--init):not(.--lazy-hotspot)").forEach(hotspot=>{
- reqArr.push(idmInsertHotspotElement(hotspot));
- });
- await Promise.all(reqArr);
- }catch(err){
- console.error(err)
- }
-}
-
-idmInsertAllHTMLHotspots();
-
-/////////////////////////////////////////////////////////////////////
-// wdrożenie dla elementu HTML
-/**
- * Struktura sekcji hotspotu w HTML.
- *
- * @typedef {HTMLElement} HotspotSection
- * @property {string} id - Identyfikator elementu (np. "idmBlogHotspot1").
- * @property {string} class - Klasy CSS używane do stylowania.
- *
- * @attribute {string} data-products-id - Lista ID produktów (rozdzielona przecinkami).
- * @attribute {number} data-products-menu - Identyfikator menu produktów.
- * @attribute {string} data-hotspots-type - Typ hotspotu (np. "promotion").
- * @attribute {boolean} data-lazy - Czy sekcja ma być ładowana w trybie lazy.
- *
- * @example
-
- */
-////////////////////////////////////////////////////
-// Funkcja init OBIEKT JS
-async function idmInsertHotspotObject(idmHotspotObj){
- // Wstaw kontener
- const selectedEl = document.querySelector(idmHotspotObj?.placement.selector);
- if(!selectedEl) throw new Error(idmHotspotTextObject["Nie znaleziono kontenera"]);
-
- selectedEl.insertAdjacentHTML(idmHotspotObj.placement.insert || "afterend", `
-
- ${idmHotspotObj?.title ? `
-
- ${idmHotspotObj.title}
-
- ` : ""}
-
-
-
-
-
- `);
-
-
- // Utworzenie markupa HTML
- const selectedContainerEl = document.getElementById(idmHotspotObj.id);
- if(!selectedContainerEl) throw new Error(idmHotspotTextObject["Nie znaleziono kontenera"]);
-
- const idmFill = async ()=>{
- try{
- let {graphFn, query} = idmGetQueryData({
- productsID: idmHotspotObj?.source?.productsId,
- productsMenu: idmHotspotObj?.source?.productsMenu,
- hotspotsType: idmHotspotObj.source.hotspotsType
- });
-
- if(idmHotspotObj?.query?.graphFn && idmHotspotObj?.query?.string){
- graphFn = idmHotspotObj.query.graphFn;
- query = idmHotspotObj.query.string;
- }
-
- // pobranie danych o produktach
- const products = await idmGetHotspotData(query, graphFn);
- if(!products) throw new Error(idmHotspotTextObject["Nie znaleziono produktów"]);
-
-
- // Wstawienie markupa na strone
- const hotspotMarkup = `${idmPrepareProductsMarkup(products, idmHotspotObj?.options?.addToBasket)}`;
- selectedContainerEl.querySelector(".products.hotspot__products")?.insertAdjacentHTML("beforeend", hotspotMarkup);
- selectedContainerEl.classList.remove("idm-loading");
-
- // init swiper + add to basket
- idmHotspotInit(idmHotspotObj.id, idmHotspotObj?.options)
- }catch(err){
- console.error(idmHotspotTextObject["Wystąpił błąd"], err);
- selectedContainerEl.remove();
- }
- }
-
-
- if(idmHotspotObj?.options?.lazy ||
- typeof idmHotspotObj?.options?.lazy === "undefined" && typeof idmGeneralHotspotObjData === "object" && idmGeneralHotspotObjData?.options?.lazy) idmObserveOnce(selectedContainerEl, idmFill);
- else idmFill();
-}
-
-
-// obiekt js z przykładowymi danymi
-/**
- * Tablica konfiguracji hotspotów rekomendacji.
- *
- * @typedef {object} Hotspot
- * @property {string} id - Identyfikator ramki (required).
- * @property {string} title - Tytuł ramki.
- * @property {string} classes - Dodatkowe klasy CSS.
- * @property {object} placement - Określa, gdzie wstawić ramkę (required).
- * @property {string} placement.selector - Selektor miejsca osadzenia.
- * @property {string} placement.insert - Pozycja wstawienia względem selektora (np. "afterbegin", "beforeend").
- * @property {object} source - Dane źródłowe dla hotspotu (required).
- * @property {string} [source.hotspotType] - Typ hotspotu (np. "promotion").
- * @property {number[]} [source.productsId] - Tablica ID produktów.
- * @property {number} [source.productsMenu] - Identyfikator menu produktów.
- * @property {object} query - Dane zapytania, nadpisują source (DEV).
- * @property {string} query.string - Zapytanie w formacie GraphQL.
- * @property {Function} query.graphFn - Funkcja do pobierania danych.
- * @property {object} options - Ustawienia dla hotspotu (required).
- * @property {boolean} options.lazy - Czy wczytywać w trybie lazy.
- * @property {boolean|string} options.addToBasket - Obsługa koszyka:
- * - true = włącz
- * - false = wyłącz
- * - "range" = dodaj z zakresem
- * @property {boolean|object} options.swiper - Slider:
- * - true = aktywny
- * - false = nieaktywny
- * - object = konfiguracja Swiper
- *
- * @type {Hotspot[]}
- */
-// const idmHotspotArr = [
-// {
-// id: "idmMainHotspot1",//!id ramki
-// title: "Nowoczesna ramka rekomendacji",//
-// classes: "abcdefg",
-// placement: {
-// selector: "#content",
-// insert: "afterbegin"
-// },
-// source: {
-// hotspotType: "protomtion",
-// productsId: [11,12],
-// productsMenu: 122,
-// },
-// query: {
-// string: `searchInput: {productsId : [589, 180, 181590,160740, 155978, 153632, 123350, 82542, 37321, 17040, 25065, 25114, 85452]}`,
-// graphFn: IDM_PRODUCTS_GQL
-// },
-// // addToBasket: true,
-// options: {
-// lazy: false,
-// addToBasket: "range",
-// swiper: true,
-// // swiper: albo true false - albo obiekt z opcjami swipera
-// }
-// },
-// {
-// id: "idmMainHotspot2",
-// title: "Super ramka rekomendacji",
-// placement: {
-// selector: "#content",
-// insert: "beforeend"
-// },
-// source: {
-// productsMenu: 488,
-// },
-// query: {
-// string: `searchInput: {hotspot: promotion,limit: 16}`,
-// graphFn: IDM_HOTSPOTS_GQL
-// },
-// // addToBasket: true,
-// options: {
-// lazy: true,
-// addToBasket: "range",
-// swiper: true,
-// // swiper: albo true false - albo obiekt z opcjami swipera
-// }
-// }
-// ];
-
-
-// Wrzucenie na strone wszystkich ramek z obiektu js
-async function idmInsertAllObjectHotspots(hotspotArr){
- try{
- const reqArr = []
- hotspotArr.forEach(hotspotObj=>{
- reqArr.push(idmInsertHotspotObject(hotspotObj));
- });
- await Promise.all(reqArr);
- }catch(err){
- console.error(err)
- }
-}
-
-
-// idmInsertAllObjectHotspots(idmHotspotArr);
\ No newline at end of file
+///////////////////////////////////////////////
+// GraphQL
+// ogolne
+const priceQuery = `price {
+ rebateCodeActive
+ price {
+ gross {
+ value
+ formatted
+ }
+ }
+ omnibusPrice {
+ gross {
+ value
+ formatted
+ }
+ }
+ omnibusPriceDetails {
+ unit {
+ gross {
+ value
+ formatted
+ }
+ }
+ youSavePercent
+ omnibusPriceIsHigherThanSellingPrice
+ newPriceEffectiveUntil {
+ formatted
+ }
+ }
+ max {
+ gross {
+ value
+ formatted
+ }
+ }
+ unit {
+ gross {
+ value
+ formatted
+ }
+ }
+ unitConvertedPrice {
+ gross {
+ value
+ formatted
+ }
+ }
+ youSavePercent
+ beforeRebate {
+ gross {
+ value
+ formatted
+ }
+ }
+ beforeRebateDetails {
+ youSavePercent
+ unit {
+ gross {
+ value
+ formatted
+ }
+ }
+ }
+ advancePrice {
+ gross {
+ value
+ formatted
+ }
+ }
+ suggested {
+ gross {
+ value
+ formatted
+ }
+ }
+ rebateNumber {
+ number
+ gross {
+ value
+ formatted
+ }
+ }
+ }`;
+
+const productQuery = `id
+ type
+ name
+ zones
+ icon
+ iconSecond
+ iconSmall
+ iconSmallSecond
+ link
+ zones
+ producer{
+ name
+ }
+ category{
+ name
+ }
+ sizes{
+ id
+ amount
+ name
+ ${priceQuery}
+ }
+ group{
+ id
+ name
+ link
+ versions{
+ id
+ name
+ icon
+ iconSecond
+ iconSmall
+ iconSmallSecond
+ }
+ }
+ awardedParameters {
+ name
+ id
+ description
+ values {
+ name
+ id
+ }
+ }
+ enclosuresImages {
+ position
+ url
+ }
+ points
+ unit{
+ id, name, singular, plural, fraction, sellBy, precision, unitConvertedFormat
+ }
+ ${priceQuery}`;
+// 1. products
+const IDM_PRODUCTS_GQL = (args) => JSON.stringify({
+ query: `{
+ products(${args}){
+ took
+ products{
+ ${productQuery}
+ }
+ }
+}`
+});
+
+// 2. hotspots
+const IDM_HOTSPOTS_GQL = (args) => JSON.stringify({
+ query: `{
+ hotspots(${args}){
+ took
+ products{
+ ${productQuery}
+ }
+ }
+}`
+});
+
+// 3. single product
+const IDM_PRODUCT_GQL = (args) => JSON.stringify({
+ query: `{
+ product(${args}){
+ product{
+ ${productQuery}
+ }
+ }
+ }`
+});
+// ADD TO BASKET
+const IDM_HOTSPOT_ADD_TO_BASKET = (t, e, a) => JSON.stringify({
+ query: `mutation {\n addProductsToBasket(ProductInput: {id: ${t}, size: "${e}", quantity: ${a}}) {\n status\n results {\n status\n error {\n code\n message\n }\n productCode\n productId\n sizeId\n quantity\n quantityAvailable\n }\n clientDetailsInBasket {\n id\n login\n firstname\n lastname\n participationPartnerProgram\n usesVat\n email\n isWholesaler\n isWholesalerOrder\n clientIdUpc\n }\n }\n }`
+});
+
+
+///////////////////////////////////////////////////////////
+// TEXT
+const idmHotspotTextObject = {
+ ["Kod rabatowy"]: "Kod rabatowy",
+ ["Okazja"]: "Okazja",
+ ["Promocja"]: "Promocja",
+ ["Bestseller"]: "Bestseller",
+ ["Nowość"]: "Nowość",
+ ["Ilość"]: "Ilość",
+ ["Zwiększ ilość"]: "Zwiększ ilość",
+ ["Zmniejsz ilość"]: "Zmniejsz ilość",
+ ["Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki"]: "Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki",
+ ["Cena regularna"]: "Cena regularna",
+ ["Cena bez kodu"]: "Cena bez kodu",
+ ["Cena nadchodząca od"]: "Cena nadchodząca od",
+ ["Coś poszło nie tak podczas dodawania do koszyka. Spróbuj ponownie lub odśwież stronę"]: "Coś poszło nie tak podczas dodawania do koszyka. Spróbuj ponownie lub odśwież stronę",
+ ["Nie znaleziono produktów"]: "Nie znaleziono produktów",
+ ["Błąd przy pobieraniu danych"]: "Błąd przy pobieraniu danych",
+ ["Kliknij, by przejść do formularza kontaktu"]: "Kliknij, by przejść do formularza kontaktu",
+ ["Cena na telefon"]: "Cena na telefon",
+ ["Dodany"]: "Dodany",
+ ["Wystąpił błąd"]: "Wystąpił błąd",
+ ["Do koszyka"]: "Do koszyka",
+ ["Maksymalna liczba sztuk tego towaru które możesz dodać do koszyka to:"]: "Maksymalna liczba sztuk tego towaru które możesz dodać do koszyka to:",
+ ["Minimalna liczba sztuk tego towaru które możesz dodać do koszyka to:"]: "Minimalna liczba sztuk tego towaru które możesz dodać do koszyka to:",
+ ["Wystąpił błąd z inicjalizacją. Proszę odśwież stronę"]: "Wystąpił błąd z inicjalizacją. Proszę odśwież stronę",
+ ["Nie znaleziono kontenera"]: "Nie znaleziono kontenera",
+ ["Nie znaleziono metody graphql"]: "Nie znaleziono metody graphql",
+}
+//////////////////////////////////////////////////////////////////////////\\\\\\\\\\\\
+// IDOSELL omnibus details
+
+// omnibusDetailsTxt - nadpisać na własny obiekt
+
+app_shop.fn.idmGetOmnibusDetails = (options) => {
+ const {
+ productData, sizeId, priceType = app_shop.vars.priceType,
+ } = options || {};
+ if (!productData) return false;
+ const sizeData = productData.sizes.find((size) => size.id === sizeId) || productData;
+ if (!sizeData?.price) return false;
+ const classes = {
+ add: [],
+ remove: ['--omnibus', '--omnibus-short', '--omnibus-code', '--omnibus-code-short', '--omnibus-new-price', '--omnibus-higher'],
+ };
+ const activeLabel = {};
+
+ const omnibusPrice = sizeData.price?.omnibusPriceDetails?.unit?.[priceType]?.formatted || sizeData.price.omnibusPrice[priceType]?.formatted;
+ if (!omnibusPrice) {
+ return {
+ classes,
+ };
+ }
+ // Omnibus
+ classes.add.push('--omnibus');
+ classes.remove = classes.remove.filter((item) => item !== '--omnibus');
+
+ const sellBy = productData?.unit?.sellBy;
+
+ const unitMaxPrice = sizeData?.price?.unit?.[priceType]?.formatted && sizeData.price.max?.[priceType]?.value ? format_price(parseFloat(sizeData.price.max?.[priceType]?.value) * parseFloat(sellBy), {
+ mask: app_shop.vars.currency_format,
+ currency: app_shop.vars?.currency?.symbol,
+ currency_space: app_shop.vars.currency_space,
+ currency_before_price: app_shop.vars.currency_before_value,
+ }) : false;
+
+ const maxPrice = unitMaxPrice || sizeData.price.max?.[priceType]?.formatted;
+ // Skrócona wersja omnibusa
+ if (!maxPrice || maxPrice === omnibusPrice) {
+ classes.add.push('--omnibus-short');
+ classes.remove = classes.remove.filter((item) => item !== '--omnibus-short');
+ }
+ // Aktywny kod rabatowy
+ if (app_shop.vars.omnibus?.rebateCodeActivate && sizeData.price?.rebateCodeActive) {
+ classes.add.push('--omnibus-code');
+ activeLabel.rebateCodeActive = `${idmHotspotTextObject["Kod rabatowy"]}`;
+ classes.remove = classes.remove.filter((item) => item !== '--omnibus-code');
+ }
+ // Skrócona wersja omnibusa, gdy aktywny kod rabatowy
+ const beforeRebatePrice = sizeData.price.beforeRebateDetails?.unit?.[priceType]?.formatted || sizeData.price.beforeRebate[priceType]?.formatted;
+ if (app_shop.vars.omnibus?.rebateCodeActivate && beforeRebatePrice === omnibusPrice && sizeData.price?.rebateCodeActive) {
+ classes.add.push('--omnibus-code-short');
+ classes.remove = classes.remove.filter((item) => item !== '--omnibus-code-short');
+ }
+ // Nadchodząca cena
+ const newDate = sizeData.price.omnibusPriceDetails?.newPriceEffectiveUntil?.formatted;
+ if (newDate && maxPrice) {
+ classes.add.push('--omnibus-new-price');
+ classes.remove = classes.remove.filter((item) => item !== '--omnibus-new-price');
+ }
+ // Cena omnibusa wyższa niż cena sprzedaży
+ const higher = sizeData.price.omnibusPriceDetails?.omnibusPriceIsHigherThanSellingPrice;
+ if (higher) {
+ classes.add.push('--omnibus-higher');
+ classes.remove = classes.remove.filter((item) => item !== '--omnibus-higher');
+ }
+ // label okazja
+ if ((!higher || newDate) && !activeLabel?.rebateCodeActive) {
+ activeLabel.bargain = `${idmHotspotTextObject["Okazja"]}`;
+ }
+ // label promocja
+ if (Object.keys(activeLabel)?.length === 0) {
+ activeLabel.bargain = `${idmHotspotTextObject["Promocja"]}`;
+ }
+
+ // labele zones
+ if(productData.zones.find(zone => zone ==="bestseller")) activeLabel.bestseller = `${idmHotspotTextObject["Bestseller"]}`;
+ if(productData.zones.find(zone => zone ==="news")) activeLabel.news = `${idmHotspotTextObject["Nowość"]}`;
+
+
+
+ let omnibusPercentSign = '';
+ if (higher) {
+ omnibusPercentSign = '-';
+ } else if (sizeData.price.omnibusPriceDetails?.youSavePercent !== 0) {
+ omnibusPercentSign = '+';
+ }
+ const omnibusPercent = `${omnibusPercentSign}${sizeData.price.omnibusPriceDetails?.youSavePercent}%`;
+ const omnibus = {
+ price: omnibusPrice,
+ visible: true,
+ percent: omnibusPercent,
+ html: `${idmHotspotTextObject['Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki']}: ${omnibusPrice}${omnibusPercent}`,
+ };
+
+ const max = (maxPrice) ? {
+ max: {
+ price: maxPrice,
+ visible: true,
+ percent: `-${sizeData.price.youSavePercent}%`,
+ html: `${idmHotspotTextObject['Cena regularna']}:
+ ${maxPrice}-${sizeData.price.youSavePercent}%`,
+ },
+ } : {};
+
+ const beforeRebate = (beforeRebatePrice) ? {
+ beforeRebate: {
+ price: beforeRebatePrice,
+ visible: !!classes.add.includes('--omnibus-code'),
+ percent: `-${sizeData.price.beforeRebateDetails?.youSavePercent}%`,
+ html: `${idmHotspotTextObject['Cena bez kodu']}:
+ ${beforeRebatePrice}
+ -${sizeData.price.beforeRebateDetails?.youSavePercent}%`,
+ },
+ } : {};
+
+ const newPriceEffectiveUntil = (newDate) ? {
+ newPriceEffectiveUntil: {
+ date: newDate,
+ price: maxPrice,
+ visible: !!classes.add.includes('--omnibus-new-price'),
+ html: `${idmHotspotTextObject['Cena nadchodząca od']}
+ ${newDate}:
+ ${maxPrice}`,
+ },
+ } : {};
+
+
+ return {
+ classes,
+ omnibus,
+ ...max,
+ ...beforeRebate,
+ ...newPriceEffectiveUntil,
+ activeLabel,
+ };
+};
+
+//////////////////////////////////////////////////////////////////////////
+// EVENTY
+// dodawanie do koszyka
+async function idmHandleAddToBasket(e){
+ const formEl = e.target.closest("form.add_to_basket");
+ if(!formEl) return;
+ try{
+ // pobieranie danych i elementów
+ formEl.classList.add("--loading")
+ const buttonEl = formEl.querySelector(".add_to_basket__button");
+ e.preventDefault();
+
+ const id = formEl.querySelector("input[name='product']")?.value;
+ const size = formEl.querySelector("input[type='hidden'][name='size']")?.value;
+ const number = formEl.querySelector("input[name='number']")?.value;
+
+ // dodanie do koszyka
+ const res = await fetch(`/graphql/v1/`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: IDM_HOTSPOT_ADD_TO_BASKET(id, size, number)
+ });
+ const data = await res.json();
+
+ // Błąd
+ if(data?.data?.addProductsToBasket?.status !== "success") throw new Error(data);
+ else{
+ // Obsługiwanie sukcesu
+ app_shop.graphql.trackingEvents(res);
+ buttonEl.classList.add("--success");
+
+ // Dodawanie do koszyka na stronie basketedit.php będzie wymagał innego indywidualnego kodu!!!!!
+ buttonEl.innerHTML = `${buttonEl.dataset.success}`;
+ setTimeout(()=>{
+ buttonEl.innerHTML = `${buttonEl.dataset.text}`;
+ app_shop.fn?.menu_basket_cache?.();
+ buttonEl.classList.remove("--success");
+ }, 3000);
+ }
+ }catch(err){
+ console.error(err);
+ Alertek.Error(idmHotspotTextObject["Coś poszło nie tak podczas dodawania do koszyka. Spróbuj ponownie lub odśwież stronę"]);
+ buttonEl.innerHTML = `${buttonEl.dataset.error}`;
+ buttonEl.classList.add("--error")
+ setTimeout(()=>{
+ buttonEl.classList.remove("--error")
+ buttonEl.innerHTML = `${buttonEl.dataset.text}`;
+ }, 3000);
+ }finally{
+ formEl.classList.remove("--loading")
+ }
+}
+
+///qty
+const idmRangeMaxAlert = (max)=> Alertek.Error(`${idmHotspotTextObject["Maksymalna liczba sztuk tego towaru które możesz dodać do koszyka to:"]} ${max}`)
+const idmRangeMinAlert = (min)=> Alertek.Error(`${idmHotspotTextObject["Minimalna liczba sztuk tego towaru które możesz dodać do koszyka to:"]} ${min}`)
+
+function idmQuantityButtonClick(e){
+ if(e.target.classList.contains("idm-products-banner__qty-input")) return e.target.select();
+ const wrapper = e.target.closest(".idm-products-banner__qty");
+
+ 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;
+ idmRangeMaxAlert(max)
+ }
+ } else if (e.target.classList.contains("idm-products-banner__qty-decrease")) {
+ current -= step;
+ if (current < step){
+ current = step;
+ idmRangeMinAlert(step)
+ }
+ }
+ input.value = current.toFixed(precision);
+}
+function idmQuantityInputChange(e){
+ if(e.target.value > +e.target.max){
+ idmRangeMaxAlert(e.target.max)
+ e.target.value = +e.target.max
+ }
+ if(e.target.value < +e.target.min){
+ idmRangeMinAlert(e.target.min)
+ e.target.value = +e.target.min;
+ }
+}
+
+//////////////////////////////////////////////
+// DANE
+// dwie funkcje zamiast jednej
+async function idmGetHotspotData(query, graphFn){
+ try{
+ const res = await fetch(`/graphql/v1/`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: graphFn ? graphFn(query) : IDM_PRODUCTS_GQL(query)
+ });
+ const data = await res.json();
+ const products = data[graphFn === IDM_HOTSPOTS_GQL ? "hotspots" : "data"]?.products?.products;
+ if(!products) throw new Error(idmHotspotTextObject["Nie znaleziono produktów"]);
+
+ console.log(data);
+ return products;
+ }catch(err){
+ console.error(idmHotspotTextObject["Błąd przy pobieraniu danych"], err);
+ return null;
+ }
+}
+
+
+function idmGetQueryData({
+ productsID,
+ productsMenu,
+ hotspotsType
+}){
+ let graphFn, query;
+
+ if(productsID){
+ graphFn = IDM_PRODUCTS_GQL;
+ query = `searchInput: {productsId: [${productsID}]}`;
+ }else if(productsMenu){
+ graphFn = IDM_PRODUCTS_GQL;
+ query = `searchInput: {navigation: ${productsMenu}}`;
+ }else if(hotspotsType){
+ graphFn = IDM_HOTSPOTS_GQL;
+ query = `searchInput: {hotspot: ${hotspotsType}, limit: 16}`;
+ }
+
+ return {graphFn, query}
+}
+
+//////////////////////////////////////////////
+// LAZY LOADING
+function idmObserveOnce(element, callback, options = { root: null, rootMargin: "0px", threshold: 0.1 }) {
+ if (!element) return;
+
+ const observer = new IntersectionObserver((entries, obs) => {
+ entries.forEach(entry => {
+ if (entry.isIntersecting) {
+ callback(entry); // run your callback
+ obs.disconnect(); // stop observing after first trigger
+ }
+ });
+ }, options);
+
+ observer.observe(element);
+}
+
+
+////////////////////////////////////////////////
+// IDM SET HEIGHT
+app_shop.fn.idmSetHeight = options => {
+ const { selector, selectors, container } = options || {}
+ if ((!selector && !selectors) || !container) return
+
+ const containerElement = document.querySelector(container)
+ if (!containerElement) return
+
+ const adjustAllHeights = itemSelector => {
+ const targets = containerElement.querySelectorAll(itemSelector)
+ if (!targets.length) return
+
+ targets.forEach(el => (el.style.minHeight = ''))
+
+ const max = Math.max(...[...targets].map(el => el.offsetHeight || 0))
+
+ targets.forEach(el => (el.style.minHeight = `${max}px`))
+ }
+
+ if (selector) adjustAllHeights(selector)
+ if (selectors?.length) selectors.forEach(adjustAllHeights)
+}
+//////////////////////////////////////////////////////////////////////////
+// Markup
+
+// Funkcja przygotująca markup dla wszystkich produktów
+function idmPrepareProductsMarkup(products, addToBasket){
+ let markup = "";
+ products.forEach((prod)=>{
+ markup += idmPrepareSingleProductMarkup(prod, addToBasket);
+ })
+ return markup;
+}
+
+// funkcja przygotowująca markup dla wybranego produktu
+function idmPrepareSingleProductMarkup(prod, addToBasket){
+ const prodExchangedData = app_shop.fn?.idmGetOmnibusDetails({productData: prod});
+
+ // pobranie labelek
+ let labelHTMLMarkup = "";
+ if(typeof prodExchangedData.activeLabel === "object") Object.entries(prodExchangedData.activeLabel).forEach(([key,value])=>{
+ labelHTMLMarkup += value;
+ })
+
+ // markup pojedynczego produktu
+ let singleMarkup = "";
+ singleMarkup += `
+ `;
+
+ return singleMarkup;
+}
+
+// markup zdjęcia
+function idmPrepareHotspotImgMarkup(prod){
+ let markup = "";
+ if(prod.iconSmallSecond !== undefined && prod.iconSecond !== undefined) markup +=`
+
+
+
+
+
+ `;
+ else if(prod?.iconSmall !== undefined) markup += `
+
+
+
+ `;
+ else markup += `
`
+ return markup;
+}
+
+// markup cen
+function idmPrepareHotspotPriceMarkup(prod, prodExchangedData){
+ const price = prod.price.price[idmPriceType];
+ const unit = prod.unit;
+ const pointsPrice = prod?.points;
+ const convertedPrice = prod.price?.unitConvertedPrice?.[idmPriceType]?.formatted;
+
+ return `
+
+ ${price.formatted}
+
+ /
+ ${unit?.sellBy}
+ ${unit?.sellBy > 1 ? unit?.plural : unit?.singular}
+
+ ${convertedPrice ? `${convertedPrice}` : ""}
+
+ ${pointsPrice ? `${pointsPrice} pkt.` : ""}
+ ${price.value === 0 ? `${idmHotspotTextObject["Cena na telefon"]}` : ""}
+ ${prodExchangedData?.beforeRebate?.visible ? `${prodExchangedData?.beforeRebate?.html}` : ""}
+ ${prodExchangedData?.newPriceEffectiveUntil?.visible ? `${prodExchangedData?.newPriceEffectiveUntil?.html}` : ""}
+ ${prodExchangedData?.omnibus?.visible ? `${prodExchangedData?.omnibus?.html}` : ""}
+ ${prodExchangedData?.max?.visible ? `${prodExchangedData?.max?.html}` : ""}
+ `;
+}
+
+// markup dodawania do koszyka
+function idmPrepareHotspotAddToBasketMarkup(prod, addToBasket){
+ let markup = "";
+ if(!addToBasket && typeof addToBasket !== "undefined" ||
+ (typeof addToBasket === "undefined" && typeof idmGeneralHotspotObjData === "object" && !idmGeneralHotspotObjData?.options?.addToBasket) ||
+ !addToBasket && typeof idmGeneralHotspotObjData === "undefined") return markup;
+
+ // link do produktu jak nie jest to zwykły produkt
+ if(prod.type !== "product") markup = `Zobacz produkt`;
+ else if(addToBasket === "range"
+ || typeof addToBasket === "undefined" && typeof idmGeneralHotspotObjData === "object" && idmGeneralHotspotObjData?.options?.addToBasket === "range") // +-
+ markup = ``;
+ else // Zwykłe dodanie do koszyka
+ markup = `
+ `;
+ return markup;
+}
+////////////////////////////////////////////////////
+// INIT
+
+// brutto/netto
+const idmPriceType = app_shop?.vars?.priceType || "gross";
+
+
+// Zmienna trzymająca dane o ustawieniach customowych ramek rekomendacji na całym sklepie
+/**
+ * Obiekt konfiguracyjny ogólnych ustawień hotspotów rekomendacji.
+ *
+ * @typedef {object} idmGeneralHotspotObjData
+ * @property {object} options - Główne ustawienia hotspotów
+ * @property {boolean} options.lazy - Czy wczytywać zawartość w trybie lazy (required).
+ * @property {boolean|string} options.addToBasket - Zachowanie przy dodawaniu do koszyka:
+ * - true = przycisk dodaj do koszyka
+ * - false = brak przycisku
+ * - "range" = dodaj z wyborem zakresu (required).
+ * @property {boolean|object} options.swiper - Ustawienia slidera:
+ * - true/false = włącz/wyłącz
+ * - object = konfiguracja instancji Swiper (required jeśli obiekt).
+ */
+const idmGeneralHotspotObjData = {
+ options: {
+ lazy: true,
+ addToBasket: true, // true, false, "range"
+ swiper: { // true, false, obiekt z opcjami swipera
+ loop: false,
+ autoHeight: false,
+ spaceBetween: 16,
+ slidesPerView: 1.4,
+ centeredSlides: true,
+ centeredSlidesBounds: true,
+ breakpoints: {
+ 550: {
+ slidesPerView: 3,
+ centeredSlides: true,
+ centeredSlidesBounds: true,
+ },
+ 979: {
+ slidesPerView: 4,
+ centeredSlides: false,
+ },
+ }
+ }
+ }
+}
+
+// Funkcja inicjalizująca wybranego hotspota(addtobasket range - swiper)
+async function idmHotspotInit(id, options={}){
+ try{
+ const hotspotEl = document.getElementById(id);
+ if(!hotspotEl) throw new Error("Nie znaleziono elementu");
+
+ // add to basket + -
+ if(options?.addToBasket === "range" ||
+ typeof options?.addToBasket === "undefined" && typeof idmGeneralHotspotObjData === "object" && idmGeneralHotspotObjData?.options?.addToBasket === "range"){
+
+ // obsługa i sprawdzanie clicków
+ // hotspotEl.addEventListener("click", e=>{
+ // const wrapper = e.target.closest(".idm-products-banner__qty");
+ // if (!wrapper) return;
+
+ // if(e.target.classList.contains("idm-products-banner__qty-input")) return e.target.select();
+
+ // 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;
+ // rangeMaxAlert(max)
+ // }
+ // } else if (e.target.classList.contains("idm-products-banner__qty-decrease")) {
+ // current -= step;
+ // if (current < step){
+ // current = step;
+ // rangeMinAlert(step)
+ // }
+ // }
+ // input.value = current.toFixed(precision);
+ // });
+ // sprawdzanie na input
+ // hotspotEl.querySelectorAll(".idm-products-banner__qty-input").forEach(inp=>{
+ // inp.addEventListener("input", e=>{
+ // if(e.target.value > +e.target.max){
+ // rangeMaxAlert(e.target.max)
+ // e.target.value = +e.target.max
+ // }
+ // if(e.target.value < +e.target.min){
+ // rangeMinAlert(e.target.min)
+ // e.target.value = +e.target.min;
+ // }
+ // });
+ // })
+
+ }
+
+ // swiper || slick
+ if(options?.swiper ||
+ typeof options?.swiper === "undefined" && typeof idmGeneralHotspotObjData === "object" && idmGeneralHotspotObjData?.options?.swiper){
+
+
+ // Opcje swipera
+ let swiperOptions = typeof options.swiper === "object" ? options.swiper : "";
+ if(typeof options.swiper === "object") swiperOptions = options.swiper;
+ else if(typeof idmGeneralHotspotObjData === "object" && typeof idmGeneralHotspotObjData?.options?.swiper === "object"){
+ swiperOptions = idmGeneralHotspotObjData?.options?.swiper;
+ swiperOptions.navigation = {
+ nextEl: `#${id} .idm-button-next`,
+ prevEl: `#${id} .idm-button-prev`,
+ }
+ }else{
+ swiperOptions = {
+ loop: false,
+ autoHeight: false,
+ spaceBetween: 16,
+ slidesPerView: 1.4,
+ centeredSlides: true,
+ centeredSlidesBounds: true,
+ breakpoints: {
+ 550: {
+ slidesPerView: 3,
+ centeredSlides: true,
+ centeredSlidesBounds: true,
+ },
+ 979: {
+ slidesPerView: 4,
+ centeredSlides: false,
+ },
+ },
+ navigation: {
+ nextEl: `#${id} .idm-button-next`,
+ prevEl: `#${id} .idm-button-prev`,
+ },
+ }
+ }
+
+ // Wywołanie swipera
+ const selectedSwiper = new HotspotSlider({
+ selector: `#${id} .swiper`,
+ hotspotName: `${id}`,
+ options: swiperOptions,
+ });
+ await selectedSwiper.init();
+ }
+
+
+ if(typeof options?.callbackFn === "function") options?.callbackFn();
+
+ // IDM setHeight
+ app_shop.fn.idmSetHeight({
+ selectors: [
+ `#${id} .product__prices`,
+ `#${id} .product__name`,
+ ],
+ container: `#${id} .products__wrapper`,
+ });
+ console.log(`Initialized hotspot #${id}`);
+ }catch(err){
+ console.error(idmHotspotTextObject["Wystąpił błąd z inicjalizacją. Proszę odśwież stronę"], err);
+ }
+}
+
+////////////////////////////////////////////////////
+// Funkcja init ELEMENT HTML
+async function idmInsertHotspotElement(selectedContainerEl){
+
+ selectedContainerEl.classList.add("--init");
+ const {graphFn, query} = idmGetQueryData({
+ productsID: selectedContainerEl?.dataset?.productsId,
+ productsMenu: selectedContainerEl?.dataset?.productsMenu,
+ hotspotsType: selectedContainerEl.dataset.hotspotsType
+ });
+
+ if(!graphFn || !query){
+ console.log(idmHotspotTextObject["Nie znaleziono metody graphql"], selectedContainerEl)
+ return selectedContainerEl.remove();
+ }
+
+ // Funkcja od uzupełniania danych
+ const idmFill = async ()=>{
+ try{
+ // pobranie danych o produktach
+ const products = await idmGetHotspotData(query, graphFn);
+ if(!products) throw new Error(idmHotspotTextObject["Nie znaleziono produktów"]);
+
+
+ // wstawienie produktów zależnie czy w section jest .hotspot czy nie
+ const hotspotInsideEl = selectedContainerEl.querySelector(".hotspot");
+ if(hotspotInsideEl) hotspotInsideEl.innerHTML += `
+
+ ${idmPrepareProductsMarkup(products, true)}
+
+
+
+
+ `;
+ else selectedContainerEl.innerHTML = `
+
+ ${idmPrepareProductsMarkup(products, true)}
+
+
+
+
+
`;
+
+ selectedContainerEl.classList.remove("idm-loading")
+ // init swipera
+ idmHotspotInit(selectedContainerEl.id)
+ }catch(err){
+ console.error(idmHotspotTextObject["Wystąpił błąd"], err);
+ selectedContainerEl.remove();
+ }
+ }
+
+
+ if(selectedContainerEl.dataset?.lazy ||
+ !selectedContainerEl.dataset?.lazy && typeof idmGeneralHotspotObjData === "object" && idmGeneralHotspotObjData?.options?.lazy) idmObserveOnce(selectedContainerEl, idmFill);
+ else idmFill();
+}
+
+
+
+
+
+// Zebranie wszystkich ramek HTML i wstawienie ich.
+async function idmInsertAllHTMLHotspots(){
+ try{
+ const reqArr = []
+ document.querySelectorAll(".idm__hotspot:not(.--init):not(.--lazy-hotspot)").forEach(hotspot=>{
+ reqArr.push(idmInsertHotspotElement(hotspot));
+ });
+ await Promise.all(reqArr);
+ }catch(err){
+ console.error(err)
+ }
+}
+
+idmInsertAllHTMLHotspots();
+
+/////////////////////////////////////////////////////////////////////
+// wdrożenie dla elementu HTML
+/**
+ * Struktura sekcji hotspotu w HTML.
+ *
+ * @typedef {HTMLElement} HotspotSection
+ * @property {string} id - Identyfikator elementu (np. "idmBlogHotspot1").
+ * @property {string} class - Klasy CSS używane do stylowania.
+ *
+ * @attribute {string} data-products-id - Lista ID produktów (rozdzielona przecinkami).
+ * @attribute {number} data-products-menu - Identyfikator menu produktów.
+ * @attribute {string} data-hotspots-type - Typ hotspotu (np. "promotion").
+ * @attribute {boolean} data-lazy - Czy sekcja ma być ładowana w trybie lazy.
+ *
+ * @example
+
+ */
+////////////////////////////////////////////////////
+// Funkcja init OBIEKT JS
+async function idmInsertHotspotObject(idmHotspotObj){
+ // Wstaw kontener
+ const selectedEl = document.querySelector(idmHotspotObj?.placement.selector);
+ if(!selectedEl) throw new Error(idmHotspotTextObject["Nie znaleziono kontenera"]);
+
+ selectedEl.insertAdjacentHTML(idmHotspotObj.placement.insert || "afterend", `
+
+ ${idmHotspotObj?.title ? `
+
+ ${idmHotspotObj.title}
+
+ ` : ""}
+
+
+
+
+
+ `);
+
+
+ // Utworzenie markupa HTML
+ const selectedContainerEl = document.getElementById(idmHotspotObj.id);
+ if(!selectedContainerEl) throw new Error(idmHotspotTextObject["Nie znaleziono kontenera"]);
+
+ const idmFill = async ()=>{
+ try{
+ let {graphFn, query} = idmGetQueryData({
+ productsID: idmHotspotObj?.source?.productsId,
+ productsMenu: idmHotspotObj?.source?.productsMenu,
+ hotspotsType: idmHotspotObj.source.hotspotsType
+ });
+
+ if(idmHotspotObj?.query?.graphFn && idmHotspotObj?.query?.string){
+ graphFn = idmHotspotObj.query.graphFn;
+ query = idmHotspotObj.query.string;
+ }
+
+ // pobranie danych o produktach
+ const products = await idmGetHotspotData(query, graphFn);
+ if(!products) throw new Error(idmHotspotTextObject["Nie znaleziono produktów"]);
+
+
+ // Wstawienie markupa na strone
+ const hotspotMarkup = `${idmPrepareProductsMarkup(products, idmHotspotObj?.options?.addToBasket)}`;
+ selectedContainerEl.querySelector(".products.hotspot__products")?.insertAdjacentHTML("beforeend", hotspotMarkup);
+ selectedContainerEl.classList.remove("idm-loading");
+
+ // init swiper + add to basket
+ idmHotspotInit(idmHotspotObj.id, idmHotspotObj?.options)
+ }catch(err){
+ console.error(idmHotspotTextObject["Wystąpił błąd"], err);
+ selectedContainerEl.remove();
+ }
+ }
+
+
+ if(idmHotspotObj?.options?.lazy ||
+ typeof idmHotspotObj?.options?.lazy === "undefined" && typeof idmGeneralHotspotObjData === "object" && idmGeneralHotspotObjData?.options?.lazy) idmObserveOnce(selectedContainerEl, idmFill);
+ else idmFill();
+}
+
+
+// obiekt js z przykładowymi danymi
+/**
+ * Tablica konfiguracji hotspotów rekomendacji.
+ *
+ * @typedef {object} Hotspot
+ * @property {string} id - Identyfikator ramki (required).
+ * @property {string} title - Tytuł ramki.
+ * @property {string} classes - Dodatkowe klasy CSS.
+ * @property {object} placement - Określa, gdzie wstawić ramkę (required).
+ * @property {string} placement.selector - Selektor miejsca osadzenia.
+ * @property {string} placement.insert - Pozycja wstawienia względem selektora (np. "afterbegin", "beforeend").
+ * @property {object} source - Dane źródłowe dla hotspotu (required).
+ * @property {string} [source.hotspotType] - Typ hotspotu (np. "promotion").
+ * @property {number[]} [source.productsId] - Tablica ID produktów.
+ * @property {number} [source.productsMenu] - Identyfikator menu produktów.
+ * @property {object} query - Dane zapytania, nadpisują source (DEV).
+ * @property {string} query.string - Zapytanie w formacie GraphQL.
+ * @property {Function} query.graphFn - Funkcja do pobierania danych.
+ * @property {object} options - Ustawienia dla hotspotu (required).
+ * @property {boolean} options.lazy - Czy wczytywać w trybie lazy.
+ * @property {boolean|string} options.addToBasket - Obsługa koszyka:
+ * - true = włącz
+ * - false = wyłącz
+ * - "range" = dodaj z zakresem
+ * @property {boolean|object} options.swiper - Slider:
+ * - true = aktywny
+ * - false = nieaktywny
+ * - object = konfiguracja Swiper
+ * @property {Function} options.callbackFn - Funkcja callback która dzieje się po wywołaniu wszystkiego włącznie ze swiperem
+ *
+ * @type {Hotspot[]}
+ */
+// const idmHotspotArr = [
+// {
+// id: "idmMainHotspot1",//!id ramki
+// title: "Nowoczesna ramka rekomendacji",//
+// classes: "abcdefg",
+// placement: {
+// selector: "#content",
+// insert: "afterbegin"
+// },
+// source: {
+// hotspotType: "protomtion",
+// productsId: [11,12],
+// productsMenu: 122,
+// },
+// query: {
+// string: `searchInput: {productsId : [589, 180, 181590,160740, 155978, 153632, 123350, 82542, 37321, 17040, 25065, 25114, 85452]}`,
+// graphFn: IDM_PRODUCTS_GQL
+// },
+// // addToBasket: true,
+// options: {
+// lazy: false,
+// addToBasket: "range",
+// swiper: true,
+// callbackFn: ()=>{console.log("test")}
+// // swiper: albo true false - albo obiekt z opcjami swipera
+// }
+// },
+// {
+// id: "idmMainHotspot2",
+// title: "Super ramka rekomendacji",
+// placement: {
+// selector: "#content",
+// insert: "beforeend"
+// },
+// source: {
+// productsMenu: 488,
+// },
+// query: {
+// string: `searchInput: {hotspot: promotion,limit: 16}`,
+// graphFn: IDM_HOTSPOTS_GQL
+// },
+// // addToBasket: true,
+// options: {
+// lazy: true,
+// addToBasket: "range",
+// swiper: true,
+// // swiper: albo true false - albo obiekt z opcjami swipera
+// }
+// }
+// ];
+
+
+// Wrzucenie na strone wszystkich ramek z obiektu js
+async function idmInsertAllObjectHotspots(hotspotArr){
+ try{
+ const reqArr = []
+ hotspotArr.forEach(hotspotObj=>{
+ reqArr.push(idmInsertHotspotObject(hotspotObj));
+ });
+ await Promise.all(reqArr);
+ }catch(err){
+ console.error(err)
+ }
+}
+
+
+// idmInsertAllObjectHotspots(idmHotspotArr);
diff --git a/ramka.txt b/ramka.txt
index f5a916b..d4b2c48 100644
--- a/ramka.txt
+++ b/ramka.txt
@@ -6,6 +6,9 @@
- wybór rozmiaru/wersji??
- Wybór kolorystyczny
- AAAAA - banner na hotspocie
+- Callback FN
+- set Height
+- Pasek jak na Pasiastym Parzystnokopytnym Kosmetyku
- własne klasy
- aplikacja do zarządania dodatkiem od obiektów hotspot
@@ -15,8 +18,15 @@ Stara ramka
- slick
+JAK ROBIĆ CALLBACKI???
+WIELE OPCJI W STYLU PRZED SWIPEREM, PO SWIPERZE
+
+
bramka z hotspots jeszcze nie działa bad request
-Get-Content 1graphQL.js,2funkcje.js,3markup.js,4init.js,5ainsertHotspotHTML.js,5binsertHotspotObject.js,6style.js | Set-Content bundle.js
\ No newline at end of file
+Get-Content 1graphQL.js,2funkcje.js,3markup.js,4init.js,5ainsertHotspotHTML.js,5binsertHotspotObject.js | Set-Content bundle.js
+
+
+Get-Content sklad/1graphQL.js,sklad/2funkcje.js,sklad/3markup.js,sklad/4init.js,sklad/5ainsertHotspotHTML.js,sklad/5binsertHotspotObject.js | Set-Content bundle.js
\ No newline at end of file