# GW Elementor Analytics — Especificación completa para LLMs (Versión Actualizada 2025)

Este documento está diseñado para que un **modelo LLM** entienda **toda la arquitectura**, **flujo**, **dependencias** y **funcionamiento interno** del plugin *GW Elementor Analytics* sin necesidad de información externa.

---

# 1. Propósito del plugin

**Nombre:** GW Elementor Analytics  
**Versión:** 2.2.1  
**Entorno:** WordPress + Elementor Pro  
**Función:** Analítica avanzada, trazabilidad, CRM y gestión de leads enviados por Elementor Forms.

### Objetivos principales
- Rastrear **origen**, **UTMs**, **ruta de navegación** y **comportamiento previo** del usuario.
- Inyectar estos datos automáticamente en **campos ocultos** de Elementor Forms.
- Ofrecer un **Dashboard** con KPIs, gráficas y datos agregados.
- Ofrecer una pestaña separada (**Correos**) para gestionar los leads con **CRM completo**.
- Integrar un sistema de **detección de SPAM** con blacklist/whitelist.
- Sistema de **estados CRM personalizables** + **notas** por lead.
- Sistema de **lectura/no lectura** de correos (read status).
- **Destacar emails** que contengan palabras clave de marketing.
- **Navegación lateral** (sidebar) para organizar emails por categorías.
- **Reportes PDF personalizables** desde la pestaña Informes.
- **Búsqueda global** en toda la base de datos.
- **Filtros avanzados** por origen y formulario.
- Exportar leads en CSV/Excel con separador configurable.
- Utilizar un sistema de **licencias + trial** de 30 días.
- Limpieza completa del admin (Sistema *Terminator*).

---

# 2. Funcionalidades principales (alto nivel)

## A. Tracking en frontend
El script `/assets/js/frontend-tracker.js` detecta:

- Parámetros `utm_*` (utm_source, utm_medium, utm_campaign, etc.)
- `gclid` (Google Click ID)
- `document.referrer`
- Primera página visitada (landing)
- Página previa al formulario
- Página actual donde se envía el formulario

### Comportamiento del sistema de tracking
- Si no hay UTMs ni referrer, asigna: `organico`.
- Encadena UTMs originales a todos los enlaces internos (solo mismo dominio).
- Guarda la ruta completa del usuario en `sessionStorage`.
- Normaliza nombres de orígenes eliminando dominios (.com, .es, etc.) y capitalizando.

---

## B. Inyección automática en Elementor Forms

El tracker rellena automáticamente estos campos ocultos:

- `origen` - Origen del tráfico (normalizado)
- `landing_url` - Primera URL visitada
- `prev_url` - URL previa al formulario
- `current_url` - URL donde se envía el formulario

### Requisitos del formulario
- Los campos deben existir en Elementor con esos mismos *IDs/names*.
- Los tipos deben ser *Hidden* (ocultos).
- Elementor Pro debe tener **Submissions activas**.

### Interceptación de spam en tiempo real
- Hook `elementor_pro/forms/new_record` intercepta nuevos envíos
- Verifica si el email está en la blacklist
- Si está en blacklist, mueve automáticamente a papelera (`status = 'trash'`)

---

# 3. Dashboard de Analítica (Pestaña "Dashboard")

El Dashboard muestra:

- **KPIs independientes del rango de fecha** (7 días, 30 días, 365 días)
- Total de leads en vista actual
- Promedio diario calculado
- Distribución por origen (gráfica circular)
- Distribución por formulario (gráfica circular)
- Distribución por estado CRM (gráfica circular)
- Estado de lectura (leídos/no leídos) (gráfica circular)
- Timeline por origen (gráfica de líneas)
- Historial mensual (12 meses con scroll horizontal)
- SPAM detectado / seguro (gráfica circular)
- **Botón para generar reportes** (abre modal con vista previa)

**Importante:**  
Esta pestaña **solo visualiza datos**.  
Las acciones se realizan en la pestaña "Correos".

---

# 4. Gestión de Correos (Pestaña "Correos") - FUNCIONAMIENTO COMPLETO

## A. Vista principal

### Sidebar de navegación (izquierda)
Categorías estáticas:
- **Todos** - Todos los correos activos (`status != 'trash'`)
- **Destacados** - Emails con palabras clave destacadas (`highlight = true`)
- **Spam** - Correos marcados como spam (`risk = 2`)
- **Papelera** - Correos eliminados (`status = 'trash'`)

Categorías dinámicas:
- **Estados CRM** - Generados desde configuración (`efa_crm_labels`)
  - Cada estado muestra: dot de color, nombre, contador
  - Al hacer clic, filtra por ese estado específico
- **Formularios** - Lista dinámica de formularios con contadores
  - Al hacer clic, filtra por ese formulario específico

**Funcionamiento del sidebar:**
- Los contadores se calculan desde **TODA la base de datos** (sin filtros de fecha)
- Se actualizan automáticamente al cambiar estados, eliminar, etc.
- Badge especial para "destacados sin leer" en el header
- Navegación con clases `.efa-nav-item` y `.active`

### Tabla unificada de leads
- **Scroll horizontal** para columnas extensas
- **Columnas redimensionables** con drag en headers (guardado en localStorage)
- **Paginación progresiva** con botón "Cargar más" (batch size configurable)
- **14 columnas** configurables (visibilidad guardada por usuario)

### Barra de Herramientas Unificada
**Izquierda (filtros):**
- **Multiselector de Formularios** - Dropdown con checkboxes, "Seleccionar todos"
- **Multiselector de Orígenes** - Dropdown con checkboxes, "Seleccionar todos"
- **Selector de Rango de Fechas** - Últimos 7 días, mes, todo, personalizado
- **Toggle Comercial** - Mostrar solo correos con aceptación comercial
- **Botón "Buscar"** - Aplica todos los filtros

**Derecha (acciones):**
- **Configurar Columnas** - Dropdown para mostrar/ocultar columnas
- **Descargar CSV** - Exporta datos filtrados con separador configurable
- **Borrar Spam** - Eliminación masiva (solo visible cuando hay spam)

**Header:**
- **Buscador en tiempo real** - Filtra por nombre, email, ID, formulario, origen, estado CRM
- **Badge de destacados sin leer** - Contador animado de emails destacados no leídos

---

## B. Campos mostrados en tabla

- **Fecha** (formato: "DD MMM YY (HH:mmh) - Hace X días")
- **Formulario** (oculto por defecto)
- **Origen** (con dot de color semántico, normalizado)
- **Nombre**
- **Email** (estilo monospace, color muted)
- **Teléfono**
- **Landing** (oculto por defecto, tooltip con URL completa)
- **Previa** (oculto por defecto, tooltip con URL completa)
- **Lead Page** (oculto por defecto, tooltip con URL completa)
- **Estado CRM** (badge personalizable con contador de notas)
- **Comercial** (dot verde/rojo, toggle visual)
- **ID** (oculto por defecto)
- **Spam/Seguro** (icono shield/warning, clickeable)
- **Acción** (botón eliminar)

