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

# Cards Personalizadas

> Renderiza tus propias cards cuando el agente ejecuta acciones.

Cuando el agente ejecuta una accion (service o action), el SDK renderiza automaticamente una tarjeta con el resultado. Con `customCardRenderers` puedes reemplazar las cards por defecto o agregar nuevas para tus propios intents.

## Como Funciona

1. El agente ejecuta una accion con un `intent` especifico (ej: `buscar_producto`)
2. El SDK busca si existe un renderer custom para ese intent
3. Si existe, renderiza tu componente. Si no, usa la card generica del SDK

## Uso

```tsx theme={null}
import { PlazbotProvider, Chat } from 'plazbot/react'

const ProductCard = ({ action }) => {
  const data = action.result as {
    name: string
    price: number
    image: string
    stock: boolean
  }

  if (!data) return null

  return (
    <div style={{
      border: '1px solid #e5e7eb',
      borderRadius: 6,
      padding: 12,
      display: 'flex',
      gap: 12,
      alignItems: 'center',
    }}>
      {data.image && (
        <img src={data.image} alt={data.name}
          style={{ width: 56, height: 56, borderRadius: 4, objectFit: 'cover' }} />
      )}
      <div>
        <p style={{ fontWeight: 600 }}>{data.name}</p>
        <p style={{ color: '#6b7280', fontSize: 13 }}>
          ${data.price.toFixed(2)} {data.stock ? '- En stock' : '- Agotado'}
        </p>
      </div>
    </div>
  )
}

<PlazbotProvider sdk={sdk} agentId="ag_xxxx">
  <Chat
    customCardRenderers={{
      buscar_producto: ProductCard,
    }}
  />
</PlazbotProvider>
```

## Props del Renderer

Cada componente custom recibe un unico prop `action`:

```typescript theme={null}
interface ActionProps {
  action: ActionExecuted
}

interface ActionExecuted {
  name?: string      // Nombre descriptivo de la accion
  intent?: string    // Identificador del intent
  result?: unknown   // Datos retornados por el servicio/accion
}
```

## Ejemplo: Card de Cita

```tsx theme={null}
const AppointmentCard = ({ action }) => {
  const data = action.result as {
    date: string
    time: string
    doctor: string
    confirmed: boolean
  }

  if (!data) return null

  return (
    <div style={{
      border: '1px solid #e5e7eb',
      borderRadius: 6,
      padding: 12,
      background: data.confirmed ? '#f0fdf4' : '#fef2f2',
    }}>
      <p style={{ fontWeight: 600, marginBottom: 4 }}>
        {data.confirmed ? 'Cita confirmada' : 'Cita pendiente'}
      </p>
      <p style={{ fontSize: 13, color: '#374151' }}>
        {data.date} a las {data.time} con {data.doctor}
      </p>
    </div>
  )
}
```

## Ejemplo: Card de Pedido

```tsx theme={null}
const OrderCard = ({ action }) => {
  const data = action.result as {
    orderId: string
    items: { name: string; qty: number }[]
    total: number
    status: string
  }

  if (!data) return null

  return (
    <div style={{ border: '1px solid #e5e7eb', borderRadius: 6, padding: 12 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
        <strong>Pedido #{data.orderId}</strong>
        <span style={{
          fontSize: 11,
          padding: '2px 8px',
          borderRadius: 4,
          background: data.status === 'confirmed' ? '#dcfce7' : '#fef9c3',
          color: data.status === 'confirmed' ? '#166534' : '#854d0e',
        }}>
          {data.status}
        </span>
      </div>
      {data.items.map((item, i) => (
        <p key={i} style={{ fontSize: 13, color: '#6b7280' }}>
          {item.qty}x {item.name}
        </p>
      ))}
      <p style={{ fontWeight: 600, marginTop: 8 }}>Total: ${data.total.toFixed(2)}</p>
    </div>
  )
}
```

## Multiples Renderers

Puedes registrar tantos renderers como necesites. Cada uno se asocia al `intent` de un servicio o accion del agente:

```tsx theme={null}
<Chat
  customCardRenderers={{
    buscar_producto: ProductCard,
    agendar_cita: AppointmentCard,
    crear_pedido: OrderCard,
    consultar_saldo: BalanceCard,
  }}
/>
```

## Cards por Defecto del SDK

Si no defines un renderer custom, el SDK usa sus cards internas:

| Tipo             | Card                | Descripcion                           |
| ---------------- | ------------------- | ------------------------------------- |
| `action.tag`     | `ActionCard`        | Muestra la accion ejecutada.          |
| `action.asign`   | `ContactCard`       | Informacion de asignacion.            |
| `action.event.*` | `EventCard`         | Eventos de calendario.                |
| `action.stage`   | `ActionCard`        | Cambio de etapa.                      |
| Service response | `ServiceResultCard` | Resultado generico de servicio.       |
| Sources          | `SourceCard`        | Fuentes de informacion (RAG).         |
| Fallback         | `GenericCard`       | Card generica para datos no mapeados. |

## Callback de Acciones

Ademas de las cards, puedes escuchar cuando se ejecutan acciones:

```tsx theme={null}
<Chat
  customCardRenderers={{ ... }}
  onActionExecuted={(action) => {
    console.log('Intent:', action.intent)
    console.log('Result:', action.result)

    // Ejemplo: tracking analytics
    analytics.track('agent_action', {
      intent: action.intent,
      success: !!action.result,
    })
  }}
/>
```
