# Kampanya ve Widget

Segment, deney, sıklık sınırı ve custom widget
Kaynak: https://selwise.com/docs/mobile/surfaces

Panelde tanımladığınız kampanya ve widget'lar uygulamada da çalışır. `getSurfaces()` tek bir soruyu yanıtlar: **bu ekran şu an hangi yüzeyleri göstermeye hakkı var?** Cevap veridir; çizimi uygulamanız yapar. Akinon App Maker kullanıyorsanız [eklenti](/docs/mobile/app-maker) bunu sizin yerinize yapar.

## Kullanım

```ts
getSurfaces(
  pageUrl?: string,                       // yoksa setScreen ile verilen ekran
  context?: {
    pageType?: 'home' | 'product' | 'category' | 'search' | 'checkout' | 'cart' | 'account' | 'other';
    deviceType?: 'mobile' | 'tablet' | 'desktop';   // varsayılan 'mobile'
    country?: string;                     // ISO 3166-1 alpha-2
    behavior?: Partial<Record<BehaviorSignal, number | string | boolean>>;
    locale?: string;                      // tutar biçimi, ör. 'tr-TR'
    product?: ProductInput | null;        // ürün ekranında
  }
): Promise<Surface[]>
```

```ts
selwise.trackScreenView('app://basket');
const surfaces = await selwise.getSurfaces('app://basket');

for (const surface of surfaces) {
  // surface.type, surface.content, surface.styles, surface.placement,
  // surface.stateId, surface.experiment, surface.customCss, surface.customJs
  draw(surface);
  selwise.trackSurfaceView(surface);          // göründüğünde
  await selwise.recordSurfaceShown(surface);  // sıklık kotasını harca
}
```

`Surface` alanları: `id`, `kind`, `type` (panelde seçtiğiniz tür, ör. `announcement_bar`, `goal_bar`, `custom`), `isTestMode`, `content`, `styles` (içinde `var()` olabilir), `placement` (konum, tetik, sıklık sınırı, öncelik), `targeting`, `segmentIds`, `customCss`, `customJs`, `experiment` ve `stateId`. Hangi türün neye benzediği: [Widget Genel Bakış](/docs/widgets/overview).

Çizim sırasında şunları siz yaparsınız:

| Ne | Çağrı |
|---|---|
| Göründü | `trackSurfaceView(surface)` → `impression` |
| Dokunuldu | `trackSurfaceClick(surface, metadata?)` → `click` |
| Kapatıldı | `await dismissSurface(surface)` → `close`; hatırlanır |
| Kota harca | `await recordSurfaceShown(surface)` |
| Tetik | `placement.triggerStrategy` (`immediate`, `delay`, `scroll`, `exit_intent`, `event`) değerini siz uygularsınız; SDK tetik ateşlemez |

`/config` açılış başına bir kez çekilir (sunucuda 5 dakika önbelleklenir); `invalidateSurfaces()` sonraki çağrıda yeniden çektirir. Kampanyanın başlangıç ve bitiş tarihi sunucuda uygulanır: [Zamanlama](/docs/campaigns/scheduling).

## Hangi kapılardan geçer

`getSurfaces()` web widget'ının sorduğu soruları aynı sırayla sorar; her adım "gereğinden fazla gösterme" yönünde korur:

