Fejlesztőknek
Kezdj bele
Témák/Termékoldal: a kosárba helyezés testreszabása

Termékoldal: a kosárba helyezés testreszabása

Hogyan épül fel a termékoldal vásárlási doboza blokkokból, és hogyan formálhatja át vagy renderelheti újra egy téma a kosárba helyezési területet a kereskedelmi logika érintése nélkül.

Ezen az oldalon

A termékoldal kosárba helyezési területe (mennyiség, a hozzáadás gomb, gyorsfizetési tárcák, az előrendelési sor és az „Értesítést kérek”) a vásárlási doboz (buy box). A termékoldal többi részéhez hasonlóan ez is blokkokból épül fel. A platform a mögötte lévő kereskedelmi működést (kosárhoz adás, analitikai események, tárcás fizetések, csomagok, hibakezelés) egyetlen hook mögött tartja: ez a useProductBuy a @poltura/theme-sdk/product csomagból. Egy téma a vásárlási doboz minden egyes pixelét re-renderelheti, miközben továbbra is ezt a hookot hívja meg.

Hogyan épül fel a termékoldal

A terméksablon egyetlen product-information szekcióból áll három régióval, amelyeket a @poltura/theme-sdk/product csomagból származó productRegions határoz meg: media (a galéria oszlopa), details (a galéria mellett) és below (a mindkettő alatti, teljes szélességű terület). Az alapértelmezett téma pages/product.json fájlja ezekkel a blokkokkal tölti fel őket, és egy related-products szekciót helyez el alá:

Blokk típusaRégióMit jelenít meg
product-gallerymediaA képgaléria
breadcrumbdetailsBreadcrumb
product-vendordetailsMárka vagy gyártó
product-titledetailsCím
product-ratingdetailsÉrtékelési pontszám
pricedetailsÁr, eredeti ár, kedvezmény, egységár, áfamegjegyzés
variant-pickerdetailsOpció- / változatválasztó
inventorydetailsRaktáron, fogyóban, elfogyott, előrendelhető állapotok
buy-buttonsdetailsA vásárlási doboz: quantity, add-to-cart, wallets, product-wishlist
delivery-infodetailsMikor indul útnak és érkezik meg, szállítónként és árakkal
product-highlightsdetailsKiemelt propok
product-detailsdetailsPlatform, régió, lefedettség
product-addonsdetailsKiegészítő termékek, mindegyik saját kosárba gombbal
product-tabsbelowLeírás, specifikációk és a bolt egyéb fülei
product-safetybelowA gyártó adatai és termékbiztonsági információk
product-reviewsbelowVásárlói vélemények és az átlagos értékelés

A szállítási doboz

A delivery-info jeleníti meg a kiválasztott változat szállítási ígéretét: a rendelési határidőt (visszaszámláló, ha három óránál kevesebb van hátra, előtte pontos időpont), a csatlakoztatott szállítókat a látogató országára érvényes nappal és árral, az ingyenes szállítási értékhatárt és egy országválasztót. Minden a bolt szállítási időre vonatkozó beállításaiból, a szállító appokból és a termék átfutási idejéből származik, így a témának nem kell saját szállítási ígéretet megfogalmaznia. A beállításai: style (compact vagy card), show_dispatch, show_carriers, carrier_limit (1-től 6-ig), show_free_shipping, show_country, egy opcionális instant_label header és egy opcionális note sor a doboz alatt. A mellékelt témák csak a style értékét állítják be. Helyezd a blokkot a vásárlási gombok alá:

{ "id": "delivery", "type": "delivery-info", "slot": "details", "settings": { "style": "card" } }

A kereskedők a Témaszerkesztő felületén rendezhetik át, adhatják hozzá, rejthetik el és szerkeszthetik a blokkokat. A galéria, a cím, az ár, a változatválasztó, a vásárlási gombok és a termékbiztonsági blokk statikusak: a kereskedő átstilizálhatja őket, de nem mozgathatja vagy törölheti. A változatválasztó, a vásárlási gombok és a termékbiztonság nem is rejthető el, ahogy a vásárlási gombokon belüli add-to-cart gomb sem. A galéria, a cím és az ár elrejthető.

