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

# Mobile SDK - React Native

**Github Repository:** [https://github.com/KlipFit/kleep-rn](https://github.com/KlipFit/kleep-rn)

Un sottile wrapper WebView attorno a `drawer.kleep.ai`. L'SDK espone un componente + due metodi, specchiando la surface iOS.

## Installazione

***

Ogni release viene pubblicata come tarball npm su `cdn.kleep.ai`. Sono disponibili due formati URL:

**Ultima stabile** (si aggiorna automaticamente con ogni nuova release stabile — le pre-release non spostano mai questo puntatore):

```bash theme={null}
npm install https://cdn.kleep.ai/react-native-sdk/latest.tgz \
  react-native-webview \
  @react-native-async-storage/async-storage
```

**Blocca una versione specifica** (consigliato per la produzione — completamente immutabile):

```bash theme={null}
npm install https://cdn.kleep.ai/react-native-sdk/releases/v1.0.0/kleep-react-native-1.0.0.tgz \
  react-native-webview \
  @react-native-async-storage/async-storage
```

Sostituisci `v1.0.0` e `1.0.0` con il tag di release desiderato. I due pacchetti elencati sono peer dependency — l'SDK li richiede ma ti lascia il controllo della versione.

Il tarball pubblicato include un SHA-256 accanto (`<tarball>.sha256` / `latest.tgz.sha256`) se vuoi verificare l'integrità prima dell'installazione.

### Expo

Entrambe le peer dep sono pre-integrate in Expo Go (SDK 54+). Non è necessaria alcuna configurazione aggiuntiva in fase di sviluppo. Per le build di produzione, `expo prebuild` le recupera automaticamente.

### React Native puro

```bash theme={null}
cd ios && pod install
```

## Permessi

***

Per il flusso calzature (scansione fotocamera), aggiungi a `ios/<App>/Info.plist`:

```xml theme={null}
<key>NSCameraUsageDescription</key>
<string>Used to scan your shoe size</string>
```

E a `android/app/src/main/AndroidManifest.xml`:

```xml theme={null}
<uses-permission android:name="android.permission.CAMERA" />
```

## Configura una volta all'avvio dell'app

***

```tsx theme={null}
import { Kleep } from '@kleep/react-native';

Kleep.configure({
  publicId: 'YOUR_KLEEP_PUBLIC_ID',  // UUID provided by Kleep
  language: 'fr',                      // optional, default UI language
});
```

| Campo      | Obbligatorio | Descrizione                                                                                                            |
| ---------- | ------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `publicId` | sì           | UUID che identifica il tuo retailer (fornito da Kleep)                                                                 |
| `language` | no           | `'fr' \| 'en' \| 'de' \| 'it' \| 'es' \| 'nl' \| 'pt' \| 'ja' \| 'ko' \| 'pl' \| 'br' \| 'dk' \| 'fi' \| 'se' \| 'gb'` |

## Utilizzo

***

### Metodo 1: `Kleep.checkProduct`

Chiamare sulla PDP al montaggio. Il risultato guida il CTA "Trova la mia taglia" — se renderizzarlo e quale etichetta mostrare.

| parametro   | priorità       | descrizione                           |
| ----------- | -------------- | ------------------------------------- |
| `productId` | *obbligatorio* | Il tuo ID prodotto presso il retailer |

**Valori restituiti**

```ts theme={null}
{
  recommendable: boolean;
  productFound: boolean;
  category?: 'clothing' | 'lingerie' | 'footwear' | 'children';
  recommendedSize?: string;   // e.g. "M", "38"
}
```

**Schema logico**

| `recommendable` | `recommendedSize` | Cosa renderizzare                                   |
| --------------- | ----------------- | --------------------------------------------------- |
| `false`         | —                 | **Nascondi il CTA** (prodotto non idoneo per Kleep) |
| `true`          | assente           | CTA: **"Trouver ma taille"**                        |
| `true`          | `"M"`             | CTA: **"Taille recommandée: M"**                    |

L'SDK memorizza nella cache il risultato per 5 minuti in memoria, indicizzato su `(publicId, productId)` per il gate e `(publicId, productId, mid)` per la taglia raccomandata. Ri-renderizzare la PDP o tornare indietro è gratuito.

**Esempio di implementazione**

```tsx theme={null}
import { useEffect, useState } from 'react';
import { Pressable, Text } from 'react-native';
import { Kleep, KleepFindSizeView, type KleepCheckProductResult } from '@kleep/react-native';

function ProductPage({ product }) {
  const [check, setCheck] = useState<KleepCheckProductResult | null>(null);
  const [open, setOpen] = useState(false);

  useEffect(() => {
    Kleep.checkProduct({ productId: product.id }).then(setCheck);
  }, [product.id]);

  if (!check?.recommendable) return <ProductContent />;

  return (
    <>
      <ProductContent />
      <Pressable onPress={() => setOpen(true)}>
        <Text>
          {check.recommendedSize
            ? `Taille recommandée : ${check.recommendedSize}`
            : 'Trouver ma taille'}
        </Text>
      </Pressable>
      <KleepFindSizeView
        visible={open}
        productId={product.id}
        variantId={selectedVariant?.id}
        onAddToCart={(e) => cart.add(e.variantId)}
        onSelectSize={(e) => pdp.setSize(e.size)}
        onDismiss={() => setOpen(false)}
      />
    </>
  );
}
```

### Metodo 2: `<KleepFindSizeView>`

Monta questo componente per aprire il drawer del trovaglie in un Modal+WebView a schermo intero. Pattern a componente controllato: sei tu a gestire lo stato `visible`, l'SDK richiede la chiusura tramite `onDismiss`.

| prop           | priorità       | descrizione                                                                                                                                                        |
| -------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `visible`      | *obbligatorio* | Controlla la visibilità del Modal                                                                                                                                  |
| `productId`    | *obbligatorio* | Uguale a quello in `checkProduct`                                                                                                                                  |
| `onDismiss`    | *obbligatorio* | Attivato quando l'utente chiude il drawer (X / swipe / indietro). Il host DEVE impostare `visible` a `false`                                                       |
| `variantId`    | *opzionale*    | Pre-seleziona una variante per la raccomandazione                                                                                                                  |
| `customerId`   | *opzionale*    | Identificatore CRM per il collegamento tra sessioni                                                                                                                |
| `language`     | *opzionale*    | Sovrascrive la lingua a livello SDK per questa apertura                                                                                                            |
| `countryCode`  | *opzionale*    | es. `"FR"`, `"US"` — determina il sistema di unità + le impostazioni predefinite per le misure reggiseno                                                           |
| `stocks`       | *opzionale*    | `{ [variantId]: number \| boolean }` — il drawer visualizza l'UI per indisponibile / stock parziale                                                                |
| `mock`         | *opzionale*    | `true` → il drawer salta le chiamate API reali di raccomandazione (solo QA)                                                                                        |
| `forceState`   | *opzionale*    | `'outOfRange' \| 'unavailable' \| 'error' \| 'qrcode'` — accesso QA per renderizzare direttamente uno stato finale                                                 |
| `warmRestore`  | *opzionale*    | `{ mid, uid }` — pre-carica una misurazione esistente (salta il flusso introduttivo). Il ripristino sullo stesso dispositivo è già automatico tramite AsyncStorage |
| `extraParams`  | *opzionale*    | `Record<string, string>` grezzo aggiunto all'URL del drawer (escape hatch)                                                                                         |
| `onAddToCart`  | *opzionale*    | `(event: { variantId, size? }) => void` — l'utente ha toccato il CTA "Aggiungi al carrello" nel drawer. **Il host aggiunge la variante al carrello**               |
| `onSelectSize` | *opzionale*    | `(event: { size }) => void` — l'utente ha selezionato una taglia nella schermata Risultato. **Il host dovrebbe sincronizzare il selettore di taglie della PDP**    |
| `onMessage`    | *opzionale*    | Hook di debug — si attiva per ogni postMessage in arrivo analizzato dal drawer                                                                                     |
| `style`        | *opzionale*    | `StyleProp<ViewStyle>` — sovrascrittura dello stile del contenitore                                                                                                |
| `webViewProps` | *opzionale*    | Inoltrato a `react-native-webview` per personalizzazioni avanzate                                                                                                  |

**Gestito automaticamente dall'SDK (non è necessario fare nulla):**

* Collega il `window.parent.postMessage` in stile iframe del drawer al bridge React Native
* Persiste `mid` / `uid` in AsyncStorage quando il drawer li invia
* Risponde ai postMessage `getMid` / `getUid` / `getSizes` dal drawer
* Chiama `Kleep.checkProduct` all'apertura per risolvere il flusso (abbigliamento / lingerie / calzature / bambini) — usa la stessa cache da 5 minuti della tua chiamata CTA, quindi è gratuito se hai già invocato `checkProduct`

### Metodo 3: `Kleep.track`

Analisi fire-and-forget. Non genera mai eccezioni.

| parametro            | priorità       | descrizione                                       |
| -------------------- | -------------- | ------------------------------------------------- |
| `eventName`          | *obbligatorio* | Nome dell'evento                                  |
| `options.customerId` | *opzionale*    | Identificatore CRM                                |
| `options.parameters` | *opzionale*    | `Record<string, unknown>` — dati evento arbitrari |

```tsx theme={null}
import { Kleep } from '@kleep/react-native';

await Kleep.track('product_viewed', {
  parameters: { productId: product.id },
});

await Kleep.track('product_added_to_cart', {
  customerId: user.id,
  parameters: {
    productId: product.id,
    variantId: selectedVariant.id,
    cart: cart.items,
  },
});

await Kleep.track('checkout_completed', {
  customerId: user.id,
  parameters: { orderId: order.id, cart: order.items },
});
```

Vogliamo tracciare 3 eventi:

| eventName               | Trigger                                     |
| ----------------------- | ------------------------------------------- |
| `product_viewed`        | Alla visualizzazione della PDP              |
| `product_added_to_cart` | All'aggiunta del prodotto al carrello       |
| `checkout_completed`    | Alla conferma dell'ordine dopo il pagamento |

**Esempio `product_viewed`**

```jsx theme={null}
{
  productId: "123ABC456"
}
```

**Esempio `product_added_to_cart`**

```jsx theme={null}
{
  productId: "123ABC456",
  variantId: "123ABC456-00R",
  cart: [
    {
      productId: "123ABC456",
      variantId: "123ABC456-00R",
      sku: "XYZ",
      size: "S",
      quantity: 2,
      price: { amount: "50", currencyCode: "EUR" }
    }
  ]
}
```

**Esempio `checkout_completed`**

```jsx theme={null}
{
  orderId: "000001",
  cart: [
    {
      lineItemId: "000001#1",
      productId: "123ABC456",
      variantId: "123ABC456-00R",
      sku: "XYZ",
      size: "S",
      quantity: 2,
      price: { amount: "50", currencyCode: "EUR" }
    }
  ]
}
```

## Invalidazione della Cache

***

L'SDK mantiene due cache in memoria (TTL da 5 minuti ciascuna):

* **Gate prodotto** — `(publicId, productId) → { recommendable, category, productFound }`
* **Taglia raccomandata** — `(publicId, productId, mid) → etichetta taglia`

Entrambe si invalidano automaticamente. Se hai bisogno di forzare l'aggiornamento (utente disconnesso, retailer cambiato, pulsante di aggiornamento manuale):

```tsx theme={null}
Kleep.clearCheckProductCache();
```
