Widgets
Ruta: /teacher/widgets · Atajo: g w · Sidebar: Widgets
Configuracion de widgets embebibles para insertar en webs externas. Tres tipos de widget con multiples variantes, preview en vivo y codigo de embed copiable.
Que hay
Section titled “Que hay”Layout de filas expandibles
Section titled “Layout de filas expandibles”Tres filas de tipo de widget, cada una muestra:
- Icono + nombre + conteo de variantes
- Descripcion breve
- Analytics resumidas (30 dias): impresiones, CTR, clicks
- Chevron de expansion
Al expandir, se muestra:
Pills de variantes
Section titled “Pills de variantes”Fila de botones pill para cada configuracion guardada. Estrella en la variante por defecto. Boton “Nueva variante” para crear configs adicionales.
Editor + Preview (2 columnas)
Section titled “Editor + Preview (2 columnas)”Columna izquierda — Editor:
- Input de nombre
- Boton “Set Default” (si no es la variante por defecto)
- Formulario de configuracion (segun tipo)
- Boton “Guardar” (deshabilitado si no hay cambios)
- Badge de analytics
- Seccion de codigo embed (copiable)
- Boton “Eliminar” (rojo, con confirmacion)
Columna derecha — Preview (sticky):
- Marco de navegador simulado (puntos rojo/amarillo/verde)
- Iframe en tiempo real con la configuracion actual
- Auto-resize via postMessage
Tipos de widget
Section titled “Tipos de widget”Modos: inline (pagina completa), popup (modal flotante), button (link)
Configuracion:
- Modo de display
- Tema: light / dark / auto
- Idioma: auto / es / en
- Label del boton (popup/button)
- Color del boton (hex picker)
- Posicion (popup): top-left / top-right / bottom-left / bottom-right
- Pre-fill nombre del alumno (opcional)
- Pre-fill email del alumno (opcional)
Configuracion:
- Modo: grid / list
- Columnas: 1, 2, 3
- Mostrar precios: toggle
- Mostrar descripciones: toggle
- Max servicios: numero (0 = sin limite)
- Mostrar tabs: toggle
- Modo tabs: auto (agrupa por estructura) / custom (tabs manuales)
- Seleccion de servicios: checkboxes
- Filtro por estructura (modo auto)
- Tabs custom: label + picker de servicios por tab
Layouts: carousel / grid / list / wall
Configuracion:
- Layout
- Tema: light / dark / auto
- Idioma
- Color de acento (hex)
- Max reviews: 1-50 (default 6)
- Rating minimo: 1-5 estrellas
- Toggles: rating, body, avatar, fecha, quick tags
- Orden: featured / recent / highest-rated
dateFrom(string, opcional): Fecha inicio para filtrar reviews por fecha de creaciondateTo(string, opcional): Fecha fin para filtrar reviews por fecha de creacion
Codigo embed generado
Section titled “Codigo embed generado”| Widget | Formato |
|---|---|
| Booking inline | <iframe> con handler de resize |
| Booking popup | <script> con boton flotante |
| Booking button | <a> con estilos inline |
| Storefront | <iframe> con handler de resize |
| Reviews | <iframe> con altura responsiva |
Pre-fill de campos en booking
Section titled “Pre-fill de campos en booking”El embed de booking soporta pre-rellenar campos del formulario via URL params. Los params se agregan automaticamente al generar el embed code segun lo que el profesor configure en el formulario de configuracion.
| Param URL | Campo |
|---|---|
prefill_name | Nombre del alumno |
prefill_email | Email del alumno |
prefill_service | ID del servicio pre-seleccionado |
Los modos inline y button incluyen los params directamente en la URL del iframe/enlace.
El modo popup soporta pre-fill via atributos data-* en el tag <script>:
<script src="https://pinteach.com/widget.js" data-teacher="maria-garcia" data-prefill-name="Juan Lopez"></script>El script de popup lee los atributos data-prefill-name y data-prefill-email al inicializarse
y los pasa como prefill_name / prefill_email en la URL del iframe interno cuando el popup se abre.
Custom CSS
Section titled “Custom CSS”Todos los tipos de widget soportan CSS personalizado via textarea en el editor de configuracion:
- Campo
customCssen los schemas de configuracion de los 3 tipos de widget - Sanitizacion XSS: Zod
.refine()bloquea@import,url(),expression(),javascript: - Endpoint dedicado:
GET /:slug/widget-css?configId=xxxretorna CSS comotext/css - Los iframes inyectan el CSS via
<style>tag en mount
Dirty state protection
Section titled “Dirty state protection”- Deteccion de cambios via serializacion JSON (nombre + config)
- Evento
beforeunloadpara navegacion accidental ConfirmDialogal colapsar fila o cambiar variante con cambios sin guardar
Que falta
Section titled “Que falta”| Feature | Descripcion | Estado | Implementado |
|---|---|---|---|
| Pre-fill en modo popup | El script de popup acepta atributos data-prefill-name y data-prefill-email y los pasa al iframe interno. El embed code los incluye automaticamente | ✅ | Batch 3 |
| A/B testing de variantes | Se pueden crear multiples variantes pero no hay forma de comparar rendimiento entre ellas. Deberia haber dashboard de comparacion con metricas por variante | Implementado ✅ | |
| Custom CSS injection | Textarea en config del widget para CSS personalizado. Sanitizacion XSS (bloquea @import, url(), expression(), javascript:). Endpoint GET /:slug/widget-css?configId=xxx retorna CSS como text/css. Iframes lo inyectan via <style> en mount | ✅ | Batch 4 |
Que falla
Section titled “Que falla”| Bug | Descripcion | Estado | Corregido |
|---|---|---|---|
| Reviews carousel altura inicial de 300px | El embed de reviews carousel usa 300px como altura inicial del iframe (otros layouts 500px). El auto-resize via postMessage funciona correctamente — el salto visual es breve y solo ocurre en la carga inicial. No requiere fix |
Que cambiaria
Section titled “Que cambiaria”| Mejora | Descripcion | Dificultad | Estado | Implementado |
|---|---|---|---|---|
| Dashboard de comparacion de variantes | Tabla comparativa de metricas (impresiones, CTR, conversiones) entre variantes del mismo tipo | Medio | Implementado ✅ | |
| Pre-fill en modo popup | El script de popup acepta atributos data-prefill-* | Facil | ✅ | Batch 3 |
| Reviews carousel altura inicial mas precisa | Ajustar la altura inicial del iframe de reviews carousel (actualmente 300px) para reducir el salto visual antes del auto-resize | Facil | Implementado ✅ |
Referencia tecnica
Section titled “Referencia tecnica”Archivos clave
Section titled “Archivos clave”| Archivo | Proposito |
|---|---|
apps/app/src/routes/teacher/widgets.lazy.tsx | Pagina principal |
apps/app/src/components/widgets/widget-editor.tsx | Editor de variante |
apps/app/src/components/widgets/widget-preview-panel.tsx | Preview en vivo |
apps/app/src/components/widgets/widget-embed-section.tsx | Codigo embed |
apps/app/src/components/widgets/widget-embed-utils.ts | Generacion de embed code |
apps/app/src/components/widgets/booking-config-form.tsx | Config booking |
apps/app/src/components/widgets/storefront-config-form.tsx | Config storefront |
apps/app/src/components/widgets/reviews-config-form.tsx | Config reviews |
apps/app/src/components/widgets/widget-analytics-badge.tsx | Badge compacto de analytics |
apps/app/src/components/widgets/widget-types.ts | Tipos TypeScript y configuraciones por defecto |
apps/app/src/components/widgets/use-widget-dirty-state.ts | Deteccion de cambios |
apps/api/src/services/teacher/widget-config-service.ts | Servicio backend |
| Endpoint | Metodo | Proposito |
|---|---|---|
/teacher/widget-configs | GET | Lista por tipo |
/teacher/widget-configs/:id | GET/PATCH/DELETE | CRUD individual |
/teacher/widget-configs | POST | Crear variante |
/teacher/widget-configs/:id/analytics | GET | Metricas 30 dias |