A témák a platformblokkok jelölését a @poltura/theme-sdk/elements csomagból származó overrideBlock segítségével írják felül, amelyet a src/blocks mappa alatti fájlból exportálnak. A platform megtartja a blokk sémáját, beállítópaneljét (inspector) és adatait, és maga rajzolja meg a blokk keretét (kereskedői stílus, horgony, kijelölés a témaszerkesztőben). A beállítások típusbiztosan érkeznek:

src/blocks/add-to-cart.tsx
import {
  overrideBlock,
  type ElementRenderProps,
  type PlatformBlockSettings,
} from '@poltura/theme-sdk/elements';
import { AddToCartButton, useProductBuy, type ProductBuy } from '@poltura/theme-sdk/product';

type AddToCartSettings = PlatformBlockSettings<'add-to-cart'>;

function labelFor(buy: ProductBuy, settings: AddToCartSettings): string {
  const status = (buy.variant ?? buy.product).availability.status;
  const custom = buy.isAdding
    ? settings.adding_label
    : status === 'preorder'
      ? settings.preorder_label
      : status === 'comingSoon'
        ? settings.coming_soon_label
        : status === 'unavailable'
          ? settings.sold_out_label
          : settings.label;
  return custom?.trim() || buy.ctaLabel;
}

function AddToCart({ settings }: ElementRenderProps<AddToCartSettings>) {
  const buy = useProductBuy();
  return (
    <div className='min-w-0 flex-1'>
      <AddToCartButton
        label={labelFor(buy, settings)}
        busy={buy.isAdding}
        disabled={!buy.canAdd || buy.isAdding}
        error={buy.error}
        fullWidth={settings.full_width}
        onAdd={() => void buy.add()}
      />
    </div>
  );
}

export const addToCart = overrideBlock('add-to-cart', AddToCart);

Ugyanez a minta érvényes a quantity, wallets, price, inventory és buy-buttons blokkokra is: block type-onként egy fájl a src/blocks alatt, amely az overrideBlock eredményét exportálja.

A vásárlási hook: useProductBuy

A vásárlási doboz minden része egyetlen közös állapotot olvas a useProductBuy() segítségével. Működik a termékoldalon, egy product card alatt és egy ProductScope belsejében is; mindenhol máshol hibát dob.

MezőJelentés
productA megvásárolni kívánt termék
variantA kiválasztott változat, vagy null, amíg a választott opciók egyhez sem illeszkednek
quantity, setQuantity(n)A hozzáadandó mennyiség
canAddHozzáadható-e most a kiválasztott változat; kapcsold ki a gombot, ha false
isAddingFolyamatban lévő állapot a gombhoz
errorMiért nem sikerült az utolsó hozzáadás, az oldal nyelvén, vagy null
add()A művelet. Ezt hívd meg, semmi mást
ctaLabelA gomb szövege az adott állapothoz: kosárba, előrendelés, hamarosan vagy elfogyott
wallet.visibleVan-e a WalletButtons komponensnek itt megjeleníthető gyorsfizetési tárcája
wholesaleNagykereskedelmi sávos árak a kiválasztott változathoz (WholesaleTiers), egyébként null
personalization, giftCardMegszemélyesítési szövegek és ajándékkártya-adatok a termékoldalon, máshol null
bundleA termékhez kínált csomag és a vásárló választása benne, vagy null

Az SDK tartalmazza azokat az elemeket, amelyekből a platform vásárlási doboza felépül, mindet a @poltura/theme-sdk/product csomagban:

ElemMit jelenít meg
AddToCartButtonA hozzáadás gomb az alatta lévő legutóbbi hibával
QuantityStepperMínusz, beviteli mező és plusz; a megadott min, step és max között tartja a beírt mennyiséget
WalletButtonsApple Pay, Google Pay és egyéb gyorsfizetési tárcák; a termékoldalon kívül nem jelenik meg
WholesaleTiersNagykereskedelmi vásárló sávos ára, listaára és mennyiségi lépcsői
PersonalizationFields, GiftCardFieldsA vásárló által kitöltött szövegek, illetve az ajándékkártya összege, címzettje és dátuma
BundleSelectorA csomagajánlat és a választási lehetőségek
AvailabilityBadge„Előrendelés”, „Hamarosan elérhető” vagy „Elfogyott”; sima raktárállapotoknál semmi
NotifyMeÉrtesítési űrlap olyan változathoz, amely most nem vásárolható meg

