# Widget por npm y React

Instala @utilia-os/widget por npm, inicialízalo desde tu código y contrólalo en React con todas las opciones de configuración.

El widget de soporte también se distribuye como paquete npm, `@utilia-os/widget`. Es la vía adecuada si tu web es una aplicación de página única o usa un empaquetador (Vite, webpack) o un framework como React, Vue o Svelte: lo inicializas desde tu código, lo abres desde tus propios botones y te enteras de cada ticket creado.

Si solo necesitas pegar un fragmento en el HTML, la [etiqueta de script](https://os.utilia.ai/docs/developers/widget/script-tag) es más sencilla. En ambos casos, prepara antes la aplicación externa, su clave y tus dominios autorizados como se explica en la [visión general](https://os.utilia.ai/docs/developers/widget/overview).

## Instalación

```bash
npm install @utilia-os/widget
```

El paquete trae sus propios tipos de TypeScript e incluye dentro el SDK de JavaScript, así que no tienes que instalar nada más. Al importarlo se registra el componente web `utilia-support`; funciona en cualquier navegador actual con soporte de componentes web.

## Uso básico

`initUtiliaWidget` busca el elemento `<utilia-support>` en la página; si no existe, lo crea y lo añade al final del `body`. Después lo configura y te devuelve la instancia para controlarlo.

```ts
import { initUtiliaWidget } from '@utilia-os/widget';

const widget = initUtiliaWidget({
  baseURL: 'https://os.utilia.ai/api',
  apiKey: 'TU_CLAVE_DE_API',
  userId: '9b2f6c1e-4d7a-4f0e-8a51-3c6d2e7f1a90',
  userSignature: firmaDelUsuario, // la calcula tu servidor con el secreto del widget
});

widget.open();
```

La clave de API viaja en el navegador y cualquiera puede leerla: usa una aplicación externa dedicada al widget, con los scopes mínimos y tus dominios autorizados, como se explica en la [visión general](https://os.utilia.ai/docs/developers/widget/overview).

## Opciones de configuración

Solo `baseURL`, `apiKey` y `userId` son obligatorias.

| Nombre | Tipo | Obligatorio | Por defecto | Descripción |
| - | - | - | - | - |
| `baseURL` | `string` | Sí | | Dirección base de la API: `https://os.utilia.ai/api`. |
| `apiKey` | `string` | Sí | | Clave de API de la aplicación externa. |
| `userId` | `string` | Sí | | Identificador opaco de la persona en tu sistema. El widget enseña sus tickets: que no se pueda adivinar. |
| `userSignature` | `string` | No | | Firma del userId hecha en tu servidor con el secreto del widget (HMAC-SHA256 en hexadecimal). Obligatoria si la aplicación exige la verificación de identidad. Desde la versión 1.2.0 del widget. |
| `userEmail` | `string` | No | | Correo de la persona. Se guarda al identificarla. |
| `userName` | `string` | No | | Nombre de la persona. Se guarda al identificarla. |
| `userMetadata` | `Record<string, unknown>` | No | | Datos adicionales de la persona, por ejemplo su plan o su empresa en tu sistema. |
| `position` | `'bottom-right' \| 'bottom-left'` | No | `bottom-right` | Esquina en la que aparece el botón. |
| `theme` | `'light' \| 'dark' \| 'auto'` | No | `light` | Aspecto del widget. `auto` se acepta, pero hoy se pinta igual que `light`. |
| `defaultCategory` | `'CONSULTA' \| 'BUG' \| 'SUGERENCIA'` | No | `CONSULTA` | Categoría preseleccionada en el formulario. Son las tres que acepta la API. |
| `defaultPriority` | `'BAJA' \| 'MEDIA' \| 'ALTA' \| 'CRITICA'` | No | `MEDIA` | Prioridad preseleccionada en el formulario. |
| `autoContext` | `boolean` | No | `false` | Si es `true`, cada ticket lleva la dirección de la página y los datos del navegador. |
| `onTicketCreated` | `(ticket: { id: string; ticketKey: string }) => void` | No | | Se llama al crear un ticket, con su identificador y su clave legible. |
| `onError` | `(error: Error) => void` | No | | Se llama cuando falla una operación del widget. |

Fíjate en `autoContext`: por npm está desactivado si no lo pones, mientras que con la etiqueta de script está activado salvo que escribas `false`.

## Integración con React

En React, el widget sigue la vida del componente que lo monta:

**El widget dentro de un componente de React**

1. **Al montar** (useEffect): initUtiliaWidget crea el elemento, identifica al usuario y pide sus mensajes sin leer. Guarda la instancia en un useRef.
2. **Mientras está en pantalla** (Tus botones): Llamas a open() o showNewTicketForm(), y onTicketCreated te avisa de cada ticket nuevo.
3. **Al desmontar** (Limpieza del efecto): widget.remove() quita el elemento de la página.

```tsx
import { useEffect, useRef, useState } from 'react';
import { initUtiliaWidget } from '@utilia-os/widget';

export function SoporteWidget({ usuario }: { usuario: { id: string; email: string; nombre: string } }) {
  const widgetRef = useRef<ReturnType<typeof initUtiliaWidget> | null>(null);
  const [ultimoTicket, setUltimoTicket] = useState<string | null>(null);

  useEffect(() => {
    const widget = initUtiliaWidget({
      baseURL: 'https://os.utilia.ai/api',
      apiKey: import.meta.env.VITE_UTILIA_API_KEY,
      userId: usuario.id,
      userEmail: usuario.email,
      userName: usuario.nombre,
      autoContext: true,
      onTicketCreated: (ticket) => setUltimoTicket(ticket.ticketKey),
      onError: (error) => console.error('Widget de soporte:', error),
    });

    widgetRef.current = widget;

    // Al desmontar, el widget sale de la página.
    return () => {
      widget.remove();
      widgetRef.current = null;
    };
  }, [usuario.id, usuario.email, usuario.nombre]);

  return (
    <div>
      <button onClick={() => widgetRef.current?.open()}>Abrir soporte</button>
      <button onClick={() => widgetRef.current?.showNewTicketForm()}>Informar de un problema</button>
      {ultimoTicket && <p>Último ticket creado: {ultimoTicket}</p>}
    </div>
  );
}
```

Cada llamada a `initUtiliaWidget` vuelve a identificar al usuario y a pedir el contador de no leídos, y ambas peticiones cuentan para el [límite de la clave](https://os.utilia.ai/docs/developers/getting-started/rate-limits-and-cors). Por eso el efecto depende solo de los datos del usuario: si cambian, el widget se reconfigura; si no, se queda como está.

## Métodos de la instancia

| Método | Qué hace |
| - | - |
| `open()` | Abre el widget con la lista de tickets del usuario. Emite `widget:open`. |
| `close()` | Lo cierra y deja solo el botón flotante. Emite `widget:close`. |
| `showNewTicketForm()` | Abre directamente el formulario de ticket nuevo. |
| `showTicketDetail(ticketId)` | Abre el detalle de un ticket a partir de su identificador. |

La instancia es el propio elemento `<utilia-support>`, así que también puedes escuchar sus eventos con `addEventListener`. Están explicados en [Eventos y callbacks](https://os.utilia.ai/docs/developers/widget/events-and-callbacks).

## Siguiente paso

- [Eventos y callbacks](https://os.utilia.ai/docs/developers/widget/events-and-callbacks): Reacciona a la creación de tickets y a la apertura o cierre del widget.
- [Variables CSS](https://os.utilia.ai/docs/developers/widget/css-variables): Adapta los colores y el radio de las esquinas a tu marca.
- [Problemas frecuentes](https://os.utilia.ai/docs/developers/widget/troubleshooting): Qué revisar si el widget no aparece o da errores.
