# Öneri ve Arama

Hook ve doğrudan çağrı
Kaynak: https://selwise.com/docs/mobile/recommendations

Uygulamada öneri şeritlerini almak: hook ile ya da doğrudan çağrıyla. SDK ürünleri ve şeridin başlığını verir; kartı uygulamanızın kendi bileşeni çizer. Arama için [Arama](/docs/mobile/search).

## Hook ile

```tsx
import { SelwiseProvider, useRecommendationWidget } from '@selwise/react-native/react';

// kökte: <SelwiseProvider client={selwise}>{children}</SelwiseProvider>

function SimilarProducts({ productCode }: { productCode: string }) {
  const { products, widget, loading, trackImpression, trackClick } = useRecommendationWidget(
    'WIDGET_ID',
    { currentProductId: productCode }
  );

  useEffect(() => {
    if (products.length > 0) trackImpression();
  }, [products, trackImpression]);

  if (loading || products.length === 0) return null;

  return (
    <>
      <Text>{widget?.title}</Text>
      <FlatList
        horizontal
        data={products}
        keyExtractor={(p) => p.itemCode}
        renderItem={({ item, index }) => (
          <ProductCard
            product={item}
            onPress={() => {
              trackClick(item.itemCode, index);
              navigation.navigate('ProductDetail', { code: item.itemCode });
            }}
          />
        )}
      />
    </>
  );
}
```

```ts
useRecommendationWidget(widgetId: string, options?: { currentProductId?: string; enabled?: boolean }): {
  products: SelwiseProduct[];
  productCard: Record<string, unknown> | null;
  loading: boolean;
  error: Error | null;
  followsCart: boolean;
  widget: RecommendationWidgetSummary | null;   // başlık, alt başlık, görünüm tipi
  experiment: { experimentId: string; variantId: string } | null;
  reload: () => void;
  trackImpression: () => void;                  // bağlama başına bir kez
  trackClick: (itemCode: string, position?: number) => void;
}
```

`widgetId` panelde widget'ın kimliğidir. `enabled: false` ürün kodu çözülene kadar isteği bekletir. Hook, bileşen kaldırılınca ve her bağımlılık değişiminde isteği iptal eder; hızlı kaydırmada aynı widget birkaç kez bağlanıp kalkabilir.

> **Ürün sayfasında bağlamı doğru alanla gönderin**
>
> Bakılan ürünün kodu `currentProductId` alanıyla gönderilir. `productItemCode`, `itemCode` ya da `sku` yazmak akla yakın gelir ama bunlar kabul edilir ve **sessizce yok sayılır**: "Benzer Ürünler" bakmakta olduğunuz ürünü önerir, "Birlikte Alınanlar" genel listeye düşer ve hiçbir yerde hata görünmez.

## Doğrudan çağrı

React ağacının dışında (servis, saga, middleware) istemciyi doğrudan kullanın:

```ts
const widgets = await selwise.getRecommendationWidgets('app://product');   // RecommendationWidgetSummary[]
const result = await selwise.getRecommendationProducts(widgets[0].id, { currentProductId: 'SKU-12345' });
if (result.hidden || result.products.length === 0) return;   // hiçbir şey çizme

const heading = result.widget?.title ?? widgets[0].title;
selwise.trackRecommendationImpression(result.widgetId, result.products.map((p) => p.itemCode));
selwise.trackRecommendationClick(result.widgetId, 'SKU-12345', 0);
```

