PageSourceSearch

https://www.forumginekologii.pl/upl/price-details-lib-v2.js

js forumginekologii.pl collected 2026-09-28 06:37:08 UTC 13,790 bytes, 399 lines download raw bytes

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.