Az add() a buy.quantity mennyiséget adja hozzá, így egy saját vásárlási doboz a QuantityStepper komponenst a buy.quantity és a buy.setQuantity értékekhez köti. Az SDK nem adja át a témának a változat rendelési szabályait (minimum, lépésköz, maximum), így a léptetőd csak azokat a határokat kényszeríti ki, amelyeket átadsz neki.

Előrendelés, hamarosan elérhető és értesítés

Egy változat lehet előrendelhető: most megvásárolható, és egy várható dátumtól szállítják. Az állapota az availability a változaton (és az összes változat legjobb állapotaként a terméken is), amely egy status alapján megkülönböztetett unió:

statusTovábbi mezőkpurchasable
inStockquantity (ha a bolt mutatja a készletszinteket)mindig
backordernincsmindig
preorderexpected, remaining, messageamíg van hely az előrendelésben
comingSoonopensAt, expectedsoha a rendelések megnyitása előtt
unavailablereason: outOfStock, preorderFull, paused, restrictedsoha

Minden állapot tartalmazza a label mezőt, amely a státuszt jelzi az oldal nyelvén. Az expected.label az oldal számára már megfogalmazott ígéret („2026. 4. negyedév”, „A dátum még megerősítésre vár”), így a témának soha nem kell magának formáznia a várható dátumot.

ÁllapotGombSor az ár alattKártyajelvény
preorder„Előrendelés”, aktívvárható dátum + üzenet„Előrendelés”
comingSoon„Hamarosan elérhető”, inaktívrendelések megnyitásának dátuma„Hamarosan elérhető”
unavailable (preorderFull)„Elfogyott”, inaktív„Előrendelés betelt”„Elfogyott”
backorder, inStockváltozatlanváltozatlanváltozatlan
import { useFormat } from '@poltura/theme-sdk/i18n';
import { useProductBuy } from '@poltura/theme-sdk/product';

export function PreorderNote() {
  const { variant } = useProductBuy();
  const format = useFormat();
  const availability = variant?.availability;
  if (availability?.status === 'preorder') {
    return (
      <div className='flex flex-col gap-1 text-sm text-muted-foreground'>
        <p>{availability.expected.label}</p>
        {availability.message ? <p>{availability.message}</p> : null}
      </div>
    );
  }
  if (availability?.status === 'comingSoon') {
    return <p className='text-sm text-muted-foreground'>{format.date(availability.opensAt)}</p>;
  }
  return null;
}

A vásárlási doboz blokkjainak feliratbeállításai (témánkénti alapértelmezések megadása a pages/product.json fájlban):

BlokkBeállításJelentés
add-to-cartpreorder_labelElőrendelhető változat gombszövege
add-to-cartcoming_soon_labelLetiltott gombszöveg az előrendelések megnyitása előtt
add-to-cartsold_out_labelNem megvásárolható változat letiltott gombszövege
inventorybackorder_labelKésőbb szállított változat készletsorának szövege
inventoryshow_preorder_remaining„Már csak N darab maradt” megjelenítése (alapértelmezetten ki van kapcsolva; a bolt „Fennmaradó készlet megjelenítése” beállítása szükséges hozzá)

Értesítést kérek. Ha a bolt kínál készletértesítéseket, és a kiválasztott változat még nem vásárolható meg, a buy-buttons blokk a gombok alatt megjeleníti a NotifyMe komponenst. Ha máshová szeretnéd helyezni, használd a <NotifyMe variantId={variant.id} reason='outOfStock' /> formát (vagy comingSoon, preorderFull). Önállóan kezeli a kérést, a captchát, a hozzájárulási szöveget és az állapotokat. A useNotifyMe saját űrlapot támogat, így a témának soha nem kell közvetlenül meghívnia a készletértesítési API-t.

A termékoldalon a buy-buttons blokk jeleníti meg az árkorlátot (price gate) is a gombok helyén, ha az árak rejtve vannak a látogató elől, valamint az előrendelési dátumot és feltételeket – így egy olyan oldal is tájékoztatja a vásárlót arról, miért nem tud vásárolni, amelynek az ár- vagy készletblokkja el van rejtve.