### Estilos especiales de filas
- **`.efa-unread`** - Fondo blanco, texto en negrita (no leído)
- **`.efa-highlight-row`** - Borde izquierdo rosa (#e01d75) para destacados
- **`.efa-commercial-row`** - Fondo verde claro para comerciales leídos
- **Hover** - Efecto de elevación y sombra

---

## C. Click en un correo - Panel de Detalles

### Flujo completo:
1. **Click en fila** → Llama a `openModal(index)`
2. **Marcado automático como leído** → `ajax_mark_as_read()` si `read_status != '1'`
3. **Apertura del panel** → Panel deslizante desde la derecha (520px de ancho)
4. **AJAX para detalles** → `efa_get_submission_details` con `id`
5. **Renderizado de contenido** → `renderDetails(data, rowData)`

### Datos obtenidos del backend:
- **Meta del submission** (id, form_name, created_at, status)
- **Todos los valores** del formulario (key-value pairs)
- **URLs procesadas** (landing, prev, lead con títulos bonitos)
- **Datos CRM** (estado actual, array de notas)

### Contenido del panel:
- **Header con navegación:**
  - Botón "Gestionar" (abre panel CRM secundario)
  - Botones Anterior/Siguiente
  - Contador "X / Y"
  - Botón cerrar (×)
- **Fecha destacada** (bloque con fondo oscuro)
- **Grid de datos** (2 columnas):
  - Nombre, Email, Teléfono
  - Formulario, Origen
  - Landing, Previa, Lead Page (con iconos)
  - Todos los campos del formulario
- **Ruta del usuario** (Landing → Previa → Lead con flechas)
- **Botones de acción** (fijos en la parte inferior):
  - **Llamar** (tel: link)
  - **WhatsApp** (wa.me link, nueva ventana)
  - **Email** (mailto: link)

### Navegación prev/next:
- Botones deshabilitados en extremos
- Actualiza `currentIndex` y `updateNavState()`
- Al cambiar, abre el nuevo correo automáticamente
- Mantiene el panel CRM abierto si estaba abierto

---

## D. Botón "Gestionar" - Panel CRM Secundario

### Funcionamiento:
1. **Click en "Gestionar"** → Abre panel CRM secundario
2. **Panel desliza desde la derecha** → Se posiciona a 520px del borde (encima del panel principal)
3. **Renderizado del contenido** → `renderCrmPanel(data, rowData)`

### Contenido del panel CRM:

#### 1. Estado del Lead
- **Pills dinámicos** generados desde `efa_vars.crm_labels`
- Cada pill tiene:
  - Color personalizado (`--pill-color`)
  - Nombre del estado
  - Clase `.active` si es el estado actual
- **Click en pill** → Actualiza estado vía AJAX (`efa_update_crm_status`)
- **Actualización optimista** → UI se actualiza inmediatamente
- **Fallback para estados borrados** → Si el estado no existe, asigna el primero disponible

#### 2. Historial de Notas
- **Lista de notas** ordenadas cronológicamente (más reciente primero)
- Cada nota muestra:
  - **Avatar** con iniciales del autor (gradiente rosa)
  - **Autor** (nombre del usuario de WordPress)
  - **Fecha** (formato relativo: "Hace X días")
  - **Texto** (con saltos de línea preservados)
- **Estado vacío** → Mensaje con icono si no hay notas
- **Scroll automático** → Al añadir nota, scroll al final

#### 3. Input para añadir nota
- **Textarea** con placeholder
- **Botón de envío** (gradiente rosa, animación al hover)
- **Atajos de teclado:**
  - `Enter` → Envía nota
  - `Shift+Enter` → Salto de línea
- **Validación** → No permite notas vacías
- **Feedback visual** → Botón muestra spinner mientras envía

### Actualizaciones en tiempo real:
- **Badge en botón "Gestionar"** → Muestra contador de notas
- **Badge en tabla** → Actualiza contador en badge de estado CRM
- **Arrays de datos** → Sincroniza `allFetchedRows` y `currentRows`
- **Visual de tabla** → `refreshTableRowCrm()` actualiza badge

---

## E. Sistema de búsqueda y filtros

### Búsqueda global
- **Input en tiempo real** → Filtra mientras escribes
- **Búsqueda en:** nombre, email, teléfono, ID, formulario, origen, estado CRM
- **Case-insensitive** → No distingue mayúsculas/minúsculas
- **Filtrado local** → No requiere AJAX, filtra `allFetchedRows`
- **Combinable con otros filtros** → Se aplica junto con destacados, etc.

### Filtros por origen
- **Multiselector** con dropdown
- **Checkboxes** para cada origen
- **"Seleccionar todos"** → Toggle para marcar/desmarcar todo
- **Texto dinámico** → "Orígenes: Todos" / "Orígenes: X" / "Orígenes: Ninguno"
- **Normalización** → Los orígenes se normalizan antes de comparar

### Filtros por formulario
- **Multiselector** con dropdown
- **Checkboxes** para cada formulario
- **"Seleccionar todos"** → Toggle para marcar/desmarcar todo
- **Texto dinámico** → "Formularios: Todos" / "Formularios: X" / "Formularios: Ninguno"

### Toggle de destacados
- **Switch** en barra de herramientas
- **Filtra** → Solo muestra emails con `highlight = true`
- **Combinable** → Funciona junto con búsqueda y otros filtros

### Lógica de filtros acumulativos
- Todos los filtros se combinan con lógica **AND**
- Búsqueda + Destacados + Origen + Formulario + Fecha = Resultado final
- Los filtros se envían al backend en `fetchData()`

---

## F. Acciones disponibles en tabla

### Marcar como SPAM / Restaurar como seguro
- **Click en badge de spam** → Muestra SweetAlert2 con opciones:
  1. **Solo marcar como spam** → Cambia `efa_spam_override = '1'`
  2. **Spam + Bloquear remitente** → Marca como spam + añade a blacklist
  3. **Eliminar todo del remitente** → Borra todos los emails + blacklist
  4. **Cancelar**
- **Click en badge seguro** → Muestra opciones similares para restaurar
- **Actualización optimista** → UI se actualiza inmediatamente
- **Sincronización** → Actualiza arrays y visual de tabla

### Eliminar lead individual
- **Botón eliminar** en columna "Acción"
- **Confirmación** → Dialog de confirmación nativo
- **AJAX** → `efa_delete_submission`
- **Lógica:**
  - Si `status = 'trash'` → Eliminación permanente (DELETE)
  - Si `status != 'trash'` → Mueve a papelera (`status = 'trash'`)

### Eliminar SPAM masivo
- **Botón "Borrar Spam"** → Solo visible cuando hay spam
- **Confirmación** → Dialog con advertencia
- **Procesamiento:**
  - Aplica filtros actuales (fecha, origen, formulario)
  - Detecta spam con heurísticas
  - Mueve a papelera todos los detectados
  - Retorna contador de eliminados

### Añadir a blacklist
- **Acción desde SweetAlert2** → Al marcar como spam
- **AJAX** → `efa_blacklist_email`
- **Almacenamiento** → `efa_spam_emails` (opción de WordPress, lista separada por comas)
- **Efecto inmediato** → Futuros envíos de ese email van directo a papelera

### Eliminar todos los emails de un remitente
- **Acción desde SweetAlert2** → Opción avanzada
- **AJAX** → `efa_delete_all_from_sender`
- **Procesamiento:**
  1. Busca todos los submissions con ese email
  2. Marca todos como spam override
  3. Mueve todos a papelera
  4. Añade email a blacklist
- **Retorna** → Contador de emails eliminados
- **Recarga página** → Para reflejar cambios

---

## G. Exportación CSV/Excel

### Funcionamiento:
- **Botón "Descargar CSV"** → En header de la página
- **Generación dinámica** → Usa datos filtrados actuales (`currentRows`)
- **Separador configurable** → Coma o punto y coma (Settings > Marketing)
- **Columnas exportadas:**
  - Fecha, Formulario, Origen, Nombre, Email, Teléfono
  - Landing, Previa, Lead Page
  - Estado CRM, Comercial, Spam, ID
- **Formato de fecha** → Mantiene formato original
- **Encoding** → UTF-8 con BOM para Excel

---

# 5. Detección y gestión de SPAM

## A. Sistema de detección automática

### Heurísticas implementadas:
1. **Nombre sospechoso:**
   - Contiene números o guiones bajos
   - Contiene URLs (http, www)
   - Más de 5 palabras
2. **Texto con caracteres especiales:**
   - Caracteres cirílicos (ruso)
   - Caracteres chinos/japoneses
3. **Palabras clave de spam** (configurables):
   - Por defecto: "crypto, investment, wallet, forex, casino, bitcoin"
   - Configurables en Settings > SPAM
4. **TLDs bloqueados:**
   - Extensiones de dominio (.ru, .cn, .pl, etc.)
   - Configurables en Settings > SPAM
5. **Lista negra permanente:**
   - Emails específicos bloqueados
   - Configurables en Settings > SPAM

### Prioridades de detección:
1. **Whitelist (Inmunidad)** → Siempre seguro, prioridad máxima
2. **Hard Ban (Blacklist/TLDs)** → Siempre spam, prioridad alta
3. **Override manual** → Decisión del usuario, prioridad media
4. **Detección automática** → Heurísticas, prioridad baja

### Almacenamiento:
- **`efa_spam_override`** → '0' = seguro, '1' = spam (override manual)
- **`risk`** → Calculado en tiempo real, no se guarda (0 = seguro, 2 = spam)
- **`status = 'trash'`** → Correos eliminados/movidos a papelera

---

## B. Sistema de Whitelist (Inmunidad)
- **Prioridad suprema** → Emails/dominios en whitelist nunca son spam
- **Soporta:**
  - Emails completos: `cliente@empresa.com`
  - Dominios: `@empresa.com` (todos los emails de ese dominio)
- **Configuración** → Settings > SPAM > Lista Blanca
- **Almacenamiento** → `efa_whitelist_emails` (opción de WordPress)

---

## C. Sistema de Blacklist (Bloqueo permanente)
- **Lista negra de emails** → Configurables en Settings > SPAM
- **Efecto inmediato** → Futuros envíos van directo a papelera
- **Interceptación** → Hook `elementor_pro/forms/new_record` verifica blacklist
- **Almacenamiento** → `efa_spam_emails` (opción de WordPress, lista separada por comas)

---

## D. Limpieza automática de spam
- **Cron job diario** → `gw_daily_spam_cleanup`
- **Configuración** → Settings > SPAM > Auto-limpieza (días)
- **Funcionamiento:**
  - Si está activado (días > 0)
  - Busca correos en papelera (`status = 'trash'`) más antiguos que X días
  - Elimina permanentemente (DELETE de BD)
- **Límite de seguridad** → Máximo 1000 correos por ejecución

---

# 6. Sistema CRM Completo

## A. Estados CRM personalizables

### Configuración:
- **Ubicación** → Settings > NOTAS > Gestión de Estados CRM
- **CRUD completo:**
  - Crear nuevo estado
  - Editar nombre y color
  - Eliminar estado (excepto "Nuevo" que está protegido)
  - Reordenar (drag & drop)

### Estructura de estado:
```php
[
    'id' => 'new',           // ID único (inglés, sin espacios)
    'label' => 'Nuevo',      // Nombre visible (español)
    'color' => '#2271b1'     // Color hexadecimal
]
```

### Estados por defecto:
- **Nuevo** (#2271b1 - Azul)
- **Contactado** (#f59e0b - Naranja)
- **Presupuesto** (#9333ea - Morado)
- **Ganado** (#16a34a - Verde)
- **Perdido** (#dc2626 - Rojo)

### Almacenamiento:
- **Opción de WordPress** → `efa_crm_labels` (array JSON)
- **En metadata** → `efa_crm_status` (valor = ID del estado, ej: "new")

### Protecciones:
- **Estado "Nuevo"** → No se puede eliminar
- **Validación** → Al menos un estado debe existir
- **Fallback** → Si un lead tiene estado borrado, se asigna automáticamente el primero disponible

---

## B. Sistema de Notas

### Estructura de nota:
```php
[
    'text' => 'Texto de la nota',
    'author' => 'Nombre del usuario',
    'date' => '2025-01-15 10:30:00',  // Formato MySQL
    'ts' => 1705315800                 // Timestamp Unix
]
```

### Funcionalidades:
- **Notas ilimitadas** por lead
- **Autor automático** → Usuario actual de WordPress (`wp_get_current_user()`)
- **Timestamp automático** → Fecha y hora actuales
- **Formato relativo** → "Hace X días" en el frontend
- **Orden cronológico** → Más reciente primero

### Almacenamiento:
- **Opción de WordPress** → No (solo en metadata)
- **En metadata** → `efa_crm_notes` (JSON array de notas)

### Visualización:
- **Badge en tabla** → Contador de notas en badge de estado CRM
- **Badge en panel** → Contador en botón "Gestionar"
- **Lista en panel CRM** → Todas las notas con avatar, autor, fecha, texto

---

## C. Flujo CRM completo

1. **Click en badge de CRM en tabla** → Abre modal con panel CRM activo
2. **Click en "Gestionar"** → Abre panel CRM secundario (si no está abierto)
3. **Cambiar estado:**
   - Click en pill de estado
   - Actualización optimista en UI
   - AJAX `efa_update_crm_status`
   - Actualiza badge en tabla
4. **Añadir nota:**
   - Escribir en textarea
   - Enter o click en botón
   - AJAX `efa_add_crm_note`
   - Nota aparece inmediatamente en lista
   - Contador se actualiza en badge
5. **Cerrar panel** → Click en × o fuera del panel

---

# 7. Sistema de Lectura/No Lectura

## A. Read Status

### Almacenamiento:
- **Campo en metadata** → `efa_read_status`
- **Valores:** '0' = no leído, '1' = leído
- **Inicialización** → Todos los correos nuevos tienen `read_status = '0'`

### Marcado automático:
- **Al abrir modal** → Se marca automáticamente como leído
- **AJAX** → `efa_mark_as_read` (se ejecuta en background)
- **Actualización optimista** → UI se actualiza inmediatamente
- **Sincronización** → Actualiza `currentRows` y `allFetchedRows`

### Visual:
- **No leído** → Fondo blanco, texto en negrita, clase `.efa-unread`
- **Leído** → Fondo gris claro (#e8eaed), texto normal, indicador sutil en primera columna

---

## B. Highlighted Keywords (Palabras Destacadas)

### Configuración:
- **Ubicación** → Settings > MARKETING > Palabras Destacadas
- **Por defecto:** "presupuesto, cita, agendar, llamar"
- **Formato:** Lista separada por comas

### Detección:
- **Búsqueda case-insensitive** → No distingue mayúsculas/minúsculas
- **Campos buscados:** Todo el texto del formulario + nombre + email
- **Marcado:** `highlight = true` si encuentra alguna palabra clave

### Visual:
- **Fila destacada** → Borde izquierdo rosa (#e01d75), clase `.efa-highlight-row`
- **Sidebar "Destacados"** → Contador de emails destacados
- **Badge especial** → "X destacados sin leer" en header
- **Toggle filter** → Mostrar solo destacados

### Contador de destacados sin leer:
- **Cálculo:** Emails con `highlight = true` AND `read_status != '1'`
- **Actualización:** Se calcula desde toda la BD en cada `fetchData()`
- **Visual:** Badge animado con pulso en header

---

# 8. Settings (Pestaña "Ajustes")

Pestaña completa con **5 subsecciones**:

## A. SPAM
- **Palabras Prohibidas** → Keywords que marcan spam automáticamente
- **Lista Negra de Emails** → Emails bloqueados permanentemente
- **Extensiones Bloqueadas (TLDs)** → Dominios por país (.ru, .cn, etc.)
- **Lista Blanca (Inmunidad)** → Emails/dominios siempre seguros
- **Auto-limpieza de Spam** → Borrado automático después de X días (0 = desactivado)

## B. NOTAS
- **Gestión de Estados CRM:**
  - CRUD completo de estados
  - Selector de color visual (color picker)
  - Vista previa en tiempo real
  - Drag & drop para reordenar
  - Protección del estado "Nuevo" (no se puede eliminar)
  - Validación: al menos un estado debe existir

## C. MARKETING
- **Palabras Destacadas** → Keywords para highlight de emails importantes
- **Separador Excel (CSV)** → Coma (,) o punto y coma (;)
- **Correos por lote** → Tamaño de batch inicial (10-500, recomendado 100)

## D. INFORMES
- **Configuración de Reportes PDF:**
  - Toggle individual para cada bloque:
    - KPIs Generales
    - Evolución de Leads (Timeline)
    - Distribución por Formularios
    - Distribución por Orígenes
    - Estados CRM
    - Correos Leídos/No Leídos
    - Detección de Spam
  - **Orden personalizable** (drag & drop)
  - **Logo de empresa** (mostrar/ocultar)

## E. OTROS
- **Acceso al Plugin** → Rol mínimo requerido (por defecto: `manage_options`)
- **Interfaz Limpia** → Ocultar pestaña Tutorial

---

# 9. Informes (Pestaña "Informes")

## A. Vista de configuración

### Columna izquierda: Filtros
- **Rango de fechas** → Selector de fecha inicio/fin
- **Filtro de orígenes** → Multiselector con checkboxes
- **Filtro de formularios** → Multiselector con checkboxes
- **Filtros guardados** → Se recuerdan entre sesiones

### Columna derecha: Configuración
- **Configuración de bloques** → Misma que Settings > INFORMES
- **Botón "Generar Informe PDF"** → Genera y descarga PDF

## B. Generación de PDF

### Proceso:
1. **Click en "Generar Informe PDF"** → Abre modal con vista previa
2. **Modal con gráficas** → Chart.js renderiza gráficas en tiempo real
3. **Botón "Imprimir / PDF"** → Usa `window.print()` del navegador
4. **Estilos de impresión** → CSS especial para PDF

### Contenido del informe:
- **Portada** → Logo, título, rango de fechas, total de correos
- **KPIs Generales** → Filtro activo, fuentes activas, promedio diario
- **Evolución de Leads** → Gráfica de barras vertical + gráfica de líneas por origen
- **Distribución por Formularios** → Cards con porcentajes
- **Distribución por Orígenes** → Cards con porcentajes
- **Estados CRM** → Gráfica circular
- **Correos Leídos/No Leídos** → Gráfica circular
- **Detección de Spam** → Cards con estadísticas
- **Tabla de datos** → Primeros 20 correos (si está habilitado)

### Orden de bloques:
- Configurable en Settings > INFORMES
- Drag & drop para reordenar
- Solo se muestran bloques habilitados

---

# 10. Configuración de Correo (Pestaña "Email Settings")

## A. Detección de SMTP
- **Detección automática** → Verifica plugins SMTP comunes:
  - WP Mail SMTP
  - Easy WP SMTP
  - Post SMTP
  - FluentSMTP
  - Mailgun
  - SendGrid
  - Y otros...
- **Indicador visual** → Muestra estado (instalado/no instalado)

## B. Firma de Correos
- **Editor WYSIWYG** → TinyMCE con toolbar básico
- **HTML permitido** → Formato básico (negrita, cursiva, enlaces, listas)
- **Almacenamiento** → `efa_email_signature` (opción de WordPress)
- **Uso futuro** → Se añadirá automáticamente a correos enviados desde el plugin

## C. Envío de Prueba
- **Input de email** → Para enviar correo de prueba
- **AJAX** → `gw_send_test_email`
- **Contenido** → Mensaje de prueba + firma (si está configurada)
- **Feedback** → Notificación de éxito/error

---

# 11. Licencias y periodo de prueba (trial)

## A. Trial (automático)
- **Duración:** 30 días desde instalación
- **Sin necesidad de clave** → Se activa automáticamente
- **Fecha almacenada** → `gw_first_install_date` (opción de WordPress)
- **Cálculo** → `GW_TRIAL_DAYS` (constante = 30)
- **No reiniciable** → Una vez expirado, requiere licencia

## B. Licencia
- **Validación remota** → API: `GW_API_URL` (constante)
- **Cache** → Transients de 24 horas
- **Almacenamiento** → Opciones de WordPress

## C. Cuando la licencia no es válida y el trial expiró:
- **Tracking deshabilitado** → No se carga `frontend-tracker.js`
- **Dashboard bloqueado** → Solo muestra mensaje de activación
- **Acceso limitado** → Solo pestaña de licencia disponible

---

# 12. Sistema Terminator (limpieza del admin)

## A. Implementación
- **CSS** → `/assets/css/admin-terminator.css`
- **JavaScript** → `/assets/js/admin-terminator.js`

## B. Elementos eliminados:
- Notices del admin de WordPress
- Avisos de Elementor
- Elementor Admin Top Bar
- Banners de actualización
- Cualquier elemento que afecte la UX del plugin

## C. Funcionamiento
- **CSS** → Oculta visualmente elementos molestos
- **JS (MutationObserver)** → Elimina del DOM elementos que aparezcan dinámicamente
- **Siempre activo** → Se ejecuta en todas las páginas del plugin

---

# 13. Estructura de archivos

## Raíz
- `gw-elementor-analytics.php` → Archivo principal, define constantes, carga clases
- `AI_README.md` → Este documento
- `sync_config.jsonc` → Configuración de sincronización
- `index.php` → Seguridad (previene acceso directo)

---

## `/assets/css/`

### `admin-main.css`
- Estilos generales del admin del plugin
- Tabs, tutorial, cards, tablas
- Estilos para Settings tabs
- Estilos para Email Settings

### `admin-dashboard.css`
- Estilos del Dashboard y de la pestaña Correos
- **Light/Dark Mode** completo
- Tabla con scroll horizontal
- Modal lateral + Panel CRM secundario
- Sidebar navigation
- Highlighted rows
- Unread rows
- Resizable columns
- Load more button
- Tooltips
- Badges y estados

### `admin-terminator.css`
- Oculta notices, banners, barras, alertas

---

## `/assets/js/`

### `admin-dashboard.js` (2795+ líneas)
**Controla todas las pestañas:**
- **Dashboard:** KPIs + gráficas + generador de reportes PDF
- **Correos:** Tabla + modal + acciones + CRM panel + sidebar navigation
- **Settings:** Gestión de estados CRM (CRUD visual)
- **Informes:** Configuración y generación de reportes

**Funcionalidades principales:**
- Peticiones AJAX (15+ endpoints)
- Exportación CSV con separador configurable
- Dark Mode (sincronizado HTML + BODY)
- Gestión de columnas visibles con toggle
- Resizable columns con drag (localStorage)
- Navegación en modal lateral (prev/next)
- Sistema de notas CRM completo
- Read status automático
- Highlighted keywords detection
- Global search con debounce
- Origin/Form dropdowns multi-select
- Load more para paginación progresiva
- Sidebar filters dinámicos
- SweetAlert2 para gestión avanzada de spam
- Tooltips en headers
- Renderizado de detalles en modal
- Acciones rápidas (llamar, WhatsApp, Email)
- Cálculo de contadores del sidebar
- Normalización de orígenes

### `admin-terminator.js`
- MutationObserver para eliminar avisos dinámicos

### `frontend-tracker.js`
- Lógica de tracking en frontend
- Inyección de campos ocultos en Elementor Forms
- Gestión de sessionStorage
- Encadenamiento de UTMs

---

## `/includes/`

### `class-gw-main.php`
**Orquestador general:**
- Instancia todas las clases principales
- Controla el enrutado (Dashboard / Correos / Settings / Informes / Email Settings)
- Encola CSS/JS del admin
- Registra menús de WordPress
- Maneja modo full width
- Pre-cargador de Dark Mode (anti-flash)
- Interceptación de spam en tiempo real
- Cron job para limpieza diaria

### `class-gw-license.php`
- Lógica de licencias
- Trial + validación remota
- `render_trial_notice()` → Muestra aviso de trial

### `class-gw-dashboard.php` (1670+ líneas)
**Responsabilidades:**
- KPIs con estados CRM y read status
- Timeline por origen
- Distribuciones (formularios, orígenes, CRM, lectura, spam)
- Historial mensual (12 meses)
- Detección de SPAM (heurísticas completas)
- Blacklist/Whitelist management
- Sistema CRM (update status, add notes)
- Read status tracking
- Highlighted keywords detection
- Global search implementation
- Normalización de nombres de orígenes
- Cálculo de contadores del sidebar (desde toda la BD)
- Generador de reportes PDF (datos)

**Endpoints AJAX:**
- `efa_get_data` → Datos completos con filtros avanzados
- `efa_get_submission_details` → Detalles de un lead
- `efa_toggle_spam_status` → Cambiar spam/correcto
- `efa_delete_bulk_spam` → Borrado masivo
- `efa_delete_submission` → Borrado individual
- `efa_mark_as_read` → Marcar como leído
- `efa_update_crm_status` → Actualizar estado CRM
- `efa_add_crm_note` → Añadir nota
- `efa_blacklist_email` → Añadir a blacklist
- `efa_delete_all_from_sender` → Eliminar todos + blacklist

### `class-gw-emails.php`
**Responsabilidades:**
- Renderizado de tabla de correos
- Sidebar navigation con categorías dinámicas
- Barra de Herramientas Unificada
- Origin/Form dropdowns con checkboxes
- Modal lateral con panel CRM integrado
- Trial notice rendering

### `class-gw-settings.php`
**Responsabilidades:**
- Renderizado de la pestaña Settings
- 5 subsecciones: SPAM, NOTAS, MARKETING, INFORMES, OTROS
- Guardado de configuración por sección
- Gestión visual de estados CRM con color picker
- Drag & drop ordering para bloques de reporte
- Validación y sanitización de inputs

### `class-gw-reports.php`
**Responsabilidades:**
- Renderizado de la pestaña Informes
- Configuración de filtros (fecha, origen, formulario)
- Generación de datos para reportes
- Endpoint AJAX para generar reportes

### `class-gw-email-settings.php`
**Responsabilidades:**
- Renderizado de la pestaña Email Settings
- Detección de plugins SMTP
- Configuración de firma de correos
- Envío de correo de prueba
- 2 subsecciones: AJUSTES DE CORREO, ENVÍO DE PRUEBA

### `class-gw-tracker.php`
- Encola el JS de tracking en frontend
- Solo activo si:
  - Trial válido, o
  - Licencia activa

---

# 14. Flujo de Datos (end-to-end)

1. **El usuario llega**
   - Se detectan UTMs en URL
   - Se guarda información en `sessionStorage`
   - Se normaliza el nombre del origen (elimina dominios, capitaliza)

2. **El usuario navega**
   - Se actualiza el historial en `sessionStorage`
   - Se encadenan UTMs a enlaces internos

3. **El usuario llega al formulario**
   - `frontend-tracker.js` detecta campos ocultos
   - Se rellenan automáticamente: `origen`, `landing_url`, `prev_url`, `current_url`

4. **El usuario envía el formulario**
   - Elementor guarda en:
     - `wp_e_submissions` (meta: id, form_name, created_at, status)
     - `wp_e_submissions_values` (key-value pairs)
   - **Hook de interceptación** → `elementor_pro/forms/new_record`
     - Verifica blacklist
     - Si está en blacklist → Mueve a papelera inmediatamente
   - Sistema anti-spam analiza el contenido:
     - Verifica whitelist (inmunidad)
     - Verifica blacklist/TLDs (bloqueo)
     - Detecta highlighted keywords
     - Calcula `risk` (0=seguro, 2=spam)
     - Marca `highlight` (true/false)
   - Inicializa:
     - `read_status` = '0' (no leído)
     - `efa_crm_status` = 'new' (estado por defecto)

5. **El admin accede a GW Analytics**
   - **Dashboard:** Visualización + reportes
   - **Correos:** Gestión + CRM
   - **Settings:** Configuración completa
   - **Informes:** Generación de PDFs
   - **Email Settings:** Configuración SMTP

6. **Gestión de leads**
   - Admin abre modal → Marca como leído automáticamente
   - Admin cambia estado CRM → Actualiza badge en tabla
   - Admin añade notas → Aparece en panel CRM, actualiza contador
   - Admin puede blacklistear → Futuros envíos van a papelera
   - Admin puede eliminar todos los emails del remitente → Borra + blacklist

---

# 15. Campos clave manejados

## Frontend (para Elementor Forms)
- `origen` → Origen del tráfico (normalizado)
- `landing_url` → Primera URL visitada
- `prev_url` → URL previa al formulario
- `current_url` → URL donde se envía el formulario
- `comercial` → Checkbox RGPD (si existe)

## Backend (base de datos - metadatos en wp_e_submissions_values)
- `id` → ID del submission
- `date` → Fecha formateada
- `form` → Nombre del formulario
- `origin` → Origen normalizado
- `name` → Nombre del lead
- `email` → Email del lead
- `phone` → Teléfono del lead
- `landing` → URL de landing (título bonito)
- `prev` → URL previa (título bonito)
- `lead_page` → URL de lead (título bonito)
- `risk` → 0 = seguro, 2 = spam (calculado, no guardado)
- `is_commercial` → 0/1 (aceptación comercial)
- `efa_crm_status` → ID del estado CRM (ej: 'new', 'contacted')
- `efa_crm_notes` → JSON array de notas
- `efa_read_status` → '0' = no leído, '1' = leído
- `efa_spam_override` → '0' = seguro, '1' = spam (override manual)
- `highlight` → true/false (palabras clave destacadas)

## Opciones de WordPress (wp_options)
- `efa_spam_keywords` → Palabras clave de spam
- `efa_spam_tlds` → TLDs bloqueados
- `efa_spam_emails` → Blacklist (lista separada por comas)
- `efa_whitelist_emails` → Whitelist (lista separada por comas)
- `efa_highlight_keywords` → Palabras destacadas (lista separada por comas)
- `efa_crm_labels` → Array JSON de estados CRM
- `efa_csv_separator` → Separador CSV (',' o ';')
- `efa_batch_size` → Tamaño de batch (10-500)
- `efa_report_show_*` → Configuración de reportes (kpis, timeline, forms, etc.)
- `efa_report_blocks_order` → Orden de bloques (lista separada por comas)
- `efa_email_signature` → Firma de correos (HTML)
- `efa_hide_tutorial` → Ocultar tutorial ('0' o '1')
- `efa_access_role` → Rol mínimo requerido
- `efa_autodelete_spam_days` → Días para auto-limpieza (0 = desactivado)
- `gw_first_install_date` → Timestamp de instalación (para trial)

---

# 16. Endpoints AJAX

## Dashboard & Data
- **`efa_get_data`** → Devuelve KPIs, timeline, distribuciones, tabla
  - Filtros: `status_filter`, `risk_filter`, `highlight_filter`, `search`, `origins[]`, `form_name[]`, `range_type`, `start_date`, `end_date`, `commercial_filter`
  - Retorna: `kpis`, `timeline`, `forms_distribution`, `origins_distribution`, `crm_distribution`, `read_distribution`, `spam_distribution`, `table_rows`, `sidebar_counts`, `highlighted_unread`

- **`efa_get_submission_details`** → Datos completos del lead para el modal
  - Parámetros: `id`
  - Retorna: `meta`, `values`, `path_info` (landing, prev, lead con títulos), `crm` (status, notes)

## Spam Management
- **`efa_toggle_spam_status`** → Cambiar estado a spam/correcto
  - Parámetros: `id`, `status` ('spam' o 'safe')
  - Almacena: `efa_spam_override` = '1' o '0'

- **`efa_delete_bulk_spam`** → Borrado masivo de leads spam
  - Parámetros: `range_type`, `start_date`, `end_date`, `origins[]`, `form_name[]`
  - Procesa: Detecta spam con heurísticas, mueve a papelera
  - Retorna: `deleted` (contador)

- **`efa_delete_submission`** → Borrado individual
  - Parámetros: `id`
  - Lógica: Si `status = 'trash'` → DELETE permanente, si no → Mueve a papelera

## CRM System
- **`efa_update_crm_status`** → Actualizar estado CRM de un lead
  - Parámetros: `id`, `status` (ID del estado, ej: 'new')
  - Almacena: `efa_crm_status` = status

- **`efa_add_crm_note`** → Añadir nota a un lead
  - Parámetros: `id`, `note` (texto)
  - Procesa: Añade nota al array JSON, guarda autor y timestamp
  - Retorna: `note` (objeto completo), `notes_count` (contador total)

## Blacklist System
- **`efa_blacklist_email`** → Añadir email a lista negra permanente
  - Parámetros: `email`
  - Almacena: Añade a `efa_spam_emails` (opción de WordPress)
  - Retorna: `message`, `already_exists`

- **`efa_delete_all_from_sender`** → Eliminar todos los emails de un remitente + blacklist
  - Parámetros: `email`
  - Procesa: Busca todos los submissions, marca como spam, mueve a papelera, añade a blacklist
  - Retorna: `count` (emails eliminados), `message`

## Read Status
- **`efa_mark_as_read`** → Marcar email como leído
  - Parámetros: `id`
  - Almacena: `efa_read_status` = '1'

## Reports
- **`gw_generate_report_data`** → Generar datos para reporte PDF
  - Parámetros: `start_date`, `end_date`, `origins[]`, `forms[]`
  - Retorna: Datos filtrados para generación de PDF

## Email Settings
- **`gw_send_test_email`** → Enviar correo de prueba
  - Parámetros: `test_email`
  - Procesa: Envía correo con `wp_mail()` incluyendo firma si está configurada
  - Retorna: `message` (éxito/error)

---

# 17. Dependencias importantes

## Backend
- **Elementor Pro** (obligatorio)
  - Necesita `wp_e_submissions` y `wp_e_submissions_values`
  - Requiere Submissions activas
- **PHP ≥ 7.4**
- **WordPress ≥ 5.8**
- **TCPDF** (incluido en `/vendor/tcpdf/`) → Para generación de PDFs

## Frontend
- **jQuery** (WordPress lo incluye)
- **Chart.js 4.4.0** (CDN)
- **ChartDataLabels 2.2.0** (plugin de Chart.js, CDN)
- **SweetAlert2 11.0.0** (CDN) → Para diálogos avanzados de spam
- **jQuery UI Sortable** (WordPress lo incluye) → Para drag & drop

## Navegador
- `sessionStorage` → Para tracking de ruta
- `localStorage` → Para preferencias (tema, columnas, full width)
- `MutationObserver` → Para Terminator
- APIs modernas de JS (fetch, async/await, arrow functions)

---

# 18. Consideraciones para LLMs

## Separación de responsabilidades
- **NO mezclar** Dashboard con Correos con Settings: roles distintos
- **Dashboard** = Visualización + reportes (solo lectura)
- **Correos** = Gestión + acciones en leads (escritura)
- **Settings** = Configuración global (escritura)
- **Informes** = Generación de PDFs (lectura + generación)
- **Email Settings** = Configuración SMTP (escritura)

## Sistema CRM
- Los estados CRM son **dinámicos** y configurables por el usuario
- **Siempre cargar** estados desde `efa_vars.crm_labels` (no hardcodear)
- El estado 'new' es **protegido** y no se puede eliminar
- Las notas se guardan como **JSON array** en metadata
- **Fallback automático** → Si un lead tiene estado borrado, se asigna el primero disponible

## Blacklist vs Spam
- **Spam** (risk=2): Detección heurística, **reversible** desde UI
- **Blacklist**: Bloqueo permanente, **irreversible** desde UI (requiere Settings)
- **Whitelist tiene prioridad** sobre todas las reglas (incluso blacklist)

## Read Status
- Se marca automáticamente al abrir modal
- Actualización **optimista** en UI (no esperar AJAX)
- Sincronizar entre `currentRows` y `allFetchedRows`
- Visual: `.efa-unread` para no leídos

## Highlighted Keywords
- Detección case-insensitive
- Buscar en **todos los campos** del formulario (full_text)
- Marcar con flag `highlight = true` en backend
- Contador de destacados sin leer en sidebar
- Visual: `.efa-highlight-row` con borde rosa

## Normalización de orígenes
- **Función:** `GW_Analytics_Dashboard::normalize_origin_name()`
- **Proceso:**
  1. Elimina espacios
  2. Convierte a minúsculas
  3. Elimina terminaciones de dominio (.com, .es, .net, etc.)
  4. Capitaliza primera letra
- **Fallback:** Si está vacío → 'Organico'
- **Uso:** Se normaliza al guardar y al comparar

## Performance
- **Batch loading** (load more) para tablas grandes (100 por defecto)
- **Debounce** en búsqueda global (300ms implícito)
- **Columnas redimensionables** con guardado en localStorage
- **Lazy loading** de datos del modal (solo cuando se abre)
- **Cálculo de contadores** desde toda la BD (no filtrado por fecha)
- **Paginación progresiva** → Carga inicial + botón "Cargar más"

## IDs y Names
- **NO cambiar** IDs de los campos ocultos sin actualizar tracker JS
- **NO asumir** funcionamiento sin Elementor Pro
- El Terminator **siempre debe ejecutarse** para evitar notices
- El Dashboard se alimenta SOLO de la base de datos de Elementor
- El trial dura 30 días desde la instalación y **no puede reiniciarse**

## UI/UX
- Toda la UI se controla desde `admin-dashboard.js`
- **Dark Mode** sincronizado en HTML + BODY (clase `efa-dark-mode`)
- **Tooltips** en headers con `data-tooltip` attribute
- **Badges semánticos** con colores por origen (función `getColorForString()`)
- **Sidebar** fijo en vista Correos (220px de ancho)
- **Modal con navegación** prev/next (botones deshabilitados en extremos)
- **Panel CRM slide-in** dentro del modal (520px de ancho, se posiciona a 520px del borde)
- **Modo Full Width** → Oculta menú lateral de WordPress (guardado en localStorage)
- **SweetAlert2** → Para diálogos avanzados (spam, confirmaciones)

## Modo Full Width
- **Toggle** en header del plugin
- **Efecto:** Oculta menú lateral de WordPress (`#adminmenuback`, `#adminmenuwrap`)
- **Guardado:** `gw_fullwidth_mode` en localStorage
- **Pre-cargador:** Aplica estilos inmediatamente para evitar flash

## Interceptación de spam en tiempo real
- **Hook:** `elementor_pro/forms/new_record`
- **Proceso:**
  1. Extrae email del submission
  2. Verifica blacklist
  3. Si está en blacklist → Busca submission_id y mueve a papelera
- **Ejecución:** Síncrona, inmediata después de que Elementor guarda

## Limpieza automática diaria
- **Cron job:** `gw_daily_spam_cleanup`
- **Frecuencia:** Una vez al día
- **Configuración:** `efa_autodelete_spam_days` (0 = desactivado)
- **Proceso:**
  1. Busca correos en papelera (`status = 'trash'`) más antiguos que X días
  2. Elimina permanentemente (DELETE de `wp_e_submissions_values` y `wp_e_submissions`)
  3. Límite de seguridad: 1000 correos por ejecución

## Exportación CSV
- **Generación:** Frontend (JavaScript)
- **Datos:** `currentRows` (filtrados)
- **Separador:** Configurable (',' o ';')
- **Encoding:** UTF-8 con BOM para Excel
- **Columnas:** Todas las visibles en tabla

## Tooltips
- **Implementación:** Div flotante con clase `.efa-tooltip`
- **Activación:** Hover en elementos con `data-tooltip`
- **Posicionamiento:** Centrado arriba del elemento
- **Uso:** Headers de tabla con información adicional

## Acciones rápidas (panel de detalles)
- **Llamar:** `tel:` link con número de teléfono
- **WhatsApp:** `wa.me` link con número (nueva ventana)
- **Email:** `mailto:` link con email
- **Extracción:** Se obtienen de los valores del formulario

## Cálculo de contadores del sidebar
- **Fuente:** Toda la base de datos (sin filtros de fecha)
- **Proceso:**
  1. Query completa de todos los submissions activos
  2. Procesa metadata en lotes
  3. Calcula: destacados, spam, estados CRM, formularios
  4. Retorna en `sidebar_counts`
- **Actualización:** En cada `fetchData()`

---

# 19. Requisitos mínimos

- **PHP ≥ 7.4**
- **WordPress ≥ 5.8**
- **Elementor Pro** (con Submissions activas)
- **Hosting con soporte para:**
  - SessionStorage
  - LocalStorage
  - AJAX
  - Chart.js (CDN)
  - jQuery UI
  - TCPDF (incluido)

---

# 20. Notas importantes de implementación

## Sistema de Reportes PDF
- **Configuración completa** en Settings > INFORMES
- **Drag & drop** para reordenar bloques
- **Toggle individual** para cada sección
- **Orden guardado** en `efa_report_blocks_order`
- **Generación** desde pestaña Informes con botón "Generar Informe PDF"
- **Vista previa** en modal antes de imprimir/descargar

## Columnas de Tabla
- **14 columnas** en total (13 de datos + 1 de acción)
- **Visibilidad configurable** por columna (guardado en localStorage)
- **Algunas ocultas por defecto:** form, landing, prev, lead, id
- **Redimensionables** con drag en header (guardado en localStorage)
- **Ancho mínimo:** Configurable por columna

## Sidebar Navigation
- **Estática:** Todos, Destacados, Spam, Papelera
- **Dinámica:** Estados CRM (generados desde configuración)
- **Dinámica:** Formularios (generados desde BD)
- **Contador de items** por categoría (desde toda la BD)
- **Badge especial** de emails destacados sin leer en header
- **Actualización en tiempo real** al cambiar estados, eliminar, etc.

## Búsqueda y Filtros
- **Búsqueda:** Input con debounce, consulta global en `allFetchedRows`
- **Origin filter:** Multi-checkbox con "Seleccionar todos"
- **Form filter:** Multi-checkbox con "Seleccionar todos"
- **Highlighted toggle:** Mostrar solo destacados
- **Filtros acumulativos:** Se combinan con AND logic
- **Aplicación:** Se envían al backend en `fetchData()`

## Paginación y Carga Progresiva
- **Batch size:** Configurable (10-500, recomendado 100)
- **Carga inicial:** Primer batch automático
- **Botón "Cargar más":** Aparece si hay más registros
- **Contador:** "Mostrando X de Y" en header de tabla
- **Datos:** Se filtran localmente antes de renderizar

## Normalización de orígenes
- **Función:** `normalize_origin_name()` en `class-gw-dashboard.php`
- **Uso:** Al guardar, al comparar, al mostrar
- **Efecto:** Unifica nombres (ej: "google.com" → "Google")
- **Fallback:** Vacío → "Organico"

## Panel CRM Secundario
- **Posicionamiento:** `right: 520px` (encima del panel principal)
- **Ancho:** 520px
- **Z-index:** 99999 (debajo del panel principal: 100000)
- **Transición:** Slide desde la derecha (0.35s cubic-bezier)
- **Contenido:** Estado CRM + Notas + Input

## SweetAlert2 para gestión de spam
- **Uso:** Diálogos avanzados al hacer click en badge de spam
- **Opciones:**
  - Solo marcar como spam
  - Spam + Bloquear remitente
  - Eliminar todo del remitente + blacklist
  - Cancelar
- **Feedback:** Notificaciones de éxito/error

## Renderizado de detalles en modal
- **Función:** `renderDetails(data, rowData)` en JavaScript
- **Estructura:**
  - Fecha destacada (bloque oscuro)
  - Grid de datos (2 columnas)
  - Ruta del usuario (Landing → Previa → Lead)
  - Todos los campos del formulario
- **Highlighting:** Resalta palabras clave en texto
- **URLs:** Convierte URLs a títulos bonitos (`get_pretty_title()`)

---

# 21. Análisis de Formularios (Pestaña "Formularios")

## A. Propósito y alcance

La pestaña **Formularios** proporciona una vista analítica dedicada para cada formulario de Elementor instalado en el sitio, con métricas de rendimiento, detección completa de spam y visualizaciones interactivas.

### Objetivos:
- Visualizar rendimiento individual de cada formulario
- Analizar orígenes de tráfico con colores semánticos
- Detectar spam con el sistema completo de reglas
- Comparar formularios por rendimiento (alto/medio/bajo)
- Filtrar por formularios específicos y períodos de tiempo

---

## B. Estructura y diseño

### Header unificado
Diseño coherente con las páginas de Informes y Ajustes:

- **Icono rosa** (#e01d75) en cuadrado de 48x48px
- **Título principal** a 32px (dashicons-feedback)
- **Descripción** en gris claro
- **Estadísticas en cards separadas:**
  - Total de formularios activos
  - Total de envíos válidos (sin spam)
  - Spam detectado (en rojo)

### Card de filtros
- **Clase:** `.gw-card`
- **Título con icono** (dashicons-filter)
- **Contenido:** Formulario con 2 selectores multiselect + botón aplicar

---

## C. Sistema de filtros

### 1. Multiselect de Formularios
**Estructura:**
- Botón desplegable que muestra "Formularios: Todos" o "Formularios: X"
- Dropdown con checkboxes individuales
- Checkbox "Seleccionar Todos" en la parte superior
- Separador visual
- Actualización dinámica del texto del botón

**Funcionamiento:**
- Al desmarcar "Todos", se desmarcan todos los formularios
- Al desmarcar todos manualmente, se desmarca "Todos"
- El contador se actualiza automáticamente
- Cierre automático de otros dropdowns al abrir

**HTML generado:**
```php
<div class="efa-multiselect-wrapper" id="gw-form-wrapper">
    <button class="efa-select-sm efa-multiselect-btn" id="gw-form-btn">
        <span>Formularios: Todos</span>
        <span class="dashicons dashicons-arrow-down-alt2"></span>
    </button>
    <div class="efa-multiselect-dropdown" id="gw-form-dropdown">
        <label class="efa-ms-option all">
            <input type="checkbox" id="gw-form-all" checked>
            <strong>Seleccionar Todos</strong>
        </label>
        <div class="efa-ms-separator"></div>
        <!-- Opciones de formularios -->
    </div>
</div>
```

### 2. Multiselect de Período
**Estructura idéntica al selector de formularios:**
- Botón desplegable con texto dinámico
- Radio buttons en lugar de checkboxes
- Opciones:
  - Últimos 7 días
  - Último mes
  - Todo el tiempo
  - Personalizado (separador antes)

**Campos de fecha personalizada:**
- Se muestran solo si se selecciona "Personalizado"
- Inputs tipo `date` con estilos coherentes
- Texto "a" entre los dos campos

**Almacenamiento:**
- Input oculto `gw_date_range` con el valor seleccionado
- Envío por POST al aplicar filtros

**JavaScript:**
```javascript
$('input[name="date_range_radio"]').on('change', function() {
    const value = $(this).val();
    $('#gw-date-range-value').val(value);
    $('#gw-date-label').text(labels[value]);
    
    if (value === 'custom') {
        $('#gw-custom-dates').show();
    } else {
        $('#gw-custom-dates').hide();
    }
    
    $('#gw-date-wrapper').removeClass('active');
});
```

### 3. Z-index y superposición
**Jerarquía de capas:**
- `.gw-forms-grid` → `z-index: 1` (base)
- `.gw-forms-filters` → `z-index: 50` (filtros)
- `.efa-multiselect-wrapper` → `z-index: 100` (normal)
- `.efa-multiselect-wrapper.active` → `z-index: 1001` (activo)
- `.efa-multiselect-dropdown` → `z-index: 1002` (dropdown)

**Resultado:** Los dropdowns siempre aparecen por encima de los cards de formularios

---

## D. Detección completa de SPAM

### Sistema implementado (idéntico al Dashboard)

El sistema de detección de spam en Formularios utiliza **exactamente las mismas reglas** que el Dashboard para garantizar consistencia.

#### Métodos implementados:
```php
private function is_whitelisted($email)       // Verifica lista blanca
private function is_hard_banned($email)       // Verifica TLDs y blacklist
private function detect_spam_risk($text, $name, $email)  // Heurísticas
private function calculate_spam_count($form_name, $date_from, $date_to)  // Calcula spam total
```

#### Orden de prioridad:
1. **Whitelist (Inmunidad)** → `is_whitelisted()` → Siempre seguro (risk = 0)
2. **Hard Ban (Blacklist/TLDs)** → `is_hard_banned()` → Siempre spam (risk = 2)
3. **Override manual** → `efa_spam_override` = '1' o '2' → Spam manual
4. **Detección automática** → `detect_spam_risk()` → Heurísticas

#### Heurísticas de detección:
```php
// Nombre sospechoso
if (preg_match('/[0-9_]/', $name)) return 2;
if (strpos($name, 'http') !== false) return 2;
if (str_word_count($name) > 5) return 2;

// Caracteres especiales
if (preg_match('/[\x{0400}-\x{04FF}]/u', $text)) return 2;  // Cirílico
if (preg_match('/[\x{4E00}-\x{9FBF}]/u', $text)) return 2;  // Chino

// Keywords de spam (configurables en Settings > SPAM)
$keywords = get_option('efa_spam_keywords', 'crypto, investment, wallet, forex, casino, bitcoin');
```

#### Procesamiento por lotes:
```php
// Procesa submissions en chunks de 100
$chunks = array_chunk($submission_ids, 100);

foreach ($chunks as $chunk_ids) {
    // Obtiene metadata
    $chunk_values = $wpdb->get_results(...);
    
    // Agrupa por submission_id
    $submissions_data = [];
    
    // Aplica reglas de spam
    foreach ($chunk_ids as $sid) {
        // 1. Extrae nombre, email, texto
        // 2. Verifica whitelist
        // 3. Verifica hard ban
        // 4. Verifica override
        // 5. Aplica heurísticas
        // 6. Cuenta si risk === 2
    }
}
```

#### Integración en métricas:
- **Total de envíos:** Excluye spam de todas las métricas
- **Spam count:** Calculado con todas las reglas
- **Spam rate:** Porcentaje sobre el total (incluido spam)
- **Todas las consultas:** Excluyen spam con filtro SQL + validación PHP

---

## E. Cards de formularios

### Sistema de clasificación por rendimiento

**Cálculo:**
```php
$performance = 'low';
if ($metrics['avg_daily'] >= 2) {
    $performance = 'high';
} elseif ($metrics['avg_daily'] >= 0.5) {
    $performance = 'medium';
}
```

**Visual:**
- **Alto rendimiento** (🔥): Borde izquierdo verde (#10b981), promedio ≥ 2/día
- **Rendimiento medio** (⚡): Borde izquierdo naranja (#f59e0b), promedio ≥ 0.5/día
- **Bajo rendimiento** (💤): Borde izquierdo gris (#6b7280), promedio < 0.5/día

### Estructura del card

**Header:**
- Nombre del formulario (truncado con ellipsis)
- Badge de performance con icono y texto

**Métricas principales (3 columnas):**
- **Total envíos** (primaria, fondo rosa #e01d75)
- **Promedio/día**
- **Últimos 30 días**

**Barra de progreso:**
- Tasa de lectura en porcentaje
- Barra rosa con animación

**Indicadores rápidos:**
- 🛡️ Tasa de spam (%)
- ✅ Correos leídos (contador)

**Top 3 Orígenes:**
- Punto de color semántico (12px, redondo)
- Nombre normalizado
- Contador a la derecha

**Top 3 Páginas:**
- URL parseada (muestra path o "Home")
- Contador a la derecha

**Gráfica de últimos 7 días:**
- Barras verticales (miniatura 60px de alto)
- Color rosa #e01d75
- Hover muestra tooltip con fecha y cantidad

**Estados CRM:**
- Pills con colores configurables
- Etiquetas visibles (no IDs internos)
- Formato: "Nombre: Cantidad"

---

## F. Colores semánticos de orígenes

### Implementación:
```php
private function get_origin_color($origin)
{
    $SEMANTIC_COLORS = [
        'facebook' => '#1877F2',
        'instagram' => '#E1306C',
        'google' => '#4285F4',
        'youtube' => '#FF0000',
        'ads' => '#EA4335',
        'organic' => '#34A853',
        'organico' => '#34A853',
        'seo' => '#34A853',
        'whatsapp' => '#25D366',
        'linkedin' => '#0A66C2',
        'tiktok' => '#000000',
        'email' => '#FBBC05',
        'newsletter' => '#FBBC05',
        'chatgpt' => '#000000',
        'directo' => '#646970',
        'referral' => '#A142F4'
    ];
    
    $FALLBACK_PALETTE = [
        '#0d9488', '#be123c', '#d97706', '#4338ca', 
        '#a21caf', '#15803d', '#0369a1', '#b91c1c'
    ];
    
    // 1. Buscar coincidencia exacta
    // 2. Buscar coincidencia parcial
    // 3. Hash del string para color fallback
}
```

### Visual en cards:
```html
<div class="origin-item">
    <div class="origin-left">
        <span class="origin-dot" style="background-color: #1877F2;"></span>
        <span class="origin-name">Facebook</span>
    </div>
    <span class="origin-count">25</span>
</div>
```

---

## G. Métricas calculadas

### Función principal: `get_form_metrics()`

**Parámetros:**
- `$form_name` (string): Nombre del formulario
- `$date_from` (string|null): Fecha inicio (Y-m-d H:i:s)
- `$date_to` (string|null): Fecha fin (Y-m-d H:i:s)

**Retorna:**
```php
[
    'total' => int,                    // Total sin spam
    'total_with_spam' => int,          // Total incluyendo spam
    'last_30_days' => int,             // Últimos 30 días sin spam
    'spam_count' => int,               // Spam detectado
    'spam_rate' => float,              // Porcentaje de spam
    'read_count' => int,               // Correos leídos
    'read_rate' => float,              // Porcentaje de lectura
    'origins' => array,                // Top 5 orígenes con colores
    'crm_states' => array,             // Estados CRM con etiquetas
    'last_7_days' => array,            // Datos para gráfica
    'top_pages' => array,              // Top 3 páginas
    'avg_daily' => float               // Promedio diario
]
```

### Consultas SQL optimizadas:

**Total de envíos:**
```sql
SELECT COUNT(*) 
FROM wp_e_submissions s
WHERE s.form_name = %s
AND s.status != 'trash'
AND s.created_at >= %s AND s.created_at <= %s
```

**Orígenes (con normalización):**
```sql
SELECT 
    COALESCE(v.value, 'Organico') as origin,
    COUNT(*) as count
FROM wp_e_submissions s
LEFT JOIN wp_e_submissions_values v ON s.id = v.submission_id 
    AND v.key IN ('origen', 'origin')
WHERE s.form_name = %s
AND s.status != 'trash'
-- Spam exclusion filter
GROUP BY v.value
ORDER BY count DESC
```

**Estados CRM (con mapeo a etiquetas):**
```sql
SELECT 
    COALESCE(v.value, 'new') as state,
    COUNT(*) as count
FROM wp_e_submissions s
LEFT JOIN wp_e_submissions_values v ON s.id = v.submission_id 
    AND v.key = 'efa_crm_status'
WHERE s.form_name = %s
AND s.status != 'trash'
-- Spam exclusion filter
GROUP BY state
```

Luego se mapean los IDs a etiquetas visibles:
```php
$crm_labels_map = [];
$crm_labels = $this->get_crm_labels();
foreach ($crm_labels as $label) {
    $crm_labels_map[$label['id']] = [
        'label' => $label['label'],  // "Nuevo" en lugar de "new"
        'color' => $label['color']
    ];
}
```

---

## H. Normalización de orígenes

### Función: `normalize_origin()`

**Proceso:**
1. Convierte a minúsculas
2. Elimina extensiones de dominio (.com, .es, .net, etc.)
3. Elimina "www."
4. Verifica reemplazos específicos (Facebook, Google, etc.)
5. Capitaliza primera letra

**Código:**
```php
private function normalize_origin($origin)
{
    if (empty($origin)) return 'Organico';
    
    $origin = strtolower(trim($origin));
    
    // Reemplazos específicos
    $replacements = [
        'organico' => 'Organico',
        'directo' => 'Directo',
        'google' => 'Google',
        'facebook' => 'Facebook',
        // ... más ...
    ];
    
    // Eliminar extensiones
    $origin = preg_replace('/\.(com|es|net|org|...)$/i', '', $origin);
    
    // Eliminar www.
    $origin = str_replace('www.', '', $origin);
    
    if (isset($replacements[$origin])) {
        return $replacements[$origin];
    }
    
    return ucfirst($origin);
}
```

**Ejemplos:**
- `facebook.com` → `Facebook`
- `GOOGLE.ES` → `Google`
- `www.instagram.com` → `Instagram`
- `linkedin` → `LinkedIn`
- `example.net` → `Example`
- (vacío) → `Organico`

---

## I. Estilos y diseño coherente

### Colores corporativos:
- **Rosa principal:** #e01d75
- **Rosa hover:** #c41866
- **Texto oscuro:** #1f2937
- **Texto gris:** #6b7280
- **Bordes:** #e5e7eb
- **Fondos claros:** #f9fafb

### Cards `.gw-card`:
```css
.gw-card {
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    padding: 24px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
    transition: all 0.3s ease;
}

.gw-card:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
    transform: translateY(-2px);
}
```

### Headers consistentes:
```css
.gw-main-title {
    font-size: 32px;
    font-weight: 700;
    color: #1f2937;
    display: flex;
    align-items: center;
    gap: 15px;
}

.gw-icon-wrapper {
    width: 48px;
    height: 48px;
    background: #e01d75;
    border-radius: 12px;
    color: white;
}
```

### Botones interactivos:
```css
.efa-btn {
    background: #e01d75;
    color: white;
    border-radius: 8px;
    font-weight: 600;
    transition: all 0.3s;
}

.efa-btn:hover {
    background: #c41866;
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(224,29,117,0.3);
}
```

---

## J. Dark Mode

### Implementación completa:

**Cards y contenedores:**
```css
body.efa-dark-mode .gw-card {
    background: #1f2940 !important;
    border-color: #3a3a4e !important;
}

body.efa-dark-mode .gw-main-title {
    color: #e4e7eb !important;
}

body.efa-dark-mode .gw-header-description {
    color: #9ca3af !important;
}
```

**Formularios y selectores:**
```css
body.efa-dark-mode .efa-multiselect-btn,
body.efa-dark-mode .efa-select-sm {
    background: #2a3a52 !important;
    border-color: #3a3a4e !important;
    color: #e5e7eb !important;
}

body.efa-dark-mode .efa-multiselect-dropdown {
    background: #1f2940 !important;
    border-color: #3a3a4e !important;
}
```

**Cards de formularios:**
```css
body.efa-dark-mode .form-card {
    background: #1f2940 !important;
    border-color: #3a3a4e !important;
}

body.efa-dark-mode .metric-box {
    background: #2a3a52 !important;
}
```

**Fechas personalizadas:**
```css
body.efa-dark-mode .efa-filters input[type="date"] {
    background: #2a3a52 !important;
    border-color: #3a3a4e !important;
    color: #e5e7eb !important;
}
```

---

## K. Responsive design

### Breakpoints:

**1200px:**
```css
@media (max-width: 1200px) {
    .gw-header-content {
        flex-direction: column;
    }
    
    .gw-header-stats {
        width: 100%;
        justify-content: space-between;
    }
}
```

**768px:**
```css
@media (max-width: 768px) {
    .gw-forms-grid {
        grid-template-columns: 1fr;
    }
    
    .gw-main-title {
        font-size: 24px;
    }
    
    .gw-header-stats {
        flex-direction: column;
        gap: 12px;
    }
    
    .gw-stat-item {
        width: 100%;
    }
    
    .filters-form {
        flex-direction: column;
        gap: 12px;
    }
}
```

---

## L. JavaScript - Funcionalidades

### 1. Multiselect de formularios:
```javascript
$('#gw-form-btn').on('click', function(e) {
    e.stopPropagation();
    $('#gw-date-wrapper').removeClass('active');
    $('#gw-form-wrapper').toggleClass('active');
});

$('#gw-form-all').on('change', function() {
    const isChecked = $(this).is(':checked');
    $('.gw-form-check').prop('checked', isChecked);
    updateFormButtonText();
});

$('.gw-form-check').on('change', function() {
    const allChecked = $('.gw-form-check:checked').length === $('.gw-form-check').length;
    $('#gw-form-all').prop('checked', allChecked);
    updateFormButtonText();
});
```

### 2. Selector de período:
```javascript
$('input[name="date_range_radio"]').on('change', function() {
    const value = $(this).val();
    const labels = {
        'last_7_days': 'Últimos 7 días',
        'last_month': 'Último mes',
        'all': 'Todo el tiempo',
        'custom': 'Personalizado'
    };
    
    $('#gw-date-range-value').val(value);
    $('#gw-date-label').text(labels[value]);
    
    if (value === 'custom') {
        $('#gw-custom-dates').show();
    } else {
        $('#gw-custom-dates').hide();
    }
    
    $('#gw-date-wrapper').removeClass('active');
});
```

### 3. Cerrar dropdowns al hacer clic fuera:
```javascript
$(document).on('click', function(e) {
    if (!$(e.target).closest('.efa-multiselect-wrapper').length) {
        $('.efa-multiselect-wrapper').removeClass('active');
    }
});
```

### 4. Animación de entrada:
```javascript
$('.form-card').each(function(i) {
    $(this).css({
        'opacity': '0',
        'transform': 'translateY(20px)'
    });
    
    setTimeout(() => {
        $(this).css({
            'opacity': '1',
            'transform': 'translateY(0)',
            'transition': 'all 0.5s ease'
        });
    }, i * 50);
});
```

---

## M. Integración con el sistema

### Clase principal: `class-gw-forms.php`

**Ubicación:** `/includes/class-gw-forms.php`

**Instanciación en `class-gw-main.php`:**
```php
$this->forms_instance = new GW_Analytics_Forms();
```

**Menú en WordPress:**
```php
add_submenu_page(
    'gw-elementor',
    'Formularios',
    'Formularios',
    $role,
    'gw-elementor-forms',
    [$this, 'render_forms_page']
);
```

**Renderizado:**
```php
public function render_forms_page()
{
    $_GET['tab'] = 'forms';
    $this->render_main_page();
}
```

### Métodos públicos:
- `render_tab()` → Punto de entrada principal
- `render_forms_view()` → DEPRECATED, ahora integrado en render_tab()

### Métodos privados:
- `get_crm_labels()` → Obtiene etiquetas CRM configuradas
- `get_origin_color()` → Colores semánticos por origen
- `normalize_origin()` → Normaliza nombres de orígenes
- `is_whitelisted()` → Verifica lista blanca
- `is_hard_banned()` → Verifica TLDs y blacklist
- `detect_spam_risk()` → Heurísticas de spam
- `get_all_forms()` → Lista de formularios
- `calculate_spam_count()` → Calcula spam con todas las reglas
- `get_form_metrics()` → Métricas completas de un formulario
- `render_forms_grid()` → Renderiza grid de cards
- `render_styles()` → Estilos CSS inline
- `render_scripts()` → JavaScript inline

---

## N. Opciones de WordPress (wp_options)

**Opciones consultadas:**
- `efa_crm_labels` → Estados CRM personalizables
- `efa_whitelist_emails` → Lista blanca de emails
- `efa_spam_emails` → Lista negra (blacklist)
- `efa_spam_tlds` → TLDs bloqueados (.ru, .cn, etc.)
- `efa_spam_keywords` → Keywords de spam

**Opciones escritas:**
- Ninguna (solo lectura)

---

## O. Tablas de base de datos

**Tablas consultadas:**
- `wp_e_submissions` → Meta de envíos (id, form_name, created_at, status)
- `wp_e_submissions_values` → Key-value pairs de cada submission

**Queries principales:**
1. Total de envíos por formulario (con filtros de fecha y spam)
2. Spam count (procesamiento por lotes con reglas completas)
3. Tasa de lectura (efa_read_status = '1')
4. Distribución por origen (normalizada, con colores)
5. Estados CRM (mapeados a etiquetas visibles)
6. Últimos 7 días (para gráfica)
7. Top 3 páginas (current_url)

---

## P. Consideraciones para LLMs

### Consistencia de spam
- **CRÍTICO:** La detección de spam en Formularios debe usar **exactamente las mismas reglas** que el Dashboard
- No hardcodear: siempre consultar `efa_spam_keywords`, `efa_spam_tlds`, etc.
- El orden de prioridad es: Whitelist → Hard Ban → Override → Heurísticas

### Normalización de orígenes
- **Siempre normalizar** antes de mostrar
- **Siempre aplicar colores** semánticos desde `get_origin_color()`
- Fallback a `Organico` si está vacío

### Estados CRM
- **Cargar dinámicamente** desde `efa_crm_labels`
- **Mostrar etiquetas** (label), no IDs internos
- **Aplicar colores** configurados por el usuario

### Filtros de spam en SQL
- No confiar solo en `efa_spam_override`
- Siempre procesar en PHP con las 4 reglas
- Usar `calculate_spam_count()` para precisión

### Multiselects
- **Cerrar otros** al abrir uno nuevo
- **Z-index correcto** para evitar superposición con cards
- **Actualizar texto del botón** dinámicamente

### Performance
- Procesar spam en **lotes de 100** submissions
- Limitar orígenes a **Top 5**
- Limitar páginas a **Top 3**
- Calcular `avg_daily` basándose en los últimos 30 días

### Responsive
- Grid de **1 columna** en móvil
- Stats de header en **columna** en móvil
- Filtros **apilados** en móvil

---

## Q. Diferencias con Dashboard y Correos

| Aspecto | Dashboard | Correos | Formularios |
|---------|-----------|---------|-------------|
| **Propósito** | Visualización global | Gestión de leads | Análisis por formulario |
| **Datos** | Agregados | Tabla detallada | Cards agrupados |
| **Acciones** | Solo lectura | CRUD completo | Solo lectura |
| **Spam** | Distribución | Gestión manual | Detección automática |
| **CRM** | Distribución | Panel completo | Distribución por form |
| **Filtros** | Avanzados | Sidebar + Toolbar | Multiselect simple |
| **Exportación** | PDF | CSV | Ninguna |

---

## R. Estado actual (v2.2.1)

**Implementado:**
- ✅ Detección completa de spam con todas las reglas
- ✅ Multiselect de formularios con checkboxes
- ✅ Multiselect de período con radio buttons
- ✅ Colores semánticos de orígenes con dots
- ✅ Estados CRM con etiquetas visibles
- ✅ Sistema de performance (alto/medio/bajo)
- ✅ Métricas completas por formulario
- ✅ Dark mode completo
- ✅ Diseño coherente con Informes y Ajustes
- ✅ Z-index correcto para dropdowns
- ✅ Responsive design completo

**No implementado (futuro):**
- ⏳ Exportación CSV de métricas de formularios
- ⏳ Comparación side-by-side de formularios
- ⏳ Gráficas de evolución histórica por formulario
- ⏳ Alertas por bajo rendimiento

---

# FIN DEL DOCUMENTO

**Última actualización:** 2025-01-25  
**Versión del plugin:** 2.2.1  
**Mantenido para:** Agentes IA (LLMs) que trabajen con este código