- `getRecommendationWidgets(pageUrl?)` ekrana hedeflenmiş **etkin** widget'ları verir: `id`, `name`, `title`, `subtitle`, `strategy`, `displayType`, `maxProducts`, `isTestMode`, `segmentIds`, `eligibilityFlags` ve stil alanları. Liste sayfa hedeflemesine göre süzülür; 0.13.0'dan beri SDK test modunu ve segmenti de uygular: test modundaki bir şerit yalnızca `testMode: true` ile başlatılan istemciye, segment hedefli bir şerit yalnızca segmentin kabul ettiği ziyaretçiye döner (tarayıcı widget'ıyla aynı kural). Daha eski bir sürümde bu iki alanı siz okuyup uygulamalısınız.
- `getRecommendationProducts(widgetId, context?, signal?)` ziyaretçi bağlamıyla (kimlik, oturum, ziyaretçi) birlikte `POST` eder; bu yüzden `mobile_write` ister. `context`: `currentProductId` ve gerekirse `cart` (bir `RecommendationCartContext`; `null` sepet göndermez).
- Sunucu bir hata yaşarsa `200` ile `{ products: [] }` döner; SDK boş liste verir ve hata fırlatmaz. Şeridin boş gelmesinin sebebi için `debug: true` ve [Sorun Giderme](/docs/mobile/troubleshooting).

### Ürün alanları

```ts
interface SelwiseProduct {
  id: string; itemCode: string; title: string;
  price: number; currency: string; originalPrice?: number | null; discountRate?: number | null;
  imageUrl?: string | null; url?: string | null; brand?: string | null; categories?: string | null;
  rating?: number | null; reviewCount?: number | null;
  isNew?: boolean; isBestseller?: boolean; inStock?: boolean; stock?: number | null;
  sku?: string | null; description?: string | null; shortDescription?: string | null;
  productType?: string | null; categoryHierarchy?: string | null;
  explainability?: Record<string, unknown> | null;
  complement?: { role: string; relation: string; label: { tr: string; en: string } } | null;
}
```

Sunucu bunlara ek olarak `cartCode` (sepetin kabul ettiği kod; `null` ise `itemCode` geçerli), `groupCode` (kardeş ürünleri birleştiren grup anahtarı, kimlik değildir), `hasVariants` ve `variants` (`variantCode`, `sku`, `size`, `color`, `price`, `stock`, `inStock`) alanlarını da gönderir; bu dört alan 0.12.0 tip tanımında yoktur, TypeScript'te bir tür dönüşümüyle okuyun. Sepete eklemek için kullanılacak kod `cartCode`'dur (boşsa `itemCode`); bedenli kategorilerde `variants[].variantCode`. Bkz. [Varyant Gruplama](/docs/product-feeds/variant-grouping). `complement` yalnızca [Tamamlayıcı](/docs/recommendations/complementary) stratejisinde doludur: rol başına sekme için `role` ile grupla, bakış (look) için `label` ile alt yazı yaz.

## Gösterim ve tıklama

Web'de gösterim ve tıklama otomatiktir; mobilde arayüzü siz çizdiğiniz için bildirimi de siz yaparsınız.

> **Bildirim yoksa rapor boş kalır**
>
> Gösterim bildirmezseniz öneri widget'ı panelde sıfır gösterim gösterir ve ziyaretçi deneye sayılmaz. Tıklamayı bildirmezseniz öneriden gelen ciro widget'a **hiç atfedilmez**. Raporda şeridin kimliği olarak sonuçtaki `widgetId` kullanılır (hook bunu kendisi yapar).

SDK sepete ekleme **işlemini yapmaz**; uygulamanızın sepet servisi yapar. Olayı yine kaydedin: `selwise.trackAddToCart(item.itemCode, { quantity: 1 })` ([Event Takibi](/docs/mobile/tracking)).

## Sepeti tamamlayan şerit

[Kargo Bedavaya Tamamla](/docs/recommendations/free-shipping-complement) şeridi, uygulamanın `setCart` ile bildirdiği sepete göre ürün seçer; SDK sepeti yalnızca bu stratejiye ekler. Sepet bildirilmemişse şerit boş döner. `useRecommendationWidget` böyle bir şeridi sepetle birlikte kendiliğinden günceller (`followsCart: true`):

| Durum | Hook ne yapar |
|---|---|
| Sepet değişti | 300 ms bekler (art arda değişiklikler tek istek) ve şeridi yeniden sorar |
| Yeni ürünler geldi | Şerit ekranda kalır, kartlar yerinde değişir; `loading` `true` olmaz, ikinci gösterim sayılmaz |
| Sepet eşiği geçti | `products` boş olur; şeridi çizmeyin |
| Sepet bilinmiyor (`setCart(null)`) | İstek atılmaz, `products` boş olur |

Boşalıp sepet değişince yeniden dolan şerit yeni bir gösterimdir. `setCart`'taki `codes` sepetteki ürünlerin tekrar önerilmesini engeller ([Sepet ve Dinamik İçerik](/docs/mobile/dynamic-content)). Hook kullanmayan uygulama aynı davranışı `RecommendationWidgetLoader` ile kurar (0.10.0 ve sonrası):

```ts
const loader = new RecommendationWidgetLoader(selwise, widgetId, { currentProductId }, (state) => {
  // state.products, state.followsCart, state.appearance (şerit boşken dolduğunda artar)
});
loader.load();
// kaldırılırken: loader.stop();
```

## A/B deneyleri

Bir öneri widget'ı üzerindeki [deney](/docs/experiments/variants) uygulamada da çalışır; widget'ı kimliğiyle çağıran uygulamanın kod değiştirmesi gerekmez. SDK, bir şeridin ürünlerini istemeden önce ziyaretçinin kolunu çözer (açılış başına bir kez sorulur).

| Mod | Uygulamada |
|---|---|
| `different_module` | Kolun şeridi kontrol yerine çizilir; ürünler o şeridin kimliğiyle istenir. `result.widgetId` o şeridi, `result.sourceWidgetId` sizin istediğinizi anlatır |
| `config_override` | Şerit aynı kalır; kolun başlığı, alt başlığı, görünüm tipi ve stili `result.widget`'a uygulanır. Strateji ve ürün sayısını sunucu uygular, SDK kırpıp yeniden sıralamaz |
| Kolun şeride hiçbir şey çizmediği durum | `hidden: true`, ürün isteği atılmaz; çizmeyin |

- **Başlığı sabit metninizden değil `widget?.title`'dan çizin**; yoksa başlık deneyinin iki kolu aynı görünür.
- Ziyaretçi, şeridin **ilk gösterim bildiriminde** deneye sayılır (her kol için açılış başına bir kez; test modundaki cihaz sayılmaz). Gösterim bildirmezseniz uygulama ziyaretçileri sonuçlarda görünmez.
- Deneyin Kitle adımı varsa şerit yalnızca eşleşen ekranda deneye girer; ekran adı, mobil cihaz, sunucunun bildirdiği ülke, ölçülen sepet, segmentler ve test modu değerlendirilir. Sayfa türü ya da ekrandaki ürün gibi yalnızca `getSurfaces`'a verilen bilgiyi isteyen kurallar şeritte eşleşmez; ziyaretçi canlı şeridi görür ve sayılmaz.
- Öneri deneylerinde holdout kolu kurulamaz; gelirse şerit çizilmez ve ziyaretçi sayılmaz.
- Sepeti okuyan bir şerit koyan kol, kolun şeridine göre değerlendirilir (0.11.0 ve sonrası; eski SDK herkese kontrolü gösterir ve kimseyi saymaz).

`resolveRecommendationWidget(widget)` aynı cevabı ürün istemeden verir; şeridi nasıl çizeceğinize önceden karar vermek için kullanılır.

## Sayfa hedefleme

Öneri widget'ının mobilde görünmesi için panelde sayfa hedeflemesi sentetik ekran adlarınızla eşleşmelidir (`app://product`); bkz. [Event Takibi](/docs/mobile/tracking#panelde-hedefleme-nasıl-eşleşir) ve [Sayfa Hedefleme](/docs/recommendations/page-targeting).