1. **Test modu.** Gerçek ziyaretçi test modundaki yüzeyi görmez; `testMode: true` ile başlatılan cihaz **yalnızca** test modundakini görür (kapı, filtre değil).
2. **Sayfa hedefleme.** Ekran adı ve `pageType`; bkz. [Event Takibi](/docs/mobile/tracking#panelde-hedefleme-nasıl-eşleşir).
3. **Cihaz, ülke ve davranış.** Aşağıda.
4. **Deney dışlaması.** Varyantın yerine geçtiği kontrol çizilmez.
5. **Segment.** `include` modunda ziyaretçi listedeki segmentlerden en az birinde, `exclude` modunda hiçbirinde olmalıdır. Üyelik sunucuya sorulur ve 5 dakika önbelleklenir. Sunucuya ulaşılamazsa cevap "hiçbir segmentte değil" olur: `include` yüzeyi **gizler**, `exclude` **gösterir**.
6. **Boş içerik.** İçeriği olmayan atlanır; istisna `custom` (kod içeriğin kendisi).
7. **Deney içeriği ve durumlar.** Bkz. [Sepet ve Dinamik İçerik](/docs/mobile/dynamic-content).
8. **Çatışma çözümü.** Aynı yuvaya aday birden fazla yüzeyden biri.
9. **Sıklık sınırı.**

> **Segment kapısı anahtarınızı ister**
>
> Segment üyeliği bir `POST` isteğidir. Yalnızca `mobile_read` taşıyan bir anahtarla bu istek `403` alır, SDK "hiçbir segmentte değil" sonucuna düşer ve **segment hedefli her kampanya sessizce gizlenir**. Ayrıca CGNAT arkasındaki ortak adresler IP başına dakikada 120 sınırına takılıp aynı sonuca düşebilir.

### Cihaz, ülke ve davranış sinyalleri

- **Cihaz:** varsayılan `mobile`; tablet için `deviceType: 'tablet'` verin.
- **Ülke:** kural, ülke bilindiğinde değerlendirilir; bilinmiyorsa kural **açık** kalır (yüzey gösterilir). SDK ülkeyi sunucunun bildirdiği değerden almak için tasarlanmıştır, ancak 0.12.0'da bu değer okunamıyor; ülke hedefli kampanyalar için `country` değerini `getSurfaces` bağlamında **kendiniz verin**.
- **Davranış:** SDK yalnızca sepet (`cartValue`, `cartItemCount`) ve ürün (`productStock`, `productOnSale`, `productDiscountRate`, `productPrice`) sinyallerini kendisi doldurur. `isLoggedIn`, `orderCount`, `lifetimeValue`, `isReturningVisitor`, `utmSource` gibi diğerlerini siz `behavior` ile verin; vermezseniz sinyal varsayılanıyla (`0`, `false`, `direct`) değerlendirilir ve `isLoggedIn eşittir true` kuralı hiç eşleşmez.

## Deneyler

Kampanya ve widget üzerindeki [A/B deneyleri](/docs/experiments/variants) uygulamada çalışır. SDK ziyaretçinin kollarını açılış başına bir kez sorar (`getExperimentAssignments()` listeyi verir) ve her `getSurfaces()` çağrısında uygular:

- `config_override`: kolun içeriği, stili ve yerleşimi kontrolün üzerine birleştirilir (diziler değiştirilir, birleştirilmez).
- `different_module`: kontrol çıkar, kolun yüzeyi listeye girer.
- **Holdout:** kolun yüzeyini siz hiç görmezsiniz; yuva boş kalır.
- **Kitle:** deneyin Kitle adımı varsa kol yalnızca eşleşen ekranda uygulanır ve sayılır. Sayfa türü ya da ekrandaki ürün gibi yalnızca `getSurfaces`'a verdiğiniz bilgiye dayanan kurallar için bu bilgiyi bağlamda verin.

Ziyaretçi, yüzey ekrana verildiği anda (SDK'nın "gösterim" anı) deneye **bir kez** sayılır; test modundaki cihaz sayılmaz. Çizmeseniz bile sayılmış olursunuz, bu yüzden listeyi çağırıp çizmeyecekseniz çağrıyı çizim koşuluna bağlayın. Ayrıntı: [Deney Oluşturma](/docs/experiments/creating).

## Çatışma çözümü

Webde çekişilen kaynak CSS seçicisidir; uygulamada **yuva konumu**dur (`top`, `bottom`, `center`). Aynı yuvaya iki duyuru çubuğu üst üste biner:

| Davranış | Sonuç |
|---|---|
| `highest_priority` (varsayılan) | En yüksek öncelik; eşitlikte `id` sırası |
| `all` | Gruptan biri `all` ise hepsi gösterilir |
| `random` | Grubun tamamı `random` ise biri seçilir |

**Aynı ekranda aynı yuvaya iki yüzey koyarsanız biri hiç görünmez.** Seçiciyle yerleştirilmiş yüzeyler yuva işgal etmez.

## Sıklık sınırı

Panelde tanımlanan `perSession`, `perDay`, `perWeek`, `total` ve `minInterval` (`30m`, `2h`, `1d` biçiminde) uygulamada geçerlidir ve **diske yazılır**; uygulama kapanıp açılınca korunur. Kota yüzey gerçekten çizildiğinde `recordSurfaceShown` ile harcanır, listeye girdiğinde değil: tetiği hiç ateşlenmeyen popup kotasını kaybetmez. Sayaçlar cihaza özgüdür.

## Kapatma ve bülten

- `dismissSurface` kapatmayı kalıcı hatırlar; hedef çubuğu istisnadır ve yalnızca oturum boyunca kapalı kalır (sepet değişir). Test için `resetDismissals()`.
- Bülten yüzeyinde e-posta alındığında `subscribeNewsletter(email, { fullName?, locale?, surfaceId? })` çağrılır; `user_signup` olayını da yazar ve `{ success }` döner. Bülten [widget](/docs/widgets/newsletter) kuralları geçerlidir.

## Test modu

`init({ testMode: true })` ile açılan cihaz yalnızca test modundaki kampanya ve widget'ları görür; panelde test modunda işaretlenen yüzey gerçek ziyaretçiye hiç gösterilmez. Test modu olay partilerine, senaryo gelen kutusuna ve zenginleştirmeye de işlenir; test cihazı deneye sayılmaz. Bkz. [Test Modu](/docs/campaigns/testing).

## Uygulamada karşılığı olmayanlar

`customCss` ve `customJs` yalnızca `custom` türünde WebView içinde çalışır; yerel türlerde uygulanmaz ([Özel Widget](/docs/mobile/custom-widget)). Sekme mesajı (`tab_talk`) uygulama kanalında yoktur.

## Sırada ne var

- [Sepet ve Dinamik İçerik](/docs/mobile/dynamic-content)
- [Özel Widget](/docs/mobile/custom-widget)
- [Senaryo Gelen Kutusu](/docs/mobile/scenario-inbox)
