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

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

Um wrapper WebView simples em torno de `drawer.kleep.ai`. O SDK expõe um componente + dois métodos, espelhando a superfície iOS.

## Instalação

***

Cada versão é publicada como um tarball npm em `cdn.kleep.ai`. Estão disponíveis dois formatos de URL:

**Versão estável mais recente** (atualiza automaticamente com cada nova versão estável — as pré-versões nunca movem este pointer):

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

**Fixar uma versão específica** (recomendado para produção — totalmente imutável):

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

Substitua `v1.0.0` e `1.0.0` pela tag de versão desejada. Os dois pacotes listados são dependências de pares — o SDK requer-os mas permite-lhe controlar a versão.

O tarball publicado inclui um SHA-256 adjacente (`<tarball>.sha256` / `latest.tgz.sha256`) caso pretenda verificar a integridade antes de instalar.

### Expo

Ambas as dependências de pares estão pré-incluídas no Expo Go (SDK 54+). Não é necessária configuração adicional no desenvolvimento. Para builds de produção, `expo prebuild` deteta-as automaticamente.

### React Native Simples

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

## Permissões

***

Para o fluxo de calçado (leitura por câmara), adicione 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" />
```

## Configurar uma vez no arranque da aplicação

***

```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      | Obrigatório | Descrição                                                                                                              |
| ---------- | ----------- | ---------------------------------------------------------------------------------------------------------------------- |
| `publicId` | sim         | UUID que identifica o seu retalhista (fornecido pelo Kleep)                                                            |
| `language` | não         | `'fr' \| 'en' \| 'de' \| 'it' \| 'es' \| 'nl' \| 'pt' \| 'ja' \| 'ko' \| 'pl' \| 'br' \| 'dk' \| 'fi' \| 'se' \| 'gb'` |

## Utilização

***

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

Chame este método no carregamento do PDP. O resultado controla o CTA "Encontrar o meu tamanho" — se deve ser apresentado e que etiqueta mostrar.

| parâmetro   | prioridade    | descrição                         |
| ----------- | ------------- | --------------------------------- |
| `productId` | *obrigatório* | O seu ID de produto no retalhista |

**Retorna**

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

**Esquema lógico**

| `recommendable` | `recommendedSize` | O que apresentar                                      |
| --------------- | ----------------- | ----------------------------------------------------- |
| `false`         | —                 | **Ocultar o CTA** (produto não elegível para o Kleep) |
| `true`          | ausente           | CTA: **"Trouver ma taille"**                          |
| `true`          | `"M"`             | CTA: **"Taille recommandée: M"**                      |

O SDK guarda o resultado em cache durante 5 min em memória, indexado por `(publicId, productId)` para o gate e `(publicId, productId, mid)` para o tamanho recomendado. Voltar a renderizar o PDP ou navegar para trás é gratuito.

**Exemplo de implementação**

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

Monte este componente para abrir o drawer de seleção de tamanho num Modal+WebView em ecrã inteiro. Padrão de componente controlado: o estado `visible` fica do seu lado, o SDK solicita o fecho via `onDismiss`.

| prop           | prioridade    | descrição                                                                                                                                             |
| -------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `visible`      | *obrigatório* | Controla a visibilidade do Modal                                                                                                                      |
| `productId`    | *obrigatório* | Igual ao usado em `checkProduct`                                                                                                                      |
| `onDismiss`    | *obrigatório* | Acionado quando o utilizador fecha o drawer (X / swipe / voltar). O host DEVE definir `visible` como `false`                                          |
| `variantId`    | *opcional*    | Pré-seleciona uma variante para a recomendação                                                                                                        |
| `customerId`   | *opcional*    | Identificador CRM para ligação entre sessões                                                                                                          |
| `language`     | *opcional*    | Substitui o idioma definido ao nível do SDK para esta abertura                                                                                        |
| `countryCode`  | *opcional*    | por exemplo `"FR"`, `"US"` — determina o sistema de unidades + padrões de tamanho de soutien                                                          |
| `stocks`       | *opcional*    | `{ [variantId]: number \| boolean }` — o drawer apresenta UI de indisponível / stock parcial                                                          |
| `mock`         | *opcional*    | `true` → o drawer ignora as chamadas reais à API de recomendação (apenas para QA)                                                                     |
| `forceState`   | *opcional*    | `'outOfRange' \| 'unavailable' \| 'error' \| 'qrcode'` — abertura de QA para apresentar diretamente um estado final                                   |
| `warmRestore`  | *opcional*    | `{ mid, uid }` — pré-carrega uma medição existente (ignora o fluxo de introdução). O restore no mesmo dispositivo é já automático via AsyncStorage    |
| `extraParams`  | *opcional*    | `Record<string, string>` em bruto adicionado ao URL do drawer (escape hatch)                                                                          |
| `onAddToCart`  | *opcional*    | `(event: { variantId, size? }) => void` — o utilizador tocou no CTA "Adicionar ao carrinho" no drawer. **O host adiciona a variante ao seu carrinho** |
| `onSelectSize` | *opcional*    | `(event: { size }) => void` — o utilizador escolheu um tamanho no ecrã de Resultado. **O host deve sincronizar o seu seletor de tamanho no PDP**      |
| `onMessage`    | *opcional*    | Gancho de depuração — acionado para cada postMessage recebido analisado do drawer                                                                     |
| `style`        | *opcional*    | `StyleProp<ViewStyle>` — substituição do estilo do container                                                                                          |
| `webViewProps` | *opcional*    | Reencaminhado para `react-native-webview` para personalização avançada                                                                                |

**Ligado automaticamente pelo SDK (não precisa de fazer nada):**

* Faz a ponte entre o `window.parent.postMessage` do drawer (estilo iframe) e a bridge React Native
* Persiste `mid` / `uid` no AsyncStorage quando o drawer os envia
* Responde a postMessages `getMid` / `getUid` / `getSizes` provenientes do drawer
* Chama `Kleep.checkProduct` na abertura para resolver o fluxo (vestuário / lingerie / calçado / infantil) — usa a mesma cache de 5 min que a sua chamada ao CTA, portanto é gratuito se já tiver invocado `checkProduct`

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

Análise fire-and-forget. Nunca lança erros.

| parâmetro            | prioridade    | descrição                                               |
| -------------------- | ------------- | ------------------------------------------------------- |
| `eventName`          | *obrigatório* | Nome do evento                                          |
| `options.customerId` | *opcional*    | Identificador CRM                                       |
| `options.parameters` | *opcional*    | `Record<string, unknown>` — dados arbitrários do 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 },
});
```

Pretendemos fazer o tracking de 3 eventos:

| eventName               | Acionador                                        |
| ----------------------- | ------------------------------------------------ |
| `product_viewed`        | Quando o PDP é visualizado                       |
| `product_added_to_cart` | Quando o produto é adicionado ao carrinho        |
| `checkout_completed`    | Quando a encomenda é confirmada após o pagamento |

**Exemplo de `product_viewed`**

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

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

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

## Invalidação de Cache

***

O SDK mantém duas caches em memória (TTL de 5 min cada):

* **Gate de produto** — `(publicId, productId) → { recommendable, category, productFound }`
* **Tamanho recomendado** — `(publicId, productId, mid) → etiqueta de tamanho`

Ambas se invalidam automaticamente. Se precisar de forçar uma atualização (utilizador terminou sessão, retalhista alterado, botão de atualização manual):

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