Product cardok esetén az <AvailabilityBadge availability={product.availability} /> jeleníti meg az „Előrendelés”, „Hamarosan elérhető” és „Elfogyott” jelvényeket, a variant.availability pedig a vásárlási dobozon kívüli változat teljes állapotát tartalmazza. Mindig az availability mezőt olvasd, soha ne a készletszinteket: egy nyitott előrendelésben lévő változat készlet nélkül is megrendelhető, a release után pedig zárva marad, amíg az előrendeléseket nem teljesítik.

Minimális egyedi vásárlási doboz

A buy-buttons override-ja a teljes sort és annak child blockjait lecseréli:

src/blocks/buy-buttons.tsx
import {
  overrideBlock,
  type ElementRenderProps,
  type PlatformBlockSettings,
} from '@poltura/theme-sdk/elements';
import {
  AddToCartButton,
  NotifyMe,
  QuantityStepper,
  WalletButtons,
  useProductBuy,
  type Availability,
  type NotifyMeReason,
} from '@poltura/theme-sdk/product';
import { Price, PriceGate, usePriceAccess } from '@poltura/theme-sdk/price';
import { Card, cn } from '@poltura/theme-sdk/ui';

function notifyReason(availability: Availability | null): NotifyMeReason | null {
  if (availability?.status === 'comingSoon') return 'comingSoon';
  if (availability?.status !== 'unavailable') return null;
  if (availability.reason === 'outOfStock' || availability.reason === 'preorderFull') {
    return availability.reason;
  }
  return null;
}

function BuyButtons({ settings }: ElementRenderProps<PlatformBlockSettings<'buy-buttons'>>) {
  const buy = useProductBuy();
  const access = usePriceAccess();
  const reason = notifyReason(buy.variant?.availability ?? null);

  if (!access.visible) {
    return (
      <div className='text-base font-semibold'>
        <PriceGate className='underline underline-offset-2' />
      </div>
    );
  }

  return (
    <Card className='flex flex-col gap-4 rounded-xl p-6'>
      <Price price={(buy.variant ?? buy.product).price} className='text-4xl font-black' />
      <div className={cn('flex gap-2', settings.layout === 'stacked' ? 'flex-col' : 'flex-row')}>
        <QuantityStepper value={buy.quantity} onChange={buy.setQuantity} />
        <AddToCartButton
          label={buy.ctaLabel}
          busy={buy.isAdding}
          disabled={!buy.canAdd || buy.isAdding}
          error={buy.error}
          fullWidth
          onAdd={() => void buy.add()}
        />
      </div>
      {buy.wallet.visible ? <WalletButtons /> : null}
      {buy.variant && reason ? <NotifyMe variantId={buy.variant.id} reason={reason} /> : null}
    </Card>
  );
}

export const buyButtons = overrideBlock('buy-buttons', BuyButtons);

A buy-buttons override-ja nem jelenít meg megszemélyesítési, ajándékkártya- vagy csomagmezőket, hacsak nem helyezi el maga a PersonalizationFields, GiftCardFields és BundleSelector komponenseket. Emellett override-olja a kereskedő belső quantity, add-to-cart, wallets és product-wishlist blokkjait is. Mentsd el a kódszerkesztőben, hogy lásd a szerkesztő Előnézet felületén, és tedd közzé, hogy élesben is megjelenjen.

A változatválasztó

A változatválasztó ugyanezt a mintát követi a saját block type-ja esetén. A platform tárolja a kijelölést, és megosztja azt a termék összes blokkjával. Ne tarts fent saját kiválasztási állapotot, különben a választó, a vásárlási doboz és a változathoz tartozó analitikai esemény eltérnek majd egymástól:

src/blocks/variant-picker.tsx
import {
  overrideBlock,
  type ElementRenderProps,
  type PlatformBlockSettings,
} from '@poltura/theme-sdk/elements';
import { VariantOption, VariantOptionValue, useVariantPicker } from '@poltura/theme-sdk/product';

function Picker({ settings }: ElementRenderProps<PlatformBlockSettings<'variant-picker'>>) {
  const picker = useVariantPicker();
  if (picker.options.length === 0) return null;
  return (
    <div className='flex flex-col gap-4'>
      {picker.options.map((option) => (
        <VariantOption
          key={option.id}
          option={option}
          display={settings.style === 'dropdown' ? 'dropdown' : option.display}
          showTitle={settings.show_option_name}
        >
          {option.values.map((value) => (
            <VariantOptionValue
              key={value.id}
              value={value}
              onSelect={() => picker.select(option.id, value.id)}
            />
          ))}
        </VariantOption>
      ))}
    </div>
  );
}

export const variantPicker = overrideBlock('variant-picker', Picker);

Minden érték selectable mezője jelzi, hogy a kiválasztása vezethet-e még megvásárolható változathoz, az availability pedig az általa teljessé tett változat állapota. Használd ezeket a letiltott és halványított opciókhoz. A <VariantPicker /> a teljes platformválasztó, arra az esetre, ha csak a classes segítségével szeretnéd módosítani a stílusát.

Áthelyezés: régiók és a szekció

Egy blokk stílusának módosítása megváltoztatja a pixeleit. Ha a vásárlási dobozt teljesen máshová szeretnéd helyezni, próbáld ki ezeket sorrendben:

A kereskedők már most is mozgathatnak blokkokat

A kereskedők a témaszerkesztő block tree-jében húzhatják át a blokkokat a media, details és below régiók között. A szekció beállításai már most is felcserélik az oszlopokat (gallery_position), beállítják a média szélességét (media_width), és láthatóan tartják a részletek oszlopát (sticky_details). Mielőtt kódot írnál, ellenőrizd, hogy a kívánt layout nem érhető-e el egyszerű áthúzással.

Saját szekció-layout kialakítása

Eltérő layouthoz override-old a product-information szekciót, és helyezd el a régiókat te magad. Az alapértelmezett téma ezt a src/sections/product-information.tsx fájlban teszi meg. Egy rövidebb változat:

src/sections/product-information.tsx
import {
  Slot,
  overrideSection,
  type ElementRenderProps,
  type PlatformSectionSettings,
} from '@poltura/theme-sdk/elements';
import { BundleSelector, productRegions } from '@poltura/theme-sdk/product';
import { cn } from '@poltura/theme-sdk/ui';

function ProductInformation({
  settings,
}: ElementRenderProps<PlatformSectionSettings<'product-information'>>) {
  return (
    <div className='bg-background'>
      <div className='container mx-auto flex flex-col gap-10 px-4 py-8'>
        <div className='grid grid-cols-1 gap-10 lg:grid-cols-2'>
          <Slot
            name={productRegions.media}
            className={cn('flex min-w-0 flex-col gap-4', settings.gallery_position === 'right' && 'lg:order-2')}
          />
          <Slot name={productRegions.details} className='flex min-w-0 flex-col gap-5' />
        </div>
        <BundleSelector placement='below' />
        <Slot name={productRegions.below} className='flex min-w-0 flex-col gap-8' />
      </div>
    </div>
  );
}

export const productInformation = overrideSection('product-information', ProductInformation);

A kereskedő blokkjai továbbra is megjelennek a Slot-jaidban, és az oldal szerkeszthető marad a Témaszerkesztőben.

Finomabb módosítások

  • Csak tokenek: az alapértelmezett vásárlási doboz a --primary, --card, --border, --muted és --radius tokenekre épül. A Témaszerkesztő színei és a tokens.css kód nélkül style-olják át.
  • Szövegek: a kereskedők a Témaszerkesztőben szerkesztik a gombok feliratait. A témák a lefordított alapértelmezéseket a platform szövegkulcsainak override-jával módosíthatják a texts/<language>.json fájlban.
  • Az oldal körül: a platform a product.above és product.below app-slotokat a terméksablon felett és alatt rajzolja ki, így a telepített appok anélkül helyezhetnek el ott bannereket vagy tartalmat, hogy a témának bármit el kellene helyeznie.

Amit a platform kezel

Az add() mögötti kosármódosítás, a tárcás fizetések, a csomagárazás, az elérhetőség és az analitikai események a platformnál maradnak. A te blokkodé a jelölés, a pénzügyi folyamat pedig a useProductBuy hooké. Ez a szétválasztás biztosítja, hogy az újratervezett vásárlási doboz a platformfrissítések után is működőképes maradjon.

Következő lépések

Utoljára frissítve