feat(analytics): Dashboard v2 con recomendaciones accionables y UX mejorada
- Agregar KPIs con tendencias vs período anterior (↑↓% comparativo) - Implementar secciones de recomendaciones: Contenido a Crear, CTR 0%, Quick Wins, Contenido Estrella, Contenido en Decadencia - Convertir listados a tablas con columnas separadas para mejor legibilidad - Agregar botones Editar + Ver en todas las tablas de posts - Ocultar secciones vacías dinámicamente (Búsquedas Sin Resultados) - Relajar criterios Quick Wins: pos 2-15, CTR ≥2%, búsquedas ≥2 - Incluir distribución de clicks por posición con barras de progreso - Agregar exportación a Markdown para análisis con IA Archivos nuevos: - admin/class-analytics-dashboard.php (UI del dashboard) - admin/class-metrics-repository.php (queries de métricas) - admin/assets/dashboard.css (estilos Bootstrap 5) - admin/assets/dashboard.js (interactividad y export) - sql/create-indices.sql (índices para optimización) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
138
admin/assets/dashboard.css
Normal file
138
admin/assets/dashboard.css
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* ROI APU Search - Analytics Dashboard Styles
|
||||
*
|
||||
* @package ROI_APU_Search
|
||||
* @since 1.2.0
|
||||
*/
|
||||
|
||||
/* =================================================================
|
||||
WordPress Admin Fixes
|
||||
================================================================= */
|
||||
|
||||
/* Fix: WordPress limita ancho de cards */
|
||||
body .card {
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
/* Fix: WordPress admin puede afectar tipografia */
|
||||
.dashboard-analytics-wrap {
|
||||
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
|
||||
/* Fix: Asegurar que Bootstrap no colisione con WP admin */
|
||||
.dashboard-analytics-wrap .btn {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/* Fix: Reset WP admin styles dentro del dashboard */
|
||||
.dashboard-analytics-wrap h1,
|
||||
.dashboard-analytics-wrap h2,
|
||||
.dashboard-analytics-wrap h3,
|
||||
.dashboard-analytics-wrap h4,
|
||||
.dashboard-analytics-wrap h5,
|
||||
.dashboard-analytics-wrap h6 {
|
||||
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
Admin Notice Styles
|
||||
================================================================= */
|
||||
|
||||
.admin-notice {
|
||||
border-left: 4px solid;
|
||||
border-radius: 4px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Error - CTR 0% (critico) */
|
||||
.admin-notice.notice-error {
|
||||
border-color: #ef4444;
|
||||
background-color: #fef2f2;
|
||||
color: #7f1d1d;
|
||||
}
|
||||
|
||||
/* Warning - Posts infraposicionados (oportunidad) */
|
||||
.admin-notice.notice-warning {
|
||||
border-color: #f59e0b;
|
||||
background-color: #fffbeb;
|
||||
color: #78350f;
|
||||
}
|
||||
|
||||
/* Info - Sin resultados */
|
||||
.admin-notice.notice-info {
|
||||
border-color: #0284c7;
|
||||
background-color: #e7f3ff;
|
||||
color: #0c4a6e;
|
||||
}
|
||||
|
||||
/* Success - Feedback export */
|
||||
.admin-notice.notice-success {
|
||||
border-color: #22c55e;
|
||||
background-color: #f0fdf4;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
Loading State
|
||||
================================================================= */
|
||||
|
||||
#loadingState {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dashboard-analytics-wrap.is-loading #loadingState {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dashboard-analytics-wrap.is-loading #dashboardContent {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
KPI Cards - Sin efecto hover
|
||||
================================================================= */
|
||||
|
||||
.dashboard-analytics-wrap .card {
|
||||
/* Sin transiciones de movimiento */
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
Tables
|
||||
================================================================= */
|
||||
|
||||
.dashboard-analytics-wrap .table {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.dashboard-analytics-wrap .table th {
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
Export Card
|
||||
================================================================= */
|
||||
|
||||
.export-card {
|
||||
border: 2px dashed #dee2e6;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.export-card:hover {
|
||||
border-color: #FF8600;
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
Responsive
|
||||
================================================================= */
|
||||
|
||||
@media (max-width: 782px) {
|
||||
.dashboard-analytics-wrap .admin-notice {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.dashboard-analytics-wrap .card-body {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
}
|
||||
233
admin/assets/dashboard.js
Normal file
233
admin/assets/dashboard.js
Normal file
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* ROI APU Search - Analytics Dashboard Scripts
|
||||
*
|
||||
* @package ROI_APU_Search
|
||||
* @since 1.2.0
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Initialize Bootstrap tooltips
|
||||
*/
|
||||
function initTooltips() {
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
if (tooltipTriggerList.length > 0 && typeof bootstrap !== 'undefined') {
|
||||
[...tooltipTriggerList].map(el => new bootstrap.Tooltip(el));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy markdown to clipboard
|
||||
* Implementation in FASE 9
|
||||
*/
|
||||
function copyMarkdown() {
|
||||
const markdown = generateMarkdown();
|
||||
|
||||
navigator.clipboard.writeText(markdown).then(function() {
|
||||
showSuccessMessage('Copiado: El resumen está en tu portapapeles.');
|
||||
}).catch(function(err) {
|
||||
console.error('Error al copiar:', err);
|
||||
showErrorMessage('Error al copiar al portapapeles.');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate comprehensive markdown report from dashboard data (v2)
|
||||
*/
|
||||
function generateMarkdown() {
|
||||
const data = window.roiApuDashboardData;
|
||||
if (!data) {
|
||||
return '# Error: No hay datos disponibles';
|
||||
}
|
||||
|
||||
let md = '';
|
||||
|
||||
// Header
|
||||
md += '# Reporte Analytics - Buscador APUs\n\n';
|
||||
md += `**Período**: Últimos ${data.period} días\n`;
|
||||
md += `**Generado**: ${data.generated}\n`;
|
||||
md += `**Sitio**: ${data.siteUrl}\n\n`;
|
||||
|
||||
// KPIs Table
|
||||
md += '## Métricas Clave\n\n';
|
||||
md += '| Métrica | Valor |\n';
|
||||
md += '|---------|-------|\n';
|
||||
md += `| Búsquedas totales | ${data.kpis.totalBusquedas} |\n`;
|
||||
md += `| CTR (Click-Through Rate) | ${data.kpis.ctr} |\n`;
|
||||
md += `| Búsquedas sin resultados | ${data.kpis.sinResultados} |\n`;
|
||||
md += `| Posición promedio clicks | ${data.kpis.posProm} |\n\n`;
|
||||
|
||||
// Click Distribution
|
||||
if (data.clickDistribution && data.clickDistribution.length > 0) {
|
||||
md += '## Distribución de Clicks por Posición\n\n';
|
||||
md += '| Posición | Clicks | Porcentaje |\n';
|
||||
md += '|----------|--------|------------|\n';
|
||||
data.clickDistribution.forEach(function(dist) {
|
||||
md += `| ${dist.posicion} | ${dist.clicks} | ${dist.porcentaje}% |\n`;
|
||||
});
|
||||
md += '\n';
|
||||
}
|
||||
|
||||
md += '---\n\n';
|
||||
md += '## RECOMENDACIONES ACCIONABLES\n\n';
|
||||
|
||||
// 🔴 Urgent: Zero Results
|
||||
if (data.zeroResults && data.zeroResults.length > 0) {
|
||||
md += '### 🔴 ACCIÓN URGENTE: Contenido a Crear\n\n';
|
||||
md += 'Los usuarios buscan esto pero NO encuentran resultados:\n\n';
|
||||
md += '| Término | Frecuencia |\n';
|
||||
md += '|---------|------------|\n';
|
||||
data.zeroResults.forEach(function(term) {
|
||||
md += `| ${term.term} | ${term.frecuencia} veces |\n`;
|
||||
});
|
||||
md += '\n**Acción**: Crear APUs o contenido para estos términos.\n\n';
|
||||
}
|
||||
|
||||
// 🟡 CTR 0% Section
|
||||
if (data.ctrZero && data.ctrZero.length > 0) {
|
||||
md += '### 🟡 REVISAR: Títulos con CTR 0%\n\n';
|
||||
md += 'Estos términos tienen resultados pero nadie hace click:\n\n';
|
||||
md += '| Término | Búsquedas | Resultados |\n';
|
||||
md += '|---------|-----------|------------|\n';
|
||||
data.ctrZero.forEach(function(term) {
|
||||
md += `| ${term.term} | ${term.busquedas} | ${term.resultados} |\n`;
|
||||
});
|
||||
md += '\n**Acción**: Mejorar títulos y descripciones de los APUs mostrados.\n\n';
|
||||
}
|
||||
|
||||
// 🎯 Quick Wins
|
||||
if (data.quickWins && data.quickWins.length > 0) {
|
||||
md += '### 🎯 QUICK WINS: Oportunidades Fáciles\n\n';
|
||||
md += 'Términos en posición 4-10 con buen CTR (una pequeña mejora = top 3):\n\n';
|
||||
md += '| Término | Búsquedas | CTR | Pos. Actual |\n';
|
||||
md += '|---------|-----------|-----|-------------|\n';
|
||||
data.quickWins.forEach(function(term) {
|
||||
md += `| ${term.term} | ${term.busquedas} | ${term.ctr}% | ${term.posProm} |\n`;
|
||||
});
|
||||
md += '\n**Acción**: Optimizar estos APUs para subir al top 3.\n\n';
|
||||
}
|
||||
|
||||
// 📉 Decay Content
|
||||
if (data.decayContent && data.decayContent.length > 0) {
|
||||
md += '### 📉 ATENCIÓN: Contenido en Decadencia\n\n';
|
||||
md += 'Posts que perdieron >20% clicks vs período anterior:\n\n';
|
||||
md += '| Título | Cambio | Clicks (antes → ahora) |\n';
|
||||
md += '|--------|--------|------------------------|\n';
|
||||
data.decayContent.forEach(function(post) {
|
||||
md += `| [${post.title}](${post.url}) | ${post.cambioPct}% | ${post.clicksAnterior} → ${post.clicksActual} |\n`;
|
||||
});
|
||||
md += '\n**Acción**: Revisar si el contenido está desactualizado.\n\n';
|
||||
}
|
||||
|
||||
// 🟢 Star Content
|
||||
if (data.contenidoEstrella && data.contenidoEstrella.length > 0) {
|
||||
md += '### 🟢 MANTENER: Tu Contenido Estrella\n\n';
|
||||
md += 'Posts con más clicks - mantén este contenido actualizado:\n\n';
|
||||
md += '| Título | Clicks | Pos. Prom. |\n';
|
||||
md += '|--------|--------|------------|\n';
|
||||
data.contenidoEstrella.forEach(function(post) {
|
||||
md += `| [${post.title}](${post.url}) | ${post.clicks} | ${post.posProm} |\n`;
|
||||
});
|
||||
md += '\n**Acción**: Mantener actualizado y considerar contenido relacionado.\n\n';
|
||||
}
|
||||
|
||||
// Infraposicionados Section
|
||||
if (data.infraposicionados && data.infraposicionados.length > 0) {
|
||||
md += '### 🔼 OPORTUNIDAD: Posts Infraposicionados\n\n';
|
||||
md += 'Estos posts reciben clicks pero aparecen muy abajo:\n\n';
|
||||
md += '| Título | Clicks | Pos. Prom. |\n';
|
||||
md += '|--------|--------|------------|\n';
|
||||
data.infraposicionados.forEach(function(post) {
|
||||
md += `| ${post.title} | ${post.clicks} | ${post.posProm} |\n`;
|
||||
});
|
||||
md += '\n**Acción**: Mejorar scoring o relevancia de estos APUs.\n\n';
|
||||
}
|
||||
|
||||
md += '---\n\n';
|
||||
|
||||
// Summary stats
|
||||
md += '## Resumen de Datos\n\n';
|
||||
md += `- **Términos únicos buscados**: ${data.totals.searches}\n`;
|
||||
md += `- **Posts con clicks**: ${data.totals.clicks}\n`;
|
||||
md += `- **Términos sin resultados**: ${data.totals.zeroResults}\n\n`;
|
||||
|
||||
// Questions for analysis
|
||||
md += '## Preguntas para Análisis con IA\n\n';
|
||||
md += '1. ¿Qué contenido debería crear primero basándome en las búsquedas sin resultados?\n';
|
||||
md += '2. ¿Por qué algunos términos tienen resultados pero CTR 0%? ¿Qué puedo mejorar?\n';
|
||||
md += '3. ¿Cómo optimizo los Quick Wins para llegar al top 3?\n';
|
||||
md += '4. ¿Qué patrones veo en mi contenido estrella que debería replicar?\n';
|
||||
md += '5. ¿Hay contenido en decadencia que debería actualizar urgentemente?\n\n';
|
||||
|
||||
// Footer
|
||||
md += '---\n';
|
||||
md += '*Generado por ROI APU Search Dashboard v2*\n';
|
||||
md += '*Comparte este reporte con Claude para obtener recomendaciones detalladas.*\n';
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show success message
|
||||
*/
|
||||
function showSuccessMessage(message) {
|
||||
const container = document.getElementById('alertContainer');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="admin-notice notice-success mb-3" role="alert">
|
||||
<i class="bi bi-check-circle me-2"></i>
|
||||
<strong>${message}</strong>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Auto-hide after 3 seconds
|
||||
setTimeout(function() {
|
||||
container.innerHTML = '';
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error message
|
||||
*/
|
||||
function showErrorMessage(message) {
|
||||
const container = document.getElementById('alertContainer');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="admin-notice notice-error mb-3" role="alert">
|
||||
<i class="bi bi-exclamation-circle me-2"></i>
|
||||
<strong>${message}</strong>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize dashboard
|
||||
*/
|
||||
function init() {
|
||||
initTooltips();
|
||||
|
||||
// Bind export button
|
||||
const exportBtn = document.getElementById('btn-export-md');
|
||||
if (exportBtn) {
|
||||
exportBtn.addEventListener('click', copyMarkdown);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
|
||||
// Expose functions globally for debugging
|
||||
window.roiApuDashboard = {
|
||||
copyMarkdown: copyMarkdown,
|
||||
initTooltips: initTooltips
|
||||
};
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user