> ## 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.

# Hooki

> Zmieniaj sposób działania Aftersell Cart: transformuj pozycje, wzbogacaj je danymi ze Storefront, kształtuj opcje subskrypcji i kontroluj add-to-cart.

Podczas gdy [zdarzenia](/pl/aftersell/cart/sdk-events) pozwalają Ci *reagować* na koszyk, a [akcje](/pl/aftersell/cart/sdk-actions) pozwalają go *zmieniać*, **hooki** zmieniają sposób działania samego koszyka: jak renderują się pozycje, jakie dane niosą i co dzieje się przy add-to-cart.

Hooki znajdują się pod `window.aftersell.cart.hooks`.

<Note>
  Hook zmienia to, co klient **widzi**; akcja zmienia to, co **jest w jego koszyku**. Ukrycie pozycji darmowego prezentu transformacją zostawia ją w koszyku i w sumie. Usunięcie jej za pomocą [`removeItem`](/pl/aftersell/cart/sdk-actions#removeitemkey) usuwa ją naprawdę.
</Note>

<Note>
  Hooki to wywołania konfiguracyjne, więc można je bezpiecznie rejestrować na samej górze skryptu, bez czekania na `ready()`. Rejestruj je w skrypcie **Initialization** swojego koszyka (zobacz [Niestandardowe skrypty](/pl/aftersell/cart/custom-scripts)).
</Note>

<div id="how-registration-works">
  ## Jak działa rejestracja
</div>

Każdy hook to metoda `register*`. Wywołujesz ją ze swoją funkcją; zwraca **funkcję wyrejestrowującą**, którą możesz wywołać, aby usunąć swoją.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const off = window.aftersell.cart.hooks.registerSkipAddToCartRule((form) =>
  form.hasAttribute('data-skip-aftersell')
);

// later: off();
```

Rejestracja jest **addytywna**, więc Twoja funkcja działa obok wszystkich innych. To ważne, bo Twój skrypt rzadko jest jedynym na stronie: aplikacja subskrypcyjna, aplikacja zestawów i sam motyw mogą rejestrować się na tym samym hooku. Żaden z nich nie może zastąpić Twojego, a nic, co zarejestrujesz, nie może być po cichu usunięte przez to, co załaduje się po Tobie.

| Hook                                                                                      | Co robi                                                                                                                          | Przy kilku rejestracjach                                     |
| ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| [`registerLineTransform`](#registerlinetransform)                                         | Ukrywa lub zmienia etykiety pojedynczych pozycji.                                                                                | Wszystkie działają, w kolejności rejestracji.                |
| [`registerLineComparator`](#registerlinecomparator)                                       | Zmienia kolejność renderowanych pozycji.                                                                                         | Składają się jako tie-breakery.                              |
| [`registerCartEnricher`](#registercartenricher)                                           | Dołącza dodatkowe dane Storefront do każdej pozycji.                                                                             | Wszystkie działają; każde `id` to osobna przestrzeń nazw.    |
| [`registerSubscriptionOptionsTransform`](#registersubscriptionoptionstransform)           | Ukrywa lub zmienia nazwy planów sprzedaży pozycji.                                                                               | Wszystkie działają; poprawki scalają się per plan, per pole. |
| [`registerDefaultSubscriptionOptionSelector`](#registerdefaultsubscriptionoptionselector) | Wybiera, który plan jest wstępnie zaznaczony.                                                                                    | Wygrywa pierwsza odpowiedź inna niż `null`.                  |
| [`registerSkipAddToCartRule`](#registerskipaddtocartrule)                                 | Pozwala konkretnym formularzom omijać koszyk. Zobacz [Przechwytywanie add-to-cart](/pl/aftersell/cart/add-to-cart-interception). | Dowolna reguła zwracająca `true` pomija.                     |

Hook, który rzuca wyjątek lub nie jest funkcją, jest pomijany; reszta nadal działa, a koszyk pracuje dalej. Jedna zepsuta integracja nie może wyłączyć add-to-cart, selektora subskrypcji ani sortowania.

Druga strona medalu: Twój zepsuty hook zawodzi **po cichu** — nic nie trafia do konsoli przeglądarki. Zobacz [Debugowanie](/pl/aftersell/cart/sdk-overview#debugging), gdzie te awarie jednak się ujawniają.

***

<div id="registerlinetransform">
  ## registerLineTransform
</div>

`registerLineTransform(fn)` uruchamia się dla każdej pozycji koszyka przed jej wyrenderowaniem. Użyj go, aby ukryć pozycję lub zmienić sposób jej prezentacji, bez dotykania tego, co faktycznie jest w koszyku klienta.

Funkcja otrzymuje pozycję tylko do odczytu plus settery. Zwraca funkcję wyrejestrowującą.

| Setter                            | Efekt                                                                                                                                                          |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `setHidden(bool)`                 | Ukrywa pozycję w drawerze. Pozostaje w koszyku i w sumie.                                                                                                      |
| `setTitle(string)`                | Zmienia wyświetlany tytuł.                                                                                                                                     |
| `setVariantTitle(string \| null)` | Zmienia wyświetlaną etykietę wariantu.                                                                                                                         |
| `setInternalProperties(obj)`      | Scala właściwości tylko do renderowania. Nigdy nie są zapisywane w Shopify. Używane do [grupowania pozycji zestawów](/pl/aftersell/cart/sdk-use-case-bundles). |

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Hide free gift lines from the drawer. The cart total is unaffected.
const off = window.aftersell.cart.hooks.registerLineTransform((line) => {
  if (line.finalLinePrice === 0) {
    line.setHidden(true);
  }
  if (line.sellingPlan) {
    line.setVariantTitle(`Delivered ${line.sellingPlan.name.toLowerCase()}`);
  }
});

// later: off();
```

<Warning>
  Transformacja zmienia tylko to, co się renderuje. Nie może zmienić ceny, ilości ani tożsamości pozycji. Do tego służą [akcje](/pl/aftersell/cart/sdk-actions).
</Warning>

**Używaj do:** ukrywania pozycji prezentów przy zakupie lub pozycji wstrzykiwanych przez aplikacje, zmiany etykiet pozycji subskrypcyjnych, oznaczania przecenionych produktów, ukrywania komponentów zestawów, którymi klient nie powinien zarządzać osobno.

`setInternalProperties` to setter stojący za grupowaniem zestawów: nadanie każdej pozycji kanonicznych właściwości zestawu to sposób, by osobne pozycje koszyka z aplikacji zewnętrznej renderowały się jako jeden produkt. Zobacz [Grupowanie pozycji zestawów z innej aplikacji](/pl/aftersell/cart/sdk-use-case-bundles).

<div id="registerlinecomparator">
  ## registerLineComparator
</div>

Komparator w tym samym kształcie, jakiego oczekuje `Array.prototype.sort`. Uruchamia się po ukrywaniu i zmianie nazw, więc widzi przetransformowane pozycje.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Subscriptions first, then everything else.
window.aftersell.cart.hooks.registerLineComparator((lineA, lineB) => {
  return (lineB.sellingPlan ? 1 : 0) - (lineA.sellingPlan ? 1 : 0);
});
```

Komparatory **składają się jako tie-breakery**: pierwszy, który zwróci wartość niezerową, decyduje o tej parze, a pozostałe są konsultowane tylko przy remisach. Zwracaj `0` dla par, o których nie masz zdania. To właśnie przekazuje decyzję następnemu komparatorowi, zamiast narzucać mu kolejność.

**Używaj do:** wynoszenia subskrypcji lub produktów o wysokiej wartości na górę, spychania darmowych prezentów i dodatków na dół, utrzymywania sponsorowanego produktu na pierwszym miejscu.

<div id="registercartenricher">
  ## registerCartEnricher
</div>

`registerCartEnricher(registration)` pobiera dodatkowe dane produktu lub wariantu z API Storefront Shopify i dołącza je do każdej pasującej pozycji koszyka pod `line.metadata[id]`. Użyj go, aby pokazać metafields, tagi lub cokolwiek innego, co udostępnia API Storefront, bez żadnej zmiany kodu po stronie Aftersell.

| Pole       | Typ                                | Opis                                                                                                                         |
| ---------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `id`       | `string`                           | Przestrzeń nazw wyniku; ląduje w `line.metadata[id]`. Musi być unikalne; druga rejestracja z tym samym `id` jest ignorowana. |
| `onType`   | `'Product'` lub `'ProductVariant'` | Który węzeł targetuje fragment. To także klucz łączenia (ID produktu vs ID wariantu).                                        |
| `fragment` | `string`                           | Wybór pól GraphQL (bez zewnętrznych klamr) wklejany do zapytania Storefront. Klamry muszą się bilansować.                    |

Zwraca **funkcję wyrejestrowującą**.

Zawsze gdy koszyk się ładuje lub zmienia, Aftersell pobiera Twój fragment dla każdego produktu lub wariantu w koszyku i dołącza wynik. Pobieranie jest nieblokujące: koszyk renderuje się natychmiast i ponownie emituje `cart_updated`, gdy dane dotrą. Wolny lub zawodzący fragment nigdy nie opóźnia ani nie psuje koszyka.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerCartEnricher({
  id: 'pricing',
  onType: 'ProductVariant',
  fragment: `
    anchorPrice: metafield(namespace: "custom", key: "anchor_price") { value }
    subscriberPrice: metafield(namespace: "custom", key: "subscriber_price") { value }
  `,
});

// Read it once the data arrives.
window.aftersell.cart.events.on('cart_updated', (state) => {
  state.items.forEach((line) => {
    const anchor = line.metadata.pricing?.anchorPrice;
    if (anchor) console.log(line.title, 'anchor price', anchor.value);
  });
});
```

Ponieważ wzbogacanie jest asynchroniczne, zawsze zabezpieczaj odczyt — `line.metadata.pricing` jest `undefined`, dopóki pierwsze pobranie się nie rozwiąże, a `metadata` domyślnie ma wartość `{}`.

**Używaj do:** pobierania metafield na każdą pozycję (szacowany czas dostawy, lista składników, flaga „wysyłane osobno”, mnożnik lojalnościowy) i renderowania go przez [blok Custom code](/pl/aftersell/cart/custom-code-blocks). Zobacz [pokazywanie danych metafield na pozycjach koszyka](/pl/aftersell/cart/sdk-use-case-metafields).

<Note>
  Wiele wzbogacaczy współistnieje bezproblemowo, ponieważ każde `id` to osobna przestrzeń nazw, więc ich dane nigdy się nie zderzają.
</Note>

<Warning>
  Wzbogacone wartości są zwracane z API Storefront w stanie surowym i **nie** są oczyszczane. Renderuj je jako tekst, nie jako surowy HTML.
</Warning>

<div id="registersubscriptionoptionstransform">
  ## registerSubscriptionOptionsTransform
</div>

Ukrywaj lub zmieniaj nazwy planów sprzedaży oferowanych na pozycji. Twoja funkcja otrzymuje opcje tylko do odczytu plus settery i niczego nie zwraca.

| Setter            | Efekt                            |
| ----------------- | -------------------------------- |
| `setHidden(bool)` | Ukrywa plan w selektorze.        |
| `setName(string)` | Zmienia wyświetlaną nazwę planu. |

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerSubscriptionOptionsTransform((options, context) => {
  // context: { productId, variantId }
  options.forEach((option) => {
    if (option.discountPercent === 0) option.setHidden(true);
    option.setName(option.name.replace('Every ', ''));
  });
});
```

**Settery, a nie zwracana lista, po to, by kilka skryptów mogło współistnieć.** Gdyby ten hook zwracał tablicę, transformacja dbająca tylko o jeden plan naturalnie napisałaby `options.filter(...)` i po drodze po cichu usunęłaby plany każdej innej aplikacji. Dzięki setterom możesz opisać tylko własne zmiany: poprawki scalają się per plan i per pole, a ostatni piszący wygrywa faktyczny konflikt na tym samym polu tego samego planu. Transformacja, która rzuca wyjątek, nic nie wnosi, a pozostałe nadal się stosują.

Każda transformacja widzi *oryginalne* opcje, a nie częściowo załatany widok, więc kolejność rejestracji nie zmienia tego, co czytasz.

<Note>
  Kolejność planów pozostaje taka, jaką zwrócił Shopify, więc transformacja nie może zmienić kolejności. Aby kontrolować, który plan jest oferowany jako pierwszy (i który subskrybuje przycisk uaktualnienia zakupu jednorazowego), użyj [`registerDefaultSubscriptionOptionSelector`](#registerdefaultsubscriptionoptionselector), który przenosi swój wybór na początek.
</Note>

Nie możesz też *dodać* planu ani zmienić ceny: `discountPercent` nie ma settera, ponieważ plan, którego Shopify nie zrealizuje przy checkoucie, byłby w selektorze tylko pustą obietnicą.

<div id="registerdefaultsubscriptionoptionselector">
  ## registerDefaultSubscriptionOptionSelector
</div>

Wybierz, który plan jest wstępnie zaznaczony na pozycji. Zwróć `id` planu lub `null`, aby przekazać decyzję dalej.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerDefaultSubscriptionOptionSelector((options) => {
  const best = options
    .slice()
    .sort((optionA, optionB) => optionB.discountPercent - optionA.discountPercent)[0];
  return best ? best.id : null;
});
```

**Wygrywa pierwszy selektor, który zwróci id dostępnego planu**, więc zwracaj `null` dla pozycji, które Cię nie interesują, zamiast zgadywać. To przekazuje decyzję następnemu selektorowi, zamiast go nadpisywać. Id, które nie pasuje do żadnego planu na pozycji, jest traktowane tak samo jak `null` i też ustępuje, więc nieaktualne id nie może wyczyścić selektora.

Twoja funkcja otrzymuje `(options, context)`, ten sam `context`, który dostaje transformacja opcji.

<div id="registerskipaddtocartrule">
  ## registerSkipAddToCartRule
</div>

Zwróć `true`, aby pozwolić konkretnemu formularzowi produktu dodawać do koszyka normalnie, całkowicie omijając Aftersell. To przydatne dla formularza, który potrzebuje własnego przekierowania lub obsługi.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerSkipAddToCartRule((form) =>
  form.hasAttribute('data-skip-aftersell')
);
```

**Dowolne `true` pomija**, więc utrzymuj swoją regułę wąską — dopasowującą konkretne formularze, które należą do Ciebie — i zwracaj `false` dla wszystkiego innego. Reguły są oceniane w kolejności rejestracji i zatrzymują się na pierwszym `true`, więc nie umieszczaj w nich efektów ubocznych: to, czy Twoja w ogóle się uruchomi, zależy od tego, co zarejestrowało się przed nią.

<Tip>
  Jeśli kontrolujesz markup formularza, w ogóle nie potrzebujesz hooka: dodaj klasę **`aftersell-cart-skip-atc`** do `<form>`, a Aftersell zostawi go w spokoju. Użyj tego hooka, gdy nie możesz edytować markupu lub gdy decyzja zależy od czegoś, co zna tylko Twój kod.
</Tip>

**Używaj do:** formularza pre-orderu lub wyceny wymagającego własnego przekierowania, niestandardowego przepływu aplikacji subskrypcyjnej, przycisku „buy it now”, który powinien iść prosto do checkoutu. Aby zamiast tego wyłączyć przechwytywanie dla całej strony, użyj [`skip_add_to_cart_interceptor`](/pl/aftersell/cart/sdk-configure#skip_add_to_cart_interceptor), ale preferuj ten hook, który jest ograniczony do wskazanych przez Ciebie formularzy.

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

* **[Obiekt koszyka](/pl/aftersell/cart/sdk-cart-object)**: kształt pozycji, którą otrzymuje transformacja.
* **[Zdarzenia](/pl/aftersell/cart/sdk-events)**: wszystko, co możesz subskrybować.
* **[Akcje](/pl/aftersell/cart/sdk-actions)**: odczytywanie i zmienianie koszyka.
* **[Przypadki użycia](/pl/aftersell/cart/sdk-use-cases)**: kompletne rozwiązania typowych potrzeb.
