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

# Widget web JavaScript

> Instala el widget de captura de woku en cualquier sitio web con un loader <script> ligero y un iframe alojado en el CDN de woku

El **widget web de woku** es una micro-app alojada en el CDN de woku
(`cdn.woku.app`) que captura calificaciones **woku** (1 a 5) o **NPS**
(0 a 10) directamente desde tu sitio web. Lo embebes con un **loader
`<script>` ligero** (\~2.5 KB gzip) que inyecta un `<iframe>` cuando se
dispara un trigger; no necesitas cargar CSS ni frameworks en tu página.

<Note>
  Todos los estilos del widget viven dentro del `<iframe>`, en un documento
  aislado. No hay riesgo de que choquen con los estilos de tu sitio ni de
  que tu JavaScript interfiera con el del widget.
</Note>

## Cómo funciona

1. Tu página carga el **loader** desde `cdn.woku.app`.
2. Llamas a `WokuWidget.init(config)` con tu configuración.
3. El loader evalúa los **triggers** (tiempo, scroll, salida, evento,
   clic) y, al dispararse el primero, **inyecta un `<iframe>`** que apunta
   a la micro-app alojada.
4. La micro-app renderiza el modo según `captureType` (`woku` muestra
   estrellas 1-5; `nps` muestra una escala 0-10), captura el feedback y lo
   envía a la API de woku.
5. El loader emite eventos (`open`, `submit`, `close`, `skip`) que puedes
   escuchar con `WokuWidget.on(...)`.

## Instalación

<Steps>
  <Step title="Obtén tu publishable key">
    En la aplicación administrativa, entra a **Empresa → Integraciones** y
    copia la **publishable key** (`pk_...`). Es segura para embeber en el
    HTML público de tu sitio.
  </Step>

  <Step title="Pega el loader en tu sitio">
    Coloca este snippet antes de `</body>` en las páginas donde quieras
    mostrar el widget.

    ```html theme={null}
    <!-- Woku Widget loader -->
    <script src="https://cdn.woku.app/sdks/woku-widget/v1/loader.js"></script>
    <script>
      WokuWidget.init({
        companyId: 'TU_COMPANY_ID',
        publishableKey: 'pk_live_...',
        captureType: 'nps',          // 'woku' | 'nps'
        // npsToolId: 'NPS_TOOL_ID',  // opcional para NPS
        triggers: [
          { type: 'time', value: 5, behavior: 'modal' },
        ],
      });
    </script>
    ```
  </Step>

  <Step title="Verifica">
    Abre tu sitio y espera a que se dispare el trigger (en el ejemplo, 5
    segundos). El widget aparecerá como modal.
  </Step>
</Steps>

<Warning>
  Usa **siempre** la publishable key (`pk_...`) en el widget, **nunca** la
  clave secreta de la API. El widget es código público: cualquiera puede
  inspeccionar su configuración. La publishable key solo permite capturar
  feedback; no expone datos de tu cuenta.
</Warning>

## Autenticación: publishable key

El widget se autentica con la **publishable key** de tu empresa,
enviándola en el header `x-woku-key` de cada captura. A diferencia de la
clave secreta (usada por integraciones server-side), la publishable key:

* Empieza con el prefijo `pk_` (ej. `pk_live_...`).
* Es **segura de embeber** en HTML público.
* Solo tiene permiso de **captura** (no lee ni administra recursos).

