1/** 2 * PriceDetailsLib - Biblioteka do obsÅugi wyÅwietlania szczegóÅów cenowych produktów z GOS 3 * 4 * Przeznaczenie: 5 * - Pobieranie danych cenowych dla pojedynczych lub wielu wariantów produktów 6 * - Automatyczne wyÅwietlanie cen w elementach HTML 7 * - Automatyczne pokazywanie/ukrywanie elementów zależnie od statusu kuponu 8 * - Odliczanie czasu do koÅca promocji 9 * - WyÅwietlanie tekstów promocyjnych 10 * 11 * Jak korzystaÄ z klas CSS: 12 * - .price-net - dla wyÅwietlania ceny netto 13 * - .price-gross - dla wyÅwietlania ceny brutto 14 * - .price-after-coupon-net - dla wyÅwietlania ceny promocyjnej netto 15 * - .price-after-coupon-gross - dla wyÅwietlania ceny promocyjnej brutto 16 * - .omnibus-net - dla wyÅwietlania najniższej ceny z 30 dni netto 17 * - .omnibus-gross - dla wyÅwietlania najniższej ceny z 30 dni brutto 18 * - .show-if-coupon-active - element bÄdzie widoczny tylko gdy kupon jest aktywny 19 * - .hide-if-coupon-active - element bÄdzie widoczny tylko gdy kupon jest nieaktywny 20 * - .coupon-countdown - dla odliczania czasu do koÅca promocji 21 * - .custom-promo-1 - dla wyÅwietlania tekstu promocyjnego 1 22 * - .custom-promo-2 - dla wyÅwietlania tekstu promocyjnego 2 23 */ 24 25class PriceDetailsLib { 26 constructor(apiBaseUrl = "https://www.gos.e-firma.pl/apiv2") { 27 this.apiBaseUrl = apiBaseUrl; 28 this.timers = {}; // Przechowuje liczniki promocji 29 this.axios = axios; // ZakÅadamy, że axios jest dostÄpny globalnie 30 } 31 32 init(config = {}) { 33 if (config.apiBaseUrl) { 34 this.apiBaseUrl = config.apiBaseUrl; 35 } 36 37 return this; 38 } 39 40 /** 41 * Pobiera szczegóÅy cenowe dla pojedynczego wariantu produktu 42 * 43 * @param {string|number} variantId - ID wariantu produktu 44 * @param {string} couponCode - Kod kuponu rabatowego 45 * @returns {Promise<Object>} Obiekt ze szczegóÅami cenowymi 46 * 47 * PrzykÅad użycia: 48 * const priceDetails = await priceLib.getPriceDetails('4666-1', 'testapicode'); 49 */ 50 async getPriceDetails(variantId, couponCode) { 51 couponCode = encodeURIComponent(couponCode); 52 if (isNaN(+variantId)) { 53 try { 54 const response = await this.axios.get( 55 `${this.apiBaseUrl}/product-variant/price-details?productVariantNoComplete=${variantId}&couponCode=${couponCode}`, 56 ); 57 58 return response.data; 59 } catch (error) { 60 console.error("BÅÄ d podczas pobierania szczegóÅów cenowych:", error); 61 throw error; 62 } 63 } else if (!isNaN(+variantId)) { 64 try { 65 const response = await this.axios.get( 66 `${this.apiBaseUrl}/product-pack/price-details/${variantId}/${couponCode}`, 67 ); 68 return response.data; 69 } catch (error) { 70 console.error("BÅÄ d podczas pobierania szczegóÅów cenowych:", error); 71 throw error; 72 } 73 } else { 74 console.error("BÅÄ d w ", variantId); 75 } 76 } 77 78 /** 79 * Pobiera szczegóÅy cenowe dla wielu wariantów produktów na raz 80 * Pozwala zminimalizowaÄ liczbÄ zapytaÅ API przy wyÅwietlaniu wielu wariantów 81 * 82 * @param {Array<Object>} variants - Tablica obiektów { variantId, couponCode } 83 * @returns {Promise<Object>} Mapa szczegóÅów cenowych wariantów 84 * 85 * PrzykÅad użycia: 86 * const bulkData = await priceLib.getBulkPriceDetails([ 87 * { variantId: '4666-1', couponCode: 'testapicode' }, 88 * { variantId: '4666-2', couponCode: 'testapicode' } 89 * ]); 90 */ 91 async getBulkPriceDetails(variants) { 92 const results = {}; 93 const fetchPromises = []; 94 95 for (const { variantId, couponCode } of variants) { 96 // Tworzymy tablicÄ dla każdego wariantu 97 98 fetchPromises.push( 99 this.getPriceDetails(variantId, couponCode) 100 .then((data) => { 101 results[`${variantId}-${couponCode}`] = data; 102 }) 103 .catch((error) => { 104 results[`${variantId}-${couponCode}`] = { error }; 105 }), 106 ); 107 } 108
109 // Czekamy na zakoÅczenie wszystkich zapytaÅ 110 await Promise.all(fetchPromises); 111 return results; 112 } 113 114 /** 115 * Aktualizuje elementy DOM cenami i danymi promocyjnymi 116 * Jest to gÅówna metoda do aktualizacji interfejsu użytkownika 117 * 118 * @param {string} selector - Selektor CSS elementu nadrzÄdnego (np. '#variant-4666-1') 119 * @param {Object} priceDetails - Dane szczegóÅów cenowych z API 120 */ 121 updatePriceElements(selector, priceDetails) { 122 const container = document.querySelector(selector); 123 if (!container) { 124 console.warn(`Element o selektorze "${selector}" nie istnieje w DOM`); 125 return; 126 } 127 128 if (priceDetails.error) { 129 console.warn(`BÅÄ d danych dla wariantu ${selector}:`); 130 return; 131 } 132 133 // Aktualizuj elementy z cenami 134 this._updateElement(container, ".price-net", `${priceDetails.priceNet} zÅ`); 135 this._updateElement( 136 container, 137 ".price-gross", 138 `${priceDetails.priceGross} zÅ`, 139 ); 140 this._updateElement( 141 container, 142 ".price-after-coupon-net", 143 `${priceDetails.priceAfterCouponNet} zÅ`, 144 ); 145 this._updateElement( 146 container, 147 ".price-after-coupon-gross", 148 `${priceDetails.priceAfterCouponGross} zÅ`, 149 ); 150 this._updateElement( 151 container, 152 ".omnibus-net", 153 `Najniższa cena z 30 dni przed promocjÄ : ${priceDetails.omnibusNet} zÅ netto.`, 154 ); 155 this._updateElement( 156 container, 157 ".omnibus-gross", 158 `Najniższa cena z 30 dni przed promocjÄ : ${priceDetails.omnibusGross} zÅ brutto.`, 159 ); 160 161 // Aktualizuj elementy promocyjne 162 this._updateElement( 163 container, 164 ".custom-promo-1", 165 priceDetails.customPromo1, 166 ); 167 this._updateElement( 168 container, 169 ".custom-promo-2", 170 priceDetails.customPromo2, 171 ); 172 173 // Aktualizuj widocznoÅÄ elementów zależnych od kuponu 174 this._toggleCouponElements( 175 container, 176 priceDetails.couponActive, 177 priceDetails.couponType, 178 priceDetails.discountPack, 179 ); 180 181 // Ustaw timer jeÅli kupon jest aktywny 182 if (priceDetails.couponActive && priceDetails.couponValidUntil) { 183 this.startCountdown( 184 `${selector} .coupon-countdown`, 185 priceDetails.couponValidUntil, 186 ); 187 } 188 } 189 190 /** 191 * Zaczyna odliczanie do koÅca ważnoÅci kuponu 192 * 193 * @param {string} selector - Selektor CSS elementu licznika 194 * @param {string} validUntil - Data koÅca ważnoÅci kuponu (format "YYYY-MM-DD HH:MM:SS") 195 */ 196 startCountdown(selector, validUntil) { 197 const countdownElement = document.querySelector(selector); 198 if (!countdownElement) { 199 console.warn( 200 `Element licznika o selektorze "${selector}" nie istnieje w DOM`, 201 ); 202 return; 203 } 204 205 // UsuÅ poprzedni timer jeÅli istnieje 206 if (this.timers[selector]) { 207 clearInterval(this.timers[selector]); 208 } 209 210 const updateCountdown = () => { 211 const now = new Date(); 212 const endTime = new Date(validUntil); 213 const timeDiff = endTime - now; 214 215 if (timeDiff <= 0) { 216 // Czas siÄ skoÅczyÅ 217 clearInterval(this.timers[selector]); 218 countdownElement.textContent = "Promocja zakoÅczona"; 219 return; 220 } 221 222 // Oblicz dni, godziny, minuty i sekundy 223 const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24)); 224 const hours = Math.floor( 225 (timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60), 226 ) 227 .toString() 228 .padStart(2, "0"); 229 const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60)) 230 .toString() 231 .padStart(2, "0"); 232 const seconds = Math.floor((timeDiff % (1000 * 60)) / 1000) 233 .toString() 234 .padStart(2, "0"); 235 236 // Zaktualizuj tekst licznika 237 let timerNode = `<div class="ft__container"> 238 <div class="ft__display"> 239 <div class="ft__item timer__days"> 240 <div class="ft__plate">${days}</div> 241 <div class="ft__label">dni</div> 242 </div> 243 <div class="ft__dots">:</div> 244 <div class="ft__item timer__hours"> 245 <div class="ft__plate">${hours}</div> 246 <div class="ft__label">godz.</div> 247 </div> 248 <div class="ft__dots">:</div> 249 <div class="ft__item timer__minutes"> 250 <div class="ft__plate">${minutes}</div> 251 <div class="ft__label">min.</div> 252 </div> 253 <div class="ft__dots">:</div> 254 <div class="ft__item timer__seconds"> 255 <div class="ft__plate">${seconds}</div> 256 <div class="ft__label">sek.</div> 257 </div> 258 </div> 259 </div>`; 260 261 countdownElement.innerHTML = timerNode; 262 }; 263 264 // Natychmiastowa aktualizacja 265 updateCountdown(); 266 267 // Ustaw interwaÅ na co sekundÄ 268 this.timers[selector] = setInterval(updateCountdown, 1000); 269 } 270 271 /** 272 * PrzeÅÄ cza widocznoÅÄ elementów zależnych od aktywnoÅci kuponu 273 * 274 * @param {Element} container - Element nadrzÄdny 275 * @param {boolean} couponActive - Czy kupon jest aktywny 276 * @param {string} couponType - Typ kuponu 277 * @param {boolean} discountPack - Czy pakiet rabatowy jest aktywny 278 */ 279 280 _toggleCouponElements(container, couponActive, couponType, discountPack) { 281 const showIfActive = container.querySelectorAll(".show-if-coupon-active"); 282 const showIfGratisActive = container.querySelectorAll( 283 ".show-if-gratis-coupon-active", 284 ); 285 const showIfDiscountPackActive = container.querySelectorAll( 286 ".show-if-discount-pack-active", 287 ); 288 const hideIfActive = container.querySelectorAll(".hide-if-coupon-active"); 289 // const netPrices = container.querySelectorAll(".price-net"); 290 // const grossPrices = container.querySelectorAll(".price-gross"); 291 if (couponActive && couponType !== "gratisCoupon") { 292 showIfActive.forEach((element) => { 293 element.style.display = couponActive ? "block" : "none"; 294 }); 295 } else if (couponActive && couponType === "gratisCoupon") { 296 showIfGratisActive.forEach((element) => { 297 element.style.display = 298 couponType === "gratisCoupon" ? "block" : "none"; 299 }); 300 } else if ( 301 discountPack === false && 302 (couponActive === undefined || !couponActive) 303 ) { 304 showIfDiscountPackActive.forEach((element) => { 305 element.style.display = !discountPack ? "block" : "none"; 306 }); 307 } 308 309 hideIfActive.forEach((element) => { 310 element.style.display = couponActive ? "none" : "block"; 311 });
312 313 // netPrices.forEach(element => { 314 // element.style.color = couponActive ? 'red' : ''; 315 // }); 316 317 // netPrices.forEach((element) => { 318 // if (couponActive) { 319 // element.classList.remove("highlightedprice"); 320 // } else { 321 // element.classList.add("highlightedprice"); 322 // } 323 // }); 324 325 // grossPrices.forEach((element) => { 326 // if (couponActive) { 327 // element.classList.remove("highlightedprice"); 328 // } else { 329 // element.classList.add("highlightedprice"); 330 // } 331 // }); 332 } 333 334 /** 335 * Aktualizuje zawartoÅÄ elementu DOM 336 * 337 * @param {Element} container - Element nadrzÄdny 338 * @param {string} selector - Selektor CSS elementu do aktualizacji 339 * @param {any} value - Nowa wartoÅÄ 340 */ 341 _updateElement(container, selector, value) { 342 const element = container.querySelector(selector); 343 if (element && value !== undefined) { 344 element.textContent = value; 345 } 346 } 347} 348 349// Eksportuj bibliotekÄ 350if (typeof module !== "undefined" && module.exports) { 351 module.exports = PriceDetailsLib; 352} else { 353 window.PriceDetailsLib = PriceDetailsLib; 354} 355 356/** 357 * PRZYKÅADY UÅ»YCIA: 358 * 359 * 1. Inicjalizacja: 360 * const priceLib = new PriceDetailsLib().init(); 361 * 362 * 2. Pobieranie danych dla pojedynczego wariantu: 363 * const priceDetails = await priceLib.getPriceDetails('4666-1', 'testapicode'); 364 * 365 * 3. Pobieranie danych dla wielu wariantów: 366 * const bulkData = await priceLib.getBulkPriceDetails([ 367 * { variantId: '4666-1', couponCode: 'testapicode' }, 368 * { variantId: '4666-2', couponCode: 'testapicode' } 369 * ]); 370 * 371 * 4. Aktualizacja DOM: 372 * // Dla pojedynczego wariantu 373 * priceLib.updatePriceElements('#variant-4666-1', priceDetails); 374 * 375 * // Dla wielu wariantów 376 * priceLib.updatePriceElements('#variant-4666-1', bulkData['4666-1-testapicode']); 377 * priceLib.updatePriceElements('#variant-4666-2', bulkData['4666-2-testapicode']); 378 * 379 * 5. Struktura HTML dla wariantu: 380 * <div id="variant-4666-1"> 381 * <div>Cena netto: <span class="price-net"></span> zÅ</div> 382 * <div>Cena brutto: <span class="price-gross"></span> zÅ</div> 383 * 384 * <!-- Ceny Omnibus (dyrektywa) --> 385 * <div>Najniższa cena z 30 dni (netto): <span class="omnibus-net"></span> zÅ</div> 386 * <div>Najniższa cena z 30 dni (brutto): <span class="omnibus-gross"></span> zÅ</div> 387 * 388 * <div class="show-if-coupon-active"> 389 * <div>Promocja netto: <span class="price-after-coupon-net"></span> zÅ</div> 390 * <div>Promocja brutto: <span class="price-after-coupon-gross"></span> zÅ</div> 391 * <div>PozostaÅo: <span class="coupon-countdown"></span></div> 392 * <div class="custom-promo-1"></div> 393 * </div> 394 * 395 * <div class="hide-if-coupon-active"> 396 * <p>Brak aktywnych promocji</p> 397 * </div> 398 * </div> 399 */
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.