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

# SDK Mobile - React Native

**Dépôt Github :** [https://github.com/KlipFit/kleep-rn](https://github.com/KlipFit/kleep-rn)

Un wrapper WebView léger autour de `drawer.kleep.ai`. Le SDK expose un composant + deux méthodes, reproduisant l'interface iOS.

## Installation

***

Chaque version est publiée sous forme de tarball npm sur `cdn.kleep.ai`. Deux formats d'URL sont disponibles :

**Dernière version stable** (se met à jour automatiquement à chaque nouvelle version stable — les pré-versions ne déplacent jamais ce pointeur) :

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

**Épingler une version spécifique** (recommandé pour la production — entièrement immuable) :

```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
```

Remplacez `v1.0.0` et `1.0.0` par le tag de version souhaité. Les deux packages listés sont des dépendances peer — le SDK les requiert mais vous laisse contrôler la version.

Le tarball publié inclut un SHA-256 à côté (`<tarball>.sha256` / `latest.tgz.sha256`) si vous souhaitez vérifier l'intégrité avant l'installation.

### Expo

Les deux dépendances peer sont pré-incluses dans Expo Go (SDK 54+). Aucune configuration supplémentaire n'est nécessaire en développement. Pour les builds de production, `expo prebuild` les récupère automatiquement.

### React Native nu

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

## Permissions

***

Pour le flux chaussures (scan caméra), ajoutez à `ios/<App>/Info.plist` :

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

Et à `android/app/src/main/AndroidManifest.xml` :

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

## Configuration au démarrage de l'application

***

```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
});
```

| Champ      | Requis | Description                                                                                                            |
| ---------- | ------ | ---------------------------------------------------------------------------------------------------------------------- |
| `publicId` | oui    | UUID identifiant votre revendeur (fourni par Kleep)                                                                    |
| `language` | non    | `'fr' \| 'en' \| 'de' \| 'it' \| 'es' \| 'nl' \| 'pt' \| 'ja' \| 'ko' \| 'pl' \| 'br' \| 'dk' \| 'fi' \| 'se' \| 'gb'` |

## Utilisation

***

### Méthode 1 : `Kleep.checkProduct`

À appeler au chargement de la PDP. Le résultat détermine le CTA « Trouver ma taille » — s'il faut l'afficher, et quel libellé montrer.

| paramètre   | priorité | description                                      |
| ----------- | -------- | ------------------------------------------------ |
| `productId` | *requis* | L'identifiant de votre produit chez le revendeur |

**Retourne**

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

**Schéma logique**

| `recommendable` | `recommendedSize` | Ce qu'il faut afficher                            |
| --------------- | ----------------- | ------------------------------------------------- |
| `false`         | —                 | **Masquer le CTA** (produit non éligible à Kleep) |
| `true`          | absent            | CTA : **« Trouver ma taille »**                   |
| `true`          | `"M"`             | CTA : **« Taille recommandée : M »**              |

Le SDK met en cache le résultat pendant 5 min en mémoire, indexé sur `(publicId, productId)` pour la vérification d'éligibilité et `(publicId, productId, mid)` pour la taille recommandée. Le re-rendu de la PDP ou le retour en arrière est gratuit.

**Exemple d'implémentation**

```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)}
      />
    </>
  );
}
```

### Méthode 2 : `<KleepFindSizeView>`

Montez ce composant pour ouvrir le drawer de recherche de taille dans un Modal+WebView plein écran. Modèle de composant contrôlé : vous gérez l'état `visible`, le SDK demande la fermeture via `onDismiss`.

| prop           | priorité    | description                                                                                                                                                  |
| -------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `visible`      | *requis*    | Contrôle la visibilité du Modal                                                                                                                              |
| `productId`    | *requis*    | Identique à celui dans `checkProduct`                                                                                                                        |
| `onDismiss`    | *requis*    | Déclenché lorsque l'utilisateur ferme le drawer (X / glissement / retour). L'hôte DOIT passer `visible` à `false`                                            |
| `variantId`    | *optionnel* | Pré-sélectionne une variante pour la recommandation                                                                                                          |
| `customerId`   | *optionnel* | Identifiant CRM pour la liaison inter-sessions                                                                                                               |
| `language`     | *optionnel* | Surcharge la langue au niveau SDK pour cette ouverture                                                                                                       |
| `countryCode`  | *optionnel* | ex. `"FR"`, `"US"` — détermine le système d'unités + les valeurs par défaut de taille de soutien-gorge                                                       |
| `stocks`       | *optionnel* | `{ [variantId]: number \| boolean }` — le drawer affiche l'UI indisponible / stock partiel                                                                   |
| `mock`         | *optionnel* | `true` → le drawer ignore les vrais appels API de recommandation (pour les tests QA uniquement)                                                              |
| `forceState`   | *optionnel* | `'outOfRange' \| 'unavailable' \| 'error' \| 'qrcode'` — trappe QA pour afficher directement un état final                                                   |
| `warmRestore`  | *optionnel* | `{ mid, uid }` — pré-charge une mesure existante (ignore le flux d'introduction). La restauration sur le même appareil est déjà automatique via AsyncStorage |
| `extraParams`  | *optionnel* | `Record<string, string>` brut ajouté à l'URL du drawer (trappe d'échappement)                                                                                |
| `onAddToCart`  | *optionnel* | `(event: { variantId, size? }) => void` — l'utilisateur a tapé le CTA « Ajouter au panier » dans le drawer. **L'hôte ajoute la variante à son panier**       |
| `onSelectSize` | *optionnel* | `(event: { size }) => void` — l'utilisateur a choisi une taille sur l'écran de résultat. **L'hôte doit synchroniser son sélecteur de taille PDP**            |
| `onMessage`    | *optionnel* | Hook de débogage — se déclenche pour chaque postMessage entrant analysé depuis le drawer                                                                     |
| `style`        | *optionnel* | `StyleProp<ViewStyle>` — surcharge du style du conteneur                                                                                                     |
| `webViewProps` | *optionnel* | Transmis à `react-native-webview` pour une personnalisation avancée                                                                                          |

**Câblé automatiquement par le SDK (vous n'avez rien à faire) :**

* Connecte le `window.parent.postMessage` de type iframe du drawer au bridge React Native
* Persiste `mid` / `uid` dans AsyncStorage lorsque le drawer les envoie
* Répond aux postMessages `getMid` / `getUid` / `getSizes` du drawer
* Appelle `Kleep.checkProduct` à l'ouverture pour résoudre le flux (vêtements / lingerie / chaussures / enfants) — utilise le même cache de 5 min que votre appel CTA, donc gratuit si vous avez déjà invoqué `checkProduct`

### Méthode 3 : `Kleep.track`

Analytics fire-and-forget. Ne lève jamais d'exception.

| paramètre            | priorité    | description                                                 |
| -------------------- | ----------- | ----------------------------------------------------------- |
| `eventName`          | *requis*    | Nom de l'événement                                          |
| `options.customerId` | *optionnel* | Identifiant CRM                                             |
| `options.parameters` | *optionnel* | `Record<string, unknown>` — données d'événement arbitraires |

```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 },
});
```

Nous souhaitons tracker 3 événements :

| eventName               | Déclencheur                                  |
| ----------------------- | -------------------------------------------- |
| `product_viewed`        | À la consultation de la PDP                  |
| `product_added_to_cart` | À l'ajout au panier                          |
| `checkout_completed`    | À la confirmation de commande après paiement |

**Exemple `product_viewed`**

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

**Exemple `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" }
    }
  ]
}
```

**Exemple `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" }
    }
  ]
}
```

## Invalidation du cache

***

Le SDK maintient deux caches en mémoire (TTL de 5 min chacun) :

* **Vérification d'éligibilité du produit** — `(publicId, productId) → { recommendable, category, productFound }`
* **Taille recommandée** — `(publicId, productId, mid) → libellé de taille`

Les deux s'invalident automatiquement. Si vous devez forcer un rafraîchissement (utilisateur déconnecté, revendeur changé, bouton de rafraîchissement manuel) :

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