La encuentras en **Empresa → Integraciones** del panel administrativo
([https://admin.woku.app](https://admin.woku.app)).

## Configuración

El objeto que pasas a `WokuWidget.init(...)` acepta estos campos:

| Campo            | Requerido   | Descripción                                                                |
| ---------------- | ----------- | -------------------------------------------------------------------------- |
| `companyId`      | sí          | Identificador de tu empresa.                                               |
| `publishableKey` | sí          | Clave pública `pk_...` (header `x-woku-key`).                              |
| `captureType`    | sí          | `'woku'` (estrellas 1-5) o `'nps'` (escala 0-10).                          |
| `wokuId`         | condicional | Requerido cuando `captureType === 'woku'`.                                 |
| `npsToolId`      | no          | Para NPS; si se omite, el NPS es a nivel de empresa.                       |
| `apiBaseUrl`     | no          | URL base de la API. Por defecto `https://clientapi.woku.app`.              |
| `widgetBaseUrl`  | no          | Origen del iframe. Por defecto `https://cdn.woku.app/sdks/woku-widget/v1`. |
| `lang`           | no          | `'es'` o `'en'`. Si se omite, se autodetecta del navegador.                |
| `branding`       | no          | Muestra "Powered by Woku". Por defecto `true`.                             |
| `theme`          | no          | Personalización visual (ver [Tema](#tema)).                                |
| `triggers`       | sí          | Lista de condiciones que muestran el widget (ver [Triggers](#triggers)).   |
| `urlRules`       | no          | Reglas include/exclude por URL (regex).                                    |

### Tipo de captura: woku vs NPS

<Tabs>
  <Tab title="woku (1-5 estrellas)">
    Captura una calificación de 1 a 5 estrellas asociada a un `wokuId`.

    ```html theme={null}
    <script src="https://cdn.woku.app/sdks/woku-widget/v1/loader.js"></script>
    <script>
      WokuWidget.init({
        companyId: 'TU_COMPANY_ID',
        publishableKey: 'pk_live_...',
        captureType: 'woku',
        wokuId: 'WOKU_ID',           // requerido en modo woku
        triggers: [
          { type: 'exit-intent', behavior: 'modal' },
        ],
      });
    </script>
    ```

    En modo `woku` el usuario ve estrellas 1-5 y, opcionalmente, deja un
    comentario en texto o audio.
  </Tab>

  <Tab title="NPS (0-10)">
    Captura un puntaje NPS de 0 a 10. El `npsToolId` es opcional: si lo
    omites, la captura se asocia al NPS a nivel de empresa.

    ```html theme={null}
    <script src="https://cdn.woku.app/sdks/woku-widget/v1/loader.js"></script>
    <script>
      WokuWidget.init({
        companyId: 'TU_COMPANY_ID',
        publishableKey: 'pk_live_...',
        captureType: 'nps',
        npsToolId: 'NPS_TOOL_ID',    // opcional
        triggers: [
          { type: 'time', value: 10, behavior: 'side-tab' },
        ],
      });
    </script>
    ```

    woku categoriza automáticamente cada respuesta: detractor (0-6),
    pasivo (7-8) o promotor (9-10).
  </Tab>
</Tabs>

### Idioma (i18n)

El widget incluye dos idiomas: **español** (`es`, por defecto) e **inglés**
(`en`). El idioma se resuelve en este orden:

1. El campo `lang` de la configuración.
2. El idioma del navegador (`navigator.language`).
3. Fallback a `es`.

<Tabs>
  <Tab title="Español">
    ```js theme={null}
    WokuWidget.init({
      companyId: 'TU_COMPANY_ID',
      publishableKey: 'pk_live_...',
      captureType: 'nps',
      lang: 'es',
      triggers: [{ type: 'time', value: 5, behavior: 'modal' }],
    });
    ```
  </Tab>

  <Tab title="Inglés">
    ```js theme={null}
    WokuWidget.init({
      companyId: 'TU_COMPANY_ID',
      publishableKey: 'pk_live_...',
      captureType: 'nps',
      lang: 'en',
      triggers: [{ type: 'time', value: 5, behavior: 'modal' }],
    });
    ```
  </Tab>

  <Tab title="Autodetección">
    ```js theme={null}
    // Sin lang: se usa navigator.language y, si no, español.
    WokuWidget.init({
      companyId: 'TU_COMPANY_ID',
      publishableKey: 'pk_live_...',
      captureType: 'nps',
      triggers: [{ type: 'time', value: 5, behavior: 'modal' }],
    });
    ```
  </Tab>
</Tabs>

### Triggers

Cada trigger define **cuándo** aparece el widget y **cómo** se presenta
(`behavior`). Puedes combinar varios; se dispara el primero que cumpla.

```ts theme={null}
interface TriggerConfig {
  type: 'time' | 'scroll' | 'exit-intent' | 'custom-event' | 'click-selector';
  value?: number | string; // segundos, % de scroll, nombre de evento o selector CSS
  behavior: 'modal' | 'banner' | 'side-tab' | 'fullscreen';
}
```

| `type`           | `value`           | Cuándo se dispara                                         |
| ---------------- | ----------------- | --------------------------------------------------------- |
| `time`           | segundos          | Tras N segundos en la página.                             |
| `scroll`         | porcentaje        | Al alcanzar N % de scroll.                                |
| `exit-intent`    |                   | Al mover el cursor fuera de la ventana (solo escritorio). |
| `custom-event`   | nombre del evento | Al dispararse `window.dispatchEvent(new Event(nombre))`.  |
| `click-selector` | selector CSS      | Al hacer clic en un elemento que coincide.                |

Modos de presentación (`behavior`): `modal` (ventana centrada con
overlay), `banner` (barra fija), `side-tab` (pestaña lateral) y
`fullscreen` (pantalla completa).

```js theme={null}
WokuWidget.init({
  companyId: 'TU_COMPANY_ID',
  publishableKey: 'pk_live_...',
  captureType: 'nps',
  triggers: [
    { type: 'scroll', value: 70, behavior: 'banner' },
    { type: 'click-selector', value: '#dar-feedback', behavior: 'modal' },
  ],
});
```

### Tema

Personaliza la apariencia del widget. Los valores se aplican como CSS
custom properties dentro del iframe (excepto `zIndex`, que controla el
overlay en tu página):

```js theme={null}
WokuWidget.init({
  // ...
  theme: {
    primaryColor: '#1447e6',
    fontFamily: 'Inter, sans-serif',
    borderRadius: '12px',
    zIndex: 9999,
  },
});
```

## API programática

El loader expone `window.WokuWidget`:

```js theme={null}
WokuWidget.show();    // muestra el widget manualmente
WokuWidget.hide();    // lo oculta
WokuWidget.destroy(); // remueve el iframe y limpia los listeners

WokuWidget.on('open',   () => { /* el widget se abrió */ });
WokuWidget.on('close',  () => { /* el widget se cerró */ });
WokuWidget.on('submit', (data) => { console.log('enviado:', data); });
WokuWidget.on('skip',   () => { /* el usuario omitió el feedback */ });
```

## Requisitos de CSP

Si tu sitio usa **Content-Security-Policy**, debes permitir el origen del
CDN tanto para el script del loader como para el iframe de la micro-app:

```
Content-Security-Policy:
  script-src cdn.woku.app;   /* el loader <script> */
  frame-src  cdn.woku.app;   /* el iframe de la micro-app */
```

<Note>
  El loader se carga como `<script src="...">` externo y **no** usa `eval`
  ni scripts inline, así que no necesitas configurar un `nonce` ni
  `unsafe-inline`.
</Note>

## Resolución de problemas

<AccordionGroup>
  <Accordion title="El widget no aparece">
    * Confirma que algún **trigger** se cumple (por ejemplo, espera los
      segundos de un trigger `time`).
    * Revisa la consola del navegador: errores de CSP indican que falta
      permitir `cdn.woku.app` en `script-src` o `frame-src`.
    * Verifica que `companyId` y `publishableKey` sean correctos.
  </Accordion>

  <Accordion title="Error 401 al enviar feedback">
    La `publishableKey` es inválida o no corresponde al `companyId`.
    Vuelve a copiarla desde **Empresa → Integraciones**.
  </Accordion>

  <Accordion title="En modo woku no carga nada">
    Falta `wokuId`, que es **requerido** cuando `captureType === 'woku'`.
  </Accordion>

  <Accordion title="El iframe se ve cortado en móvil">
    El widget ajusta su alto automáticamente vía `postMessage`. Si tu
    sitio aplica `overflow: hidden` o un `transform` al `<body>`, puede
    interferir con el overlay. Usa `behavior: 'fullscreen'` como
    alternativa.
  </Accordion>
</AccordionGroup>
