> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Zdarzenia

> Wszystkie zdarzenia Aftersell Cart SDK: kiedy każde z nich się uruchamia, co Ci przekazuje, do czego go używać oraz błędy powodujące nieskończone pętle.

Zdarzenia pozwalają uruchamiać kod, **gdy coś się dzieje** w koszyku. Znajdują się pod `window.aftersell.cart.events`.

Subskrybowanie to wywołanie konfiguracyjne, więc jest bezpieczne na górze Twojego skryptu, bez potrzeby czekania na `ready()`.

<div id="available-events">
  ## Dostępne zdarzenia
</div>

| Zdarzenie                                     | Payload                                               | Uruchamia się, gdy                                      |
| --------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------- |
| [`cart_loaded`](#cart_loaded)                 | [`AftersellCart`](/pl/aftersell/cart/sdk-cart-object) | Koszyk się ładuje, raz na stronę.                       |
| [`cart_updated`](#cart_updated)               | [`AftersellCart`](/pl/aftersell/cart/sdk-cart-object) | Zawartość koszyka zmienia się po pierwszym załadowaniu. |
| [`item_added`](#item_added)                   | `{ item }`                                            | W koszyku pojawia się nowa pozycja.                     |
| [`item_removed`](#item_removed)               | `{ item }`                                            | Pozycja znika z koszyka.                                |
| [`cart_opened`](#cart_opened-and-cart_closed) | Brak                                                  | Drawer się otwiera.                                     |
| [`cart_closed`](#cart_opened-and-cart_closed) | Brak                                                  | Drawer się zamyka.                                      |
| [`checkout`](#checkout)                       | Brak                                                  | Kliknięto przycisk checkoutu.                           |

<div id="subscribing">
  ## Subskrybowanie
</div>

`events.on(event, handler)` rejestruje handler i **zwraca funkcję, która go wyrejestrowuje**:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const off = window.aftersell.cart.events.on('cart_updated', (state) => {
  console.log('Cart total is now', state.totalPrice);
});

// later, to stop listening:
off();
```

* `events.once(event, handler)`: uruchamia się raz, a potem sam się wyrejestrowuje.
* `events.off(event, handler)`: usuwa konkretny handler.

Handler, który rzuca wyjątek, jest izolowany i logowany w konsoli; pozostałe handlery nadal działają.

***

<div id="the-two-rules">
  ## Dwie zasady
</div>

Prawie każdy błąd związany ze zdarzeniami sprowadza się do jednej z nich.

<div id="dont-change-the-cart-from-cart_updated-without-a-guard">
  ### Nie zmieniaj koszyka z `cart_updated` bez zabezpieczenia
</div>

Zmiana koszyka wewnątrz handlera `cart_updated` ponownie uruchamia `cart_updated`. Jeśli ten handler znów zmienia koszyk, masz nieskończoną pętlę. Klient patrzy, jak jego koszyk się miota, podczas gdy strona bombarduje Shopify.

<Warning>
  **Nigdy nie wywołuj akcji bezwarunkowo z `cart_updated` lub `cart_loaded`.** Zabezpiecz ją sprawdzeniem stanu, który zamierzasz utworzyć, tak by drugi przebieg nic nie zrobił.
</Warning>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// ❌ Loops forever: every add triggers an update, which triggers another add.
window.aftersell.cart.events.on('cart_updated', (state) => {
  window.aftersell.cart.actions.addItem(GIFT_VARIANT_ID, 1);
});

// ✅ Guarded: once the gift is present, the condition is false and it stops.
window.aftersell.cart.events.on('cart_updated', (state) => {
  const hasGift = state.items.some((line) => line.variantId === GIFT_VARIANT_ID);
  if (state.totalPrice >= 5000 && !hasGift) {
    window.aftersell.cart.actions.addItem(GIFT_VARIANT_ID, 1);
  }
});
```

Koszyk daje Ci jedną siatkę bezpieczeństwa: aktualizacja, która daje **identyczny** koszyk, niczego nie emituje, więc ponowne pobranie, które nic nie zmienia, nie wznowi cyklu. To chroni Cię przed przypadkowymi pętlami bez efektu. **Nie** chroni Cię przed handlerem, który faktycznie zmienia koszyk za każdym razem.

<div id="treat-the-payload-as-read-only">
  ### Traktuj payload jako tylko do odczytu
</div>

Każdy handler jednego zdarzenia otrzymuje *ten sam* obiekt. Jego modyfikacja zmienia to, co widzą handlery po Twoim, w tym handlery należące do innych aplikacji w sklepie.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// ❌ Corrupts the payload for every later handler.
window.aftersell.cart.events.on('cart_updated', (state) => {
  state.items = state.items.filter((line) => line.finalLinePrice > 0);
});

// ✅ Copy first.
window.aftersell.cart.events.on('cart_updated', (state) => {
  const paidItems = state.items.filter((line) => line.finalLinePrice > 0);
});
```

Aby faktycznie zmienić koszyk, użyj [akcji](/pl/aftersell/cart/sdk-actions). Aby zmienić sposób renderowania pozycji, użyj [`registerLineTransform`](/pl/aftersell/cart/sdk-hooks#registerlinetransform).

***

<div id="cart_loaded">
  ## cart\_loaded
</div>

Uruchamia się **raz**, gdy koszyk po raz pierwszy ładuje się na stronie. Payload to pełny [obiekt koszyka](/pl/aftersell/cart/sdk-cart-object).

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_loaded', (state) => {
  console.log('Page loaded with', state.itemCount, 'items');
});
```

**Używaj do:** wszystkiego, co musi działać na stanie początkowym koszyka, np. uzgadniania darmowego prezentu, inicjalizacji widgetu lub raportowania zawartości koszyka do analityki przy załadowaniu strony.

**`cart_loaded` jest odtwarzane dla spóźnionych subskrybentów.** Jeśli zasubskrybujesz po tym, jak koszyk już się załadował, Twój handler jest natychmiast wywoływany z bieżącym koszykiem. Kolejność subskrypcji nigdy nie ma znaczenia, więc nie musisz się martwić, czy Twój skrypt wyprzedził koszyk.

<Tip>
  Logika, która musi być poprawna zarówno przy załadowaniu strony, jak i przy każdej późniejszej zmianie, powinna subskrybować **oba** zdarzenia `cart_loaded` i `cart_updated` z tą samą funkcją. To standardowy wzorzec „utrzymuj X w synchronizacji z koszykiem”.
</Tip>

<div id="cart_updated">
  ## cart\_updated
</div>

Uruchamia się za każdym razem, gdy zawartość koszyka zmienia się **po** pierwszym załadowaniu, czy to z drawera, z Twoich własnych akcji, z motywu, czy z innej aplikacji. Payload to pełny [obiekt koszyka](/pl/aftersell/cart/sdk-cart-object).

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_updated', (state) => {
  document.querySelector('#my-total').textContent =
    window.aftersell.cart.actions.formatMoney(state.totalPrice);
});
```

**Używaj do:** synchronizowania czegoś poza koszykiem, np. własnej sumy, paska postępu, plakietki w nagłówku lub zdarzenia analitycznego przy każdej zmianie.

Aktualizacja dająca identyczny koszyk niczego nie emituje. Ponowne otwarcie drawera, powrót do karty lub ponowne pobranie zwracające tę samą zawartość jej nie uruchomi.

<Warning>
  Przeczytaj ponownie [dwie zasady](#the-two-rules), zanim wywołasz tu akcję.
</Warning>

<div id="item_added">
  ## item\_added
</div>

Uruchamia się, gdy w koszyku pojawia się **nowa pozycja**. Payload to `{ item }`, gdzie `item` to [pozycja koszyka](/pl/aftersell/cart/sdk-cart-object#cart-lines).

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('item_added', (payload) => {
  myAnalytics.track('Added to cart', {
    id: payload.item.variantId,
    title: payload.item.title,
    quantity: payload.item.quantity,
  });
});
```

**Używaj do:** śledzenia add-to-cart w zewnętrznym narzędziu analitycznym. To najczęstsze zastosowanie SDK. Zobacz [śledzenie add-to-cart](/pl/aftersell/cart/sdk-use-case-analytics).

Dwie rzeczy warte wiedzy o tym, jak jest wyprowadzane:

<Warning>
  **Zmiana ilości nie jest dodaniem.** Koszyk ustala dodania i usunięcia, porównując *pozycje*, nie ilości. Klient zwiększający pozycję z 1 na 3 uruchamia `cart_updated`, a nie `item_added`. Jeśli musisz wychwytywać też wzrosty ilości, porównuj z poprzednim stanem w handlerze `cart_updated`.
</Warning>

Nie uruchamia się też dla produktów, które były już w koszyku przy załadowaniu strony; te przychodzą przez `cart_loaded`. Dodanie kilku różnych produktów naraz uruchamia zdarzenie raz na pozycję.

<div id="item_removed">
  ## item\_removed
</div>

Uruchamia się, gdy pozycja znika z koszyka. Payload to `{ item }` — pozycja w stanie tuż przed zniknięciem, więc nadal możesz odczytać jej `key`, `variantId` i `title`.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('item_removed', (payload) => {
  console.log('Removed', payload.item.title);
});
```

**Używaj do:** odwracania czegoś, co zrobiono przy dodaniu, np. czyszczenia flagi, ponownego pokazania oferty odrzuconej przez klienta lub raportowania usunięć do analityki.

To samo zastrzeżenie co przy `item_added`: obniżenie ilości bez osiągnięcia zera nie jest usunięciem.

<div id="cart_opened-and-cart_closed">
  ## cart\_opened i cart\_closed
</div>

Uruchamiają się, gdy drawer otwiera się i zamyka. Bez payloadu.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_opened', () => {
  myAnalytics.track('Cart viewed');
});

window.aftersell.cart.events.on('cart_closed', () => {
  document.body.classList.remove('cart-is-open');
});
```

**Używaj do:** śledzenia wyświetleń, wstrzymywania wideo lub karuzeli za drawerem, przełączania klasy na stronie.

Żadne z nich nie uruchamia się przy początkowym załadowaniu strony, tylko przy faktycznym otwarciu lub zamknięciu.

<div id="checkout">
  ## checkout
</div>

Uruchamia się, gdy klient klika przycisk checkoutu, tuż przed nawigacją przeglądarki. Bez payloadu.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('checkout', () => {
  myAnalytics.track('Checkout started');
});
```

**Używaj do:** śledzenia intencji checkoutu.

<Warning>
  **Nie możesz anulować checkoutu z tego handlera.** Zdarzenie to powiadomienie, a nie bramka; nawigacja następuje niezależnie od tego, co robi Twój kod. Utrzymuj handler szybki i synchroniczny: `await` lub wolne wywołanie sieciowe może nie zdążyć przed wyładowaniem strony. Użyj [`navigator.sendBeacon`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) dla wszystkiego, co musisz niezawodnie wysłać.
</Warning>

***

<div id="listening-from-outside-the-sdk">
  ## Nasłuchiwanie spoza SDK
</div>

Każde zdarzenie jest też wysyłane jako DOM-owe `CustomEvent` na `window`, więc możesz nasłuchiwać bez dotykania `window.aftersell.cart`. To przydatne z pliku motywu, aplikacji zewnętrznej lub skryptu ładującego się niezależnie od koszyka.

| Zdarzenie busa | Zdarzenie DOM                 |
| -------------- | ----------------------------- |
| `cart_loaded`  | `aftersell:cart:cart-loaded`  |
| `cart_updated` | `aftersell:cart:cart-updated` |
| `item_added`   | `aftersell:cart:item-added`   |
| `item_removed` | `aftersell:cart:item-removed` |
| `cart_opened`  | `aftersell:cart:cart-opened`  |
| `cart_closed`  | `aftersell:cart:cart-closed`  |
| `checkout`     | `aftersell:cart:checkout`     |

Zwróć uwagę na nazewnictwo: bus używa `snake_case`, zdarzenia DOM używają `kebab-case` za prefiksem `aftersell:cart:`.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.addEventListener('aftersell:cart:cart-updated', (event) => {
  console.log('Cart total is now', event.detail.totalPrice);
});
```

Payload przychodzi w `event.detail` i odpowiada [obiektowi koszyka](/pl/aftersell/cart/sdk-cart-object). Zdarzenia są wysyłane na `window`, więc listener w dowolnym miejscu strony je otrzymuje. Koszyk renderuje się w shadow root, ale granica shadow nigdy nie znajduje się na ścieżce zdarzenia. Każde wysłanie klonuje payload, więc listener modyfikujący `event.detail` nie może wpłynąć na nikogo innego, a listener rzucający wyjątek nie może zakłócić SDK.

<Warning>
  **`cart-loaded` nie jest odtwarzane w DOM.** Bus odtwarza `cart_loaded` dla spóźnionych subskrybentów, ale ta ścieżka omija wysyłkę DOM, więc `window.addEventListener('aftersell:cart:cart-loaded')` zarejestrowany po tym, jak koszyk już się załadował, nigdy się nie uruchomi. Jeśli kolejność ładowania Twojego skryptu nie jest gwarantowana, użyj `window.aftersell.cart.events.on('cart_loaded', …)`, które odtwarza, lub nasłuchuj też `aftersell:cart:cart-updated`.
</Warning>

<div id="shopify-standard-cart-events">
  ### Standardowe zdarzenia koszyka Shopify
</div>

Osobno koszyk publikuje [standardowe zdarzenia koszyka Shopify](https://shopify.dev/docs/storefronts/themes/best-practices/standard-events) na `document`, ilekroć zmienia koszyk, dzięki czemu kod motywu i inne aplikacje mogą reagować na modyfikacje Aftersell tak samo, jak reagują na te z motywu:

| Zdarzenie                      | Payload na instancji zdarzenia                                                   |
| ------------------------------ | -------------------------------------------------------------------------------- |
| `shopify:cart:lines-update`    | `action: 'add' \| 'update' \| 'remove'`, `context: 'cart' \| 'product'`, `lines` |
| `shopify:cart:note-update`     | `context: 'cart'`, `note`                                                        |
| `shopify:cart:discount-update` | `discountCodes: [{ code }]`                                                      |

<Warning>
  **Payload nie znajduje się w `event.detail`.** `detail` zawiera tylko `{ source: 'aftersell' }` — znacznik, którego koszyk używa, by ignorować własne zdarzenia zamiast wpadać w pętlę. Wszystko z powyższej tabeli jest przypisywane bezpośrednio do obiektu zdarzenia, więc czytaj `event.action`, a nie `event.detail.action`.
</Warning>

Każde zdarzenie niesie też `promise`, który Aftersell rozstrzyga, gdy podstawowy zapis się powiedzie, zgodnie ze standardem Shopify — czekaj na niego (await), nie rozstrzygaj go. Są one wysyłane na `document` i propagują się (bubble), więc listener na `window` też je otrzymuje.

<div id="where-to-go-next">
  ## Co dalej
</div>

* **[Obiekt koszyka](/pl/aftersell/cart/sdk-cart-object)**: pełna struktura powyższych payloadów.
* **[Akcje](/pl/aftersell/cart/sdk-actions)**: jak zmieniać koszyk z handlera.
* **[Hooki](/pl/aftersell/cart/sdk-hooks)**: do zmieniania sposobu renderowania koszyka, zamiast reagowania na niego.
* **[Przypadki użycia](/pl/aftersell/cart/sdk-use-cases)**: śledzenie analityczne, darmowe prezenty i inne kompletne przykłady.
