Wybór Rozmiarów - wersja prosta wizualne

This commit is contained in:
2025-12-16 14:11:59 +01:00
parent 93f91a4b16
commit 61d6b024cc
3 changed files with 129 additions and 54 deletions

129
klasa.js
View File

@@ -485,6 +485,7 @@ class IdmHotspot{
this.query = query || {};
// this.type = type;
this.products = products || null;
this.productsVersions = [];
this.hotspotEl = hotspotEl || null;
@@ -632,9 +633,6 @@ class IdmHotspot{
}
markupProductInnerHTML(prod){
// IDM DO POPRAWKI
const prodExchangedData = app_shop?.fn?.getOmnibusDetails?.({productData: prod}) || app_shop.fn?.idmGetOmnibusDetails({productData: prod});
return `<div class="product__yousave --hidden">
<span class="product__yousave --label"></span>
<span class="product__yousave --value"></span>
@@ -652,9 +650,7 @@ class IdmHotspot{
${this.markupVersions(prod)}
${this.markupOpinions(prod)}
<a class="product__name" tabindex="0" href="${prod.link}" title="${prod.name}">${prod.name}</a>
<div class="product__prices mb-auto ${prodExchangedData?.classes?.add?.reduce((acc,val) => acc + " " + val,"")}">
${this.markupPrice(prod, prodExchangedData)}
</div>
${this.markupPrice({prod})}
${this.markupAddToBasket(prod)}
</div>`;
}
@@ -790,13 +786,26 @@ class IdmHotspot{
</div>`
}
markupPrice(prod, prodExchangedData){
const price = prod.price.price[this.priceType];
markupPrice({prod, sizeId}){
let priceRoot = prod.price;
const getOmnibusDetailsObject = {productData: prod};
// Znalezienie ceny wybranego rozmiaru
if(this.options?.selectSize){
const currentSizeId = sizeId || prod.sizes?.find(size=>size.amount!==0)?.id || prod.sizes?.size?.[0]?.id;
priceRoot = prod.sizes.find(size=>size.id === currentSizeId)?.price || priceRoot;
getOmnibusDetailsObject.sizeId = currentSizeId;
};
const prodExchangedData = app_shop?.fn?.getOmnibusDetails?.(getOmnibusDetailsObject) || app_shop.fn?.idmGetOmnibusDetails(getOmnibusDetailsObject);
const price = priceRoot.price[this.priceType];
const unit = prod.unit;
const pointsPrice = prod?.points;
const convertedPrice = prod.price?.unitConvertedPrice?.[this.priceType]?.formatted;
const convertedPrice = priceRoot?.unitConvertedPrice?.[this.priceType]?.formatted;
return `
<div class="product__prices mb-auto ${prodExchangedData?.classes?.add?.reduce((acc,val) => acc + " " + val,"")}">
<strong class="price --normal --main ${price.value === 0 ? "--hidden" : ""}">
<span class="price__sub">${price.formatted}</span>
<span class="price_sellby">
@@ -812,6 +821,7 @@ class IdmHotspot{
${prodExchangedData?.newPriceEffectiveUntil?.visible ? `<span class="price --new-price new_price">${prodExchangedData?.newPriceEffectiveUntil?.html}</span>` : ""}
${this.markupOmnibus(prodExchangedData?.omnibus)}
${prodExchangedData?.max?.visible ? `<span class="price --max">${prodExchangedData?.max?.html}</span>` : ""}
</div>
`;
}
@@ -848,6 +858,7 @@ class IdmHotspot{
if(!this.options.addToBasket) return markup;
const prodTotalAmount = this.getProdTotalAmount(prod);
const prodCurrentSizeAmount = this.options.selectSize ? prod.sizes.find(size=>size.amount !== 0).amount : prod.sizes[0].amount;
// link do produktu jak nie jest to zwykły produkt
@@ -860,7 +871,7 @@ class IdmHotspot{
<div class="idm-products-banner__qty"
data-sell-by="${prod.unit?.sellBy}"
data-precision="${prod.unit?.precision}"
data-max="${prod.sizes[0].amount === -1 ? 999999 : prod.sizes[0].amount}"
data-max="${prodCurrentSizeAmount === -1 ? 999999 : prodCurrentSizeAmount}"
>
<button type="button" class="idm-products-banner__qty-decrease" aria-label="${idmHotspotTextObject["Zmniejsz ilość"]}"></button>
@@ -870,7 +881,7 @@ class IdmHotspot{
value="${prod.unit?.sellBy}"
step="${prod.unit?.sellBy}"
min="${prod.unit?.sellBy}"
max="${prod.sizes[0].amount === -1 ? 999999 : prod.sizes[0].amount}"
max="${prodCurrentSizeAmount === -1 ? 999999 : prodCurrentSizeAmount}"
aria-label="${idmHotspotTextObject["Ilość"]}"
>
@@ -1181,12 +1192,38 @@ class IdmHotspot{
const inputEl = e.target.closest("input[type='radio']");
if(!inputEl) return;
const newValue = inputEl?.dataset?.value;
//1. Zmiana Wybranego inputa
const sizeId = inputEl?.dataset?.value;
const hiddenSizeInputEl = e.target.closest("form.add_to_basket")?.querySelector("input[type='hidden'][name='size']");
if(!hiddenSizeInputEl || !newValue) return inputEl.checked = false;
if(!hiddenSizeInputEl || !sizeId) return inputEl.checked = false;
hiddenSizeInputEl.value = newValue;
hiddenSizeInputEl.value = sizeId;
// 2. Zmiana ceny
const productEl = e.target.closest(".product.hotspot__product");
const productData = this.searchForProductData(+productEl?.dataset.id)
const currSizeData = productData?.sizes.find(size=>size.id === sizeId);
if(!currSizeData || !productData) return;
const priceEl = productEl.querySelector(".product__prices");
if(!priceEl) return;
priceEl.outerHTML = this.markupPrice({prod: productData, sizeId});
this.setHeightDefault();
// 3. Zmiana maksymalnej ilości produktu dozwolonego do kupienia dla range
if(this.options.addToBasket === "range"){
const qtyContainer = productEl.querySelector(".idm-products-banner__qty");
const qtyInput = qtyContainer?.querySelector("input.idm-products-banner__qty-input");
if(!qtyContainer || !qtyInput) return console.error("Brak elementów qty");
qtyContainer.dataset.max = currSizeData.amount;
qtyInput.max = currSizeData.amount;
if(qtyInput.value > currSizeData.amount) qtyInput.value = currSizeData.amount - (currSizeData.amount % +qtyInput.step);
}
}
/**
@@ -1224,6 +1261,7 @@ class IdmHotspot{
}
cssVariableVersionColumnCount(){
if(!this.options?.selectVersion) return false;
this.cssSetVariable("--version-desktop-columns", this.cssVariables?.version?.columnDesktop || 5)
this.cssSetVariable("--version-tablet-columns", this.cssVariables?.version?.columnTablet || 3)
this.cssSetVariable("--version-mobile-columns", this.cssVariables?.version?.columnMobile || 4)
@@ -1290,7 +1328,7 @@ class IdmHotspot{
cssInsertStyleTag(){
this.hotspotEl.insertAdjacentHTML("beforeend", `
<style>
${this.cssVersionColumnStyle()}
${this.options?.selectVersion ? this.cssVersionColumnStyle() : ""}
${this.cssSkeletonStyle()}
</style>`)
}
@@ -1305,7 +1343,7 @@ class IdmHotspot{
// ========================================================
getProdTotalAmount(prod){
return prod.sizes.reduce((acc, val) => val === -1 || acc === -1 ? -1 : acc + val, 0);
return prod.sizes.reduce((acc, val) => val.amount === -1 || acc === -1 ? -1 : acc + val.amount, 0);
}
/**
@@ -1347,22 +1385,38 @@ class IdmHotspot{
if (selectors?.length) selectors.forEach(adjustAllHeights)
}
setHeightDefault(){
this.setHeight({
selectors: [
`#${this.id} .product__prices`,
`#${this.id} .product__name`,
],
container: `#${this.id} .products__wrapper`,
});
}
/**
* Przeładowanie pojedynczego produktu
*/
async reloadProduct(prodEl, newProdId){
try{
prodEl.classList.add("--loading");
const res = await fetch(`/graphql/v1/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: IDM_PRODUCT_GQL(`productId: ${newProdId}`, this.priceType),
});
const data = await res.json();
let productData = this.searchForProductData(newProdId);
if(!productData){
const res = await fetch(`/graphql/v1/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: IDM_PRODUCT_GQL(`productId: ${newProdId}`, this.priceType),
});
const data = await res.json();
productData = data?.data?.product?.product;
if(productData) this.productsVersions.push(productData);
}
const productData = data?.data?.product?.product;
if(!productData) throw new Error("Nie udało się pobrać danych o produkcie");
@@ -1374,13 +1428,7 @@ class IdmHotspot{
else prodEl.classList.remove("--phone");
this.initSingleEvent(prodEl);
this.setHeight({
selectors: [
`#${this.id} .product__prices`,
`#${this.id} .product__name`,
],
container: `#${this.id} .products__wrapper`,
});
this.setHeightDefault();
}catch(err){
Alertek?.Error(idmHotspotTextObject["Błąd przy pobieraniu danych"]);
console.error(err);
@@ -1389,6 +1437,15 @@ class IdmHotspot{
}
}
searchForProductData(searchedId){
let productData;
productData = this.products.find(prod=>prod.id === +searchedId) || this.productsVersions.find(prod=>prod.id === +searchedId) || null;
return productData;
}
// ========================================================
// XML to GraphQL
// ========================================================
@@ -1741,13 +1798,7 @@ class IdmHotspot{
await this.initSwiper();
// IDM setHeight
this.setHeight({
selectors: [
`#${this.id} .product__prices`,
`#${this.id} .product__name`,
],
container: `#${this.id} .products__wrapper`,
});
this.setHeightDefault();
this.initEvents();
console.log(`Initialized hotspot #${this.id}`);

View File

@@ -2,16 +2,18 @@
bugi selectSize + addToBasket="range"
- wybór rozmiaru/wersji?? Wykluczenie powtarzających się wersji
- Wykluczenie powtarzających się wersji
wersja max 5 zdjęć i później + może????
Wyświetlanie filmu na hover?
- zakres cen?????????????
- Wybór kolorystyczny???
- AAAAA - banner na hotspocie
- stary szablon
- blokowanie dodawania do koszyka jeśli ilość jest maksymalna
- informacje o producencie albo serri gdzieś nad produktem
- blokowanie dodawania do koszyka jeśli ilość jest maksymalna(chyba tylko nowy szablon ma app_shop.vars.basket)
- informacje o producencie albo seri gdzieś nad produktem
- czy zapisywać wszystkie hotspoty do jakiegoś app_shop.fn.idmHotspots = {"#idmMainHotspot1": {}}
- czy jakoś inteligentnie ucinać niepotrzebne query na podstawie wybranych opcji? chyba za dużo roboty i nie ma sensu
@@ -19,13 +21,15 @@ wersja max 5 zdjęć i później + może????
- własne klasy
Stara ramka
- getProductXML=t
- slick
cacheowanie ramek do indexedDB
cacheowanie ramek do indexedD
awaitowanie idmHotspot
zapisywanie querySelectorów produktów??
awaitowanie idmHotspot???(raczej nie przez intersection observer)
fix cssVariables jak nie ma wesji
zapisywanie querySelectorów produktów
rozmiary i wersje - dropdown
Dodawanie do this.products klikniętego produktu na wersje, ale tak żeby nie wczytywał się ponownie, jakaś baza danych wersji??

View File

@@ -131,6 +131,14 @@
top: -1px;
transition: all 0.3s;
border-radius: 5px;
&:after{
content: "";
position: absolute;
opacity: 0;
height: calc(100% + 8px);
top: -4px;
width: 100%;
}
}
@@ -400,24 +408,28 @@
gap: 1rem;
padding: 0.3rem;
background: #fff;
width: 100%;
input[type="radio"]{
display: none;
}
.product__size_select{
width: 4.5rem;
// width: 4.5rem;
width: calc(26% - 1.5rem);
height: 4.5rem;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
border: 1px solid #ccc;
transition: border 0.2s;
&:hover{
border-color: #000;
}
&:not(:has(input:checked)){
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
border-radius: 8px;
&:has(input:disabled){
opacity: 0.6;
cursor: not-allowed!important;
}
&:has(input:checked){
box-shadow: 0 0 0 1px #000;
border-color: #000;
@@ -433,6 +445,14 @@
top: calc(100%);
padding-top: 1rem;
}
.product__select_sizes .product__size_select{
&:hover:not(:has(input:disabled)){
border-color: #000;
}
&:not(:has(input:checked)){
cursor: pointer;
}
}
.product:hover .product__select_sizes{
clip-path: inset(0 0 0 0);
}