> ## 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 Móvil - React Native

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

Un wrapper WebView ligero alrededor de `drawer.kleep.ai`. El SDK expone un componente + dos métodos, siguiendo la misma superficie que iOS.

## Instalación

***

Cada versión se publica como un tarball npm en `cdn.kleep.ai`. Hay dos formatos de URL disponibles:

**Última versión estable** (se actualiza automáticamente con cada nueva versión estable — las versiones preliminares nunca mueven este puntero):

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

**Fijar una versión específica** (recomendado para producción — completamente inmutable):

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

Reemplaza `v1.0.0` y `1.0.0` con la etiqueta de versión deseada. Los dos paquetes listados son dependencias peer — el SDK los requiere pero te permite controlar la versión.

El tarball publicado incluye un SHA-256 adjunto (`<tarball>.sha256` / `latest.tgz.sha256`) si deseas verificar la integridad antes de instalar.

### Expo

Ambas dependencias peer están pre-empaquetadas en Expo Go (SDK 54+). No se necesita configuración adicional en desarrollo. Para compilaciones de producción, `expo prebuild` las recoge automáticamente.

### Bare React Native

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

## Permisos

***

Para el flujo de calzado (escaneo con cámara), añade a `ios/<App>/Info.plist`:

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

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

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

## Configurar una vez al arrancar la 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      | Obligatorio | Descripción                                                                                                            |
| ---------- | ----------- | ---------------------------------------------------------------------------------------------------------------------- |
| `publicId` | sí          | UUID que identifica tu comerciante (proporcionado por Kleep)                                                           |
| `language` | no          | `'fr' \| 'en' \| 'de' \| 'it' \| 'es' \| 'nl' \| 'pt' \| 'ja' \| 'ko' \| 'pl' \| 'br' \| 'dk' \| 'fi' \| 'se' \| 'gb'` |

## Uso

***

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

Llama a esto al montar la PDP. El resultado controla el CTA «Encontrar mi talla» — si renderizarlo y qué etiqueta mostrar.

| parámetro   | prioridad     | descripción                            |
| ----------- | ------------- | -------------------------------------- |
| `productId` | *obligatorio* | El ID de tu producto en el comerciante |

**Retorna**

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

**Esquema lógico**

| `recommendable` | `recommendedSize` | Qué renderizar                                       |
| --------------- | ----------------- | ---------------------------------------------------- |
| `false`         | —                 | **Ocultar el CTA** (producto no elegible para Kleep) |
| `true`          | ausente           | CTA: **"Trouver ma taille"**                         |
| `true`          | `"M"`             | CTA: **"Taille recommandée: M"**                     |

El SDK almacena en caché el resultado durante 5 min en memoria, con clave en `(publicId, productId)` para la compuerta y `(publicId, productId, mid)` para la talla recomendada. Volver a renderizar la PDP o navegar de vuelta es gratuito.

**Ejemplo de implementación**

```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étodo 2: `<KleepFindSizeView>`

Monta este componente para abrir el drawer de búsqueda de talla en un Modal+WebView a pantalla completa. Patrón de componente controlado: tú posees el estado `visible`, el SDK solicita el cierre mediante `onDismiss`.

| prop           | prioridad     | descripción                                                                                                                                                       |
| -------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `visible`      | *obligatorio* | Controla la visibilidad del Modal                                                                                                                                 |
| `productId`    | *obligatorio* | El mismo que en `checkProduct`                                                                                                                                    |
| `onDismiss`    | *obligatorio* | Se activa cuando el usuario cierra el drawer (X / deslizamiento / atrás). El host DEBE cambiar `visible` a `false`                                                |
| `variantId`    | *opcional*    | Pre-selecciona una variante para la recomendación                                                                                                                 |
| `customerId`   | *opcional*    | Identificador CRM para vinculación entre sesiones                                                                                                                 |
| `language`     | *opcional*    | Sobreescribe el idioma a nivel de SDK para esta apertura                                                                                                          |
| `countryCode`  | *opcional*    | Por ejemplo `"FR"`, `"US"` — determina el sistema de unidades y los valores predeterminados de talla de sujetador                                                 |
| `stocks`       | *opcional*    | `{ [variantId]: number \| boolean }` — el drawer muestra la interfaz de no disponible / stock parcial                                                             |
| `mock`         | *opcional*    | `true` → el drawer omite las llamadas reales a la API de recomendación (solo para QA)                                                                             |
| `forceState`   | *opcional*    | `'outOfRange' \| 'unavailable' \| 'error' \| 'qrcode'` — acceso QA para renderizar un estado final directamente                                                   |
| `warmRestore`  | *opcional*    | `{ mid, uid }` — precarga una medición existente (omite el flujo de introducción). La restauración en el mismo dispositivo ya es automática mediante AsyncStorage |
| `extraParams`  | *opcional*    | `Record<string, string>` sin procesar añadido a la URL del drawer (vía de escape)                                                                                 |
| `onAddToCart`  | *opcional*    | `(event: { variantId, size? }) => void` — el usuario pulsó el CTA «Añadir al carrito» en el drawer. **El host añade la variante a su carrito**                    |
| `onSelectSize` | *opcional*    | `(event: { size }) => void` — el usuario eligió una talla en la pantalla de Resultados. **El host debe sincronizar su selector de talla en la PDP**               |
| `onMessage`    | *opcional*    | Hook de depuración — se activa por cada postMessage entrante analizado desde el drawer                                                                            |
| `style`        | *opcional*    | `StyleProp<ViewStyle>` — sobreescritura del estilo del contenedor                                                                                                 |
| `webViewProps` | *opcional*    | Reenviado a `react-native-webview` para personalización avanzada                                                                                                  |

**Conectado automáticamente por el SDK (no necesitas hacer nada):**

* Enlaza el `window.parent.postMessage` estilo iframe del drawer al puente de React Native
* Persiste `mid` / `uid` en AsyncStorage cuando el drawer los envía
* Responde a los postMessages `getMid` / `getUid` / `getSizes` del drawer
* Llama a `Kleep.checkProduct` al abrir para resolver el flujo (ropa / lencería / calzado / niños) — usa la misma caché de 5 min que tu llamada al CTA, por lo que es gratuito si ya has invocado `checkProduct`

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

Analíticas de tipo fire-and-forget. Nunca lanza excepciones.

| parámetro            | prioridad     | descripción                                              |
| -------------------- | ------------- | -------------------------------------------------------- |
| `eventName`          | *obligatorio* | Nombre del evento                                        |
| `options.customerId` | *opcional*    | Identificador CRM                                        |
| `options.parameters` | *opcional*    | `Record<string, unknown>` — datos arbitrarios del evento |

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

Queremos rastrear 3 eventos:

| eventName               | Disparador                                       |
| ----------------------- | ------------------------------------------------ |
| `product_viewed`        | Al visualizar la PDP                             |
| `product_added_to_cart` | Al añadir producto al carrito                    |
| `checkout_completed`    | Tras la confirmación del pedido después del pago |

**Ejemplo de `product_viewed`**

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

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

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

## Invalidación de caché

***

El SDK mantiene dos cachés en memoria (TTL de 5 min cada una):

* **Compuerta de producto** — `(publicId, productId) → { recommendable, category, productFound }`
* **Talla recomendada** — `(publicId, productId, mid) → size label`

Ambas se invalidan automáticamente. Si necesitas forzar una actualización (usuario cerró sesión, comerciante cambiado, botón de actualización manual):

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