- 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>
875 lines
51 KiB
PHP
875 lines
51 KiB
PHP
<?php
|
|
/**
|
|
* Analytics Dashboard for ROI APU Search
|
|
*
|
|
* @package ROI_APU_Search
|
|
* @since 1.2.0
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
// Prevent direct access
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Main dashboard class for search analytics
|
|
*/
|
|
final class ROI_APU_Analytics_Dashboard
|
|
{
|
|
/**
|
|
* Singleton instance
|
|
*/
|
|
private static ?self $instance = null;
|
|
|
|
/**
|
|
* Menu slug
|
|
*/
|
|
private const MENU_SLUG = 'roi-apu-analytics';
|
|
|
|
/**
|
|
* Get singleton instance
|
|
*/
|
|
public static function get_instance(): self
|
|
{
|
|
if (self::$instance === null) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct()
|
|
{
|
|
// Private constructor for singleton
|
|
}
|
|
|
|
/**
|
|
* Initialize the dashboard
|
|
*/
|
|
public function init(): void
|
|
{
|
|
add_action('admin_menu', [$this, 'register_menu']);
|
|
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
|
}
|
|
|
|
/**
|
|
* Enqueue dashboard assets (CSS/JS)
|
|
*
|
|
* @param string $hook Current admin page hook
|
|
*/
|
|
public function enqueue_assets(string $hook): void
|
|
{
|
|
// Only load on our dashboard page
|
|
if (strpos($hook, self::MENU_SLUG) === false) {
|
|
return;
|
|
}
|
|
|
|
// Google Fonts - Poppins
|
|
wp_enqueue_style(
|
|
'poppins-font',
|
|
'https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap',
|
|
[],
|
|
null
|
|
);
|
|
|
|
// Bootstrap 5 CSS
|
|
wp_enqueue_style(
|
|
'bootstrap',
|
|
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css',
|
|
[],
|
|
'5.3.2'
|
|
);
|
|
|
|
// Bootstrap Icons
|
|
wp_enqueue_style(
|
|
'bootstrap-icons',
|
|
'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css',
|
|
[],
|
|
'1.11.3'
|
|
);
|
|
|
|
// Dashboard CSS
|
|
wp_enqueue_style(
|
|
'roi-apu-dashboard',
|
|
ROI_APU_SEARCH_PLUGIN_URL . 'admin/assets/dashboard.css',
|
|
['bootstrap', 'bootstrap-icons'],
|
|
ROI_APU_SEARCH_VERSION
|
|
);
|
|
|
|
// Bootstrap 5 JS Bundle (includes Popper)
|
|
wp_enqueue_script(
|
|
'bootstrap',
|
|
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js',
|
|
[],
|
|
'5.3.2',
|
|
true
|
|
);
|
|
|
|
// Dashboard JS
|
|
wp_enqueue_script(
|
|
'roi-apu-dashboard',
|
|
ROI_APU_SEARCH_PLUGIN_URL . 'admin/assets/dashboard.js',
|
|
['bootstrap'],
|
|
ROI_APU_SEARCH_VERSION,
|
|
true
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Register admin menu page
|
|
*/
|
|
public function register_menu(): void
|
|
{
|
|
add_menu_page(
|
|
__('APU Analytics', 'roi-apu-search'), // Page title
|
|
__('APU Analytics', 'roi-apu-search'), // Menu title
|
|
'manage_options', // Capability
|
|
self::MENU_SLUG, // Menu slug
|
|
[$this, 'render_page'], // Callback
|
|
'dashicons-chart-bar', // Icon
|
|
30 // Position
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Render the dashboard page
|
|
*/
|
|
public function render_page(): void
|
|
{
|
|
// Check user capabilities
|
|
if (!current_user_can('manage_options')) {
|
|
return;
|
|
}
|
|
|
|
// Get metrics from repository
|
|
$db = ROI_APU_Search_DB::get_instance();
|
|
$repository = new ROI_APU_Metrics_Repository($db->get_pdo(), $db->get_prefix());
|
|
|
|
// Default period: 30 days
|
|
$days = 30;
|
|
|
|
// KPIs (v1)
|
|
$kpis = $repository->getKPIs($days);
|
|
$ctr_zero = $repository->getCTRZero($days, 10);
|
|
$infraposicionados = $repository->getInfraposicionados($days, 3, 5, 10);
|
|
|
|
// v2 Recommendations
|
|
$zero_results = $repository->getZeroResults($days, 10);
|
|
$quick_wins = $repository->getQuickWins($days, 10);
|
|
$contenido_estrella = $repository->getContenidoEstrella($days, 10);
|
|
$decay_content = $repository->getDecayContent($days, 10);
|
|
$click_distribution = $repository->getClickDistribution($days);
|
|
|
|
// v2 Paginated tables (first page)
|
|
$top_searches = $repository->getTopSearches($days, 20);
|
|
$top_clicks = $repository->getTopClicks($days, 20);
|
|
$all_zero_results = $repository->getZeroResults($days, 20);
|
|
$total_counts = $repository->getTotalCounts($days);
|
|
|
|
// Site URL for building permalinks
|
|
$site_url = rtrim(home_url(), '/');
|
|
|
|
// Format numbers for display
|
|
$total_busquedas = number_format($kpis['total_busquedas']);
|
|
$ctr = number_format($kpis['ctr'], 1) . '%';
|
|
$sin_resultados = number_format($kpis['pct_sin_resultados'], 1) . '%';
|
|
$pos_prom = number_format($kpis['pos_prom'], 1);
|
|
|
|
?>
|
|
<div class="dashboard-analytics-wrap">
|
|
<div class="container-fluid py-4" style="max-width: 1400px;">
|
|
|
|
<!-- Alert Container for JS messages -->
|
|
<div id="alertContainer"></div>
|
|
|
|
<!-- Header with navy gradient -->
|
|
<div class="rounded p-4 mb-4 shadow text-white"
|
|
style="background: linear-gradient(135deg, #0E2337 0%, #1e3a5f 100%);
|
|
border-left: 4px solid #FF8600;">
|
|
<div class="d-flex align-items-center justify-content-between flex-wrap gap-3">
|
|
<div>
|
|
<h4 class="mb-1 d-flex align-items-center gap-2">
|
|
<i class="bi bi-bar-chart-line" style="color: #FF8600;"></i>
|
|
<?php esc_html_e('Analytics del Buscador', 'roi-apu-search'); ?>
|
|
</h4>
|
|
<small class="text-white-50">
|
|
<?php esc_html_e('Métricas y oportunidades de mejora', 'roi-apu-search'); ?>
|
|
</small>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<select class="form-select form-select-sm"
|
|
id="period-selector"
|
|
style="width: auto;">
|
|
<option value="30"><?php esc_html_e('Últimos 30 días', 'roi-apu-search'); ?></option>
|
|
<option value="7"><?php esc_html_e('Últimos 7 días', 'roi-apu-search'); ?></option>
|
|
<option value="90"><?php esc_html_e('Últimos 90 días', 'roi-apu-search'); ?></option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Loading State -->
|
|
<div id="loadingState" class="text-center py-5">
|
|
<div class="spinner-border" style="color: #FF8600;" role="status">
|
|
<span class="visually-hidden"><?php esc_html_e('Cargando...', 'roi-apu-search'); ?></span>
|
|
</div>
|
|
<p class="mt-2 text-muted"><?php esc_html_e('Cargando métricas...', 'roi-apu-search'); ?></p>
|
|
</div>
|
|
|
|
<!-- Dashboard Content -->
|
|
<div id="dashboardContent">
|
|
|
|
<!-- KPIs Cards with Trends -->
|
|
<?php
|
|
$trends = $kpis['trends'];
|
|
// Helper function for trend display
|
|
$getTrendHtml = function($value, $inverse = false) {
|
|
if ($value == 0) return '';
|
|
$isPositive = $inverse ? $value < 0 : $value > 0;
|
|
$icon = $isPositive ? 'bi-arrow-up' : 'bi-arrow-down';
|
|
$color = $isPositive ? '#22c55e' : '#ef4444';
|
|
$sign = $value > 0 ? '+' : '';
|
|
return sprintf(
|
|
'<small class="d-block mt-1" style="font-size: 0.7rem;"><i class="bi %s" style="color: %s;"></i> %s%s%% vs anterior</small>',
|
|
$icon,
|
|
$color,
|
|
$sign,
|
|
number_format($value, 1)
|
|
);
|
|
};
|
|
?>
|
|
<div class="row g-3 mb-4">
|
|
<!-- KPI 1: Búsquedas -->
|
|
<div class="col-6 col-md-3">
|
|
<div class="card shadow-sm text-white" style="background-color: #FF8600;">
|
|
<div class="card-body text-center py-3">
|
|
<h2 class="mb-0 fw-bold"
|
|
data-bs-toggle="tooltip"
|
|
data-bs-placement="top"
|
|
title="<?php esc_attr_e('Total de búsquedas realizadas en el período', 'roi-apu-search'); ?>">
|
|
<?php echo esc_html($total_busquedas); ?>
|
|
</h2>
|
|
<small><i class="bi bi-search me-1"></i><?php esc_html_e('Búsquedas', 'roi-apu-search'); ?></small>
|
|
<?php echo $getTrendHtml($trends['busquedas']); ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- KPI 2: CTR -->
|
|
<div class="col-6 col-md-3">
|
|
<div class="card shadow-sm text-white" style="background-color: #1e3a5f;">
|
|
<div class="card-body text-center py-3">
|
|
<h2 class="mb-0 fw-bold"
|
|
data-bs-toggle="tooltip"
|
|
data-bs-placement="top"
|
|
title="<?php esc_attr_e('Click-Through Rate: % de búsquedas que resultan en un click', 'roi-apu-search'); ?>">
|
|
<?php echo esc_html($ctr); ?>
|
|
</h2>
|
|
<small><i class="bi bi-hand-index me-1"></i><?php esc_html_e('CTR', 'roi-apu-search'); ?></small>
|
|
<?php echo $getTrendHtml($trends['ctr']); ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- KPI 3: Sin Resultados -->
|
|
<div class="col-6 col-md-3">
|
|
<div class="card shadow-sm" style="background-color: #f8f9fa; border-left: 3px solid #dc3545;">
|
|
<div class="card-body text-center py-3">
|
|
<h2 class="mb-0 fw-bold text-danger"
|
|
data-bs-toggle="tooltip"
|
|
data-bs-placement="top"
|
|
title="<?php esc_attr_e('% de búsquedas que no encontraron resultados', 'roi-apu-search'); ?>">
|
|
<?php echo esc_html($sin_resultados); ?>
|
|
</h2>
|
|
<small class="text-muted"><i class="bi bi-x-circle me-1"></i><?php esc_html_e('Sin Resultados', 'roi-apu-search'); ?></small>
|
|
<?php echo $getTrendHtml($trends['sin_resultados'], true); ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- KPI 4: Posición Promedio -->
|
|
<div class="col-6 col-md-3">
|
|
<div class="card shadow-sm" style="background-color: #f8f9fa; border-left: 3px solid #1e3a5f;">
|
|
<div class="card-body text-center py-3">
|
|
<h2 class="mb-0 fw-bold" style="color: #1e3a5f;"
|
|
data-bs-toggle="tooltip"
|
|
data-bs-placement="top"
|
|
title="<?php esc_attr_e('Posición promedio de los resultados clickeados', 'roi-apu-search'); ?>">
|
|
<?php echo esc_html($pos_prom); ?>
|
|
</h2>
|
|
<small class="text-muted"><i class="bi bi-sort-numeric-down me-1"></i><?php esc_html_e('Pos. Prom.', 'roi-apu-search'); ?></small>
|
|
<?php echo $getTrendHtml($trends['pos_prom'], true); ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════════ RECOMENDACIONES ACCIONABLES ═══════════════ -->
|
|
|
|
<!-- 🔴 ACCIÓN URGENTE: Contenido a Crear -->
|
|
<?php if (!empty($zero_results)) : ?>
|
|
<div class="recommendation-card mb-4" role="alert"
|
|
style="border-left: 4px solid #ef4444; background-color: #fef2f2; border-radius: 0.5rem; padding: 1rem;">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<h6 class="mb-0 d-flex align-items-center gap-2" style="color: #7f1d1d;">
|
|
<i class="bi bi-exclamation-triangle-fill"></i>
|
|
<?php esc_html_e('ACCIÓN URGENTE: Contenido a Crear', 'roi-apu-search'); ?>
|
|
</h6>
|
|
<span class="badge" style="background-color: #ef4444;"><?php echo count($zero_results); ?></span>
|
|
</div>
|
|
<p class="small mb-2" style="color: #991b1b;">
|
|
<?php esc_html_e('Los usuarios buscan esto pero NO encuentran nada. ¡Crea este contenido!', 'roi-apu-search'); ?>
|
|
</p>
|
|
<div class="small">
|
|
<?php foreach ($zero_results as $index => $term) : ?>
|
|
<div class="d-flex justify-content-between py-1 <?php echo $index < count($zero_results) - 1 ? 'border-bottom' : ''; ?>" style="border-color: #fecaca !important;">
|
|
<span style="color: #7f1d1d;">
|
|
<strong><?php echo esc_html($term['q_term']); ?></strong>
|
|
</span>
|
|
<span style="color: #991b1b;">
|
|
<?php echo esc_html(number_format((int)$term['frecuencia'])); ?> <?php esc_html_e('veces buscado', 'roi-apu-search'); ?>
|
|
</span>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- 🟡 REVISAR: Títulos con CTR 0% -->
|
|
<?php if (!empty($ctr_zero)) : ?>
|
|
<div class="recommendation-card mb-4" role="alert"
|
|
style="border-left: 4px solid #f59e0b; background-color: #fffbeb; border-radius: 0.5rem; padding: 1rem;">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<h6 class="mb-0 d-flex align-items-center gap-2" style="color: #78350f;">
|
|
<i class="bi bi-pencil-square"></i>
|
|
<?php esc_html_e('REVISAR: Títulos con CTR 0%', 'roi-apu-search'); ?>
|
|
</h6>
|
|
<span class="badge" style="background-color: #f59e0b;"><?php echo count($ctr_zero); ?></span>
|
|
</div>
|
|
<p class="small mb-2" style="color: #92400e;">
|
|
<?php esc_html_e('Estos términos tienen resultados pero nadie hace click. Mejora títulos y descripciones.', 'roi-apu-search'); ?>
|
|
</p>
|
|
<div class="small">
|
|
<?php foreach ($ctr_zero as $index => $term) : ?>
|
|
<div class="d-flex justify-content-between py-1 <?php echo $index < count($ctr_zero) - 1 ? 'border-bottom' : ''; ?>" style="border-color: #fde68a !important;">
|
|
<span style="color: #78350f;">
|
|
<strong><?php echo esc_html($term['q_term']); ?></strong>
|
|
<small>(<?php echo esc_html(number_format((int)$term['resultados'])); ?> <?php esc_html_e('resultados', 'roi-apu-search'); ?>)</small>
|
|
</span>
|
|
<span style="color: #92400e;">
|
|
<?php echo esc_html(number_format((int)$term['busquedas'])); ?> <?php esc_html_e('búsquedas', 'roi-apu-search'); ?> → 0 clicks
|
|
</span>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- 🎯 QUICK WINS: Oportunidades Fáciles -->
|
|
<?php if (!empty($quick_wins)) : ?>
|
|
<div class="recommendation-card mb-4" role="alert"
|
|
style="border-left: 4px solid #0284c7; background-color: #e7f3ff; border-radius: 0.5rem; padding: 1rem;">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<h6 class="mb-0 d-flex align-items-center gap-2" style="color: #0c4a6e;">
|
|
<i class="bi bi-bullseye"></i>
|
|
<?php esc_html_e('QUICK WINS: Oportunidades Fáciles', 'roi-apu-search'); ?>
|
|
</h6>
|
|
<span class="badge" style="background-color: #0284c7;"><?php echo count($quick_wins); ?></span>
|
|
</div>
|
|
<p class="small mb-2" style="color: #075985;">
|
|
<?php esc_html_e('Términos con clicks que no están en top 1. Una pequeña mejora = más visibilidad.', 'roi-apu-search'); ?>
|
|
</p>
|
|
<div class="small">
|
|
<?php foreach ($quick_wins as $index => $term) : ?>
|
|
<div class="d-flex justify-content-between py-1 <?php echo $index < count($quick_wins) - 1 ? 'border-bottom' : ''; ?>" style="border-color: #bae6fd !important;">
|
|
<span style="color: #0c4a6e;">
|
|
<strong><?php echo esc_html($term['q_term']); ?></strong>
|
|
</span>
|
|
<span style="color: #075985;">
|
|
<?php esc_html_e('Pos.', 'roi-apu-search'); ?> <?php echo esc_html($term['pos_prom']); ?>,
|
|
CTR <?php echo esc_html($term['ctr']); ?>%
|
|
</span>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- 📉 ATENCIÓN: Contenido en Decadencia -->
|
|
<?php if (!empty($decay_content)) : ?>
|
|
<div class="card mb-4 shadow-sm" style="border-left: 4px solid #6b7280;">
|
|
<div class="card-header d-flex justify-content-between align-items-center" style="background-color: #6b7280; color: white;">
|
|
<h6 class="mb-0 d-flex align-items-center gap-2">
|
|
<i class="bi bi-graph-down"></i>
|
|
<?php esc_html_e('ATENCIÓN: Contenido en Decadencia', 'roi-apu-search'); ?>
|
|
</h6>
|
|
<span class="badge bg-light text-dark"><?php echo count($decay_content); ?></span>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<p class="small text-muted px-3 pt-3 mb-2">
|
|
<?php esc_html_e('Posts que perdieron >20% clicks vs período anterior. Revisa si están desactualizados.', 'roi-apu-search'); ?>
|
|
</p>
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th style="width: 50%;"><?php esc_html_e('Título', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Cambio', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Antes', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Ahora', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Acciones', 'roi-apu-search'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($decay_content as $post) : ?>
|
|
<tr>
|
|
<td>
|
|
<strong><?php echo esc_html(mb_strimwidth($post['post_title'], 0, 80, '...')); ?></strong>
|
|
</td>
|
|
<td class="text-center">
|
|
<span class="badge bg-danger"><?php echo esc_html($post['cambio_pct']); ?>%</span>
|
|
</td>
|
|
<td class="text-center"><?php echo esc_html($post['clicks_anterior']); ?></td>
|
|
<td class="text-center"><?php echo esc_html($post['clicks_actual']); ?></td>
|
|
<td class="text-center">
|
|
<div class="btn-group btn-group-sm" role="group">
|
|
<a href="<?php echo esc_url(admin_url('post.php?post=' . $post['post_id'] . '&action=edit')); ?>" target="_blank" class="btn btn-outline-primary" title="<?php esc_attr_e('Editar', 'roi-apu-search'); ?>">
|
|
<i class="bi bi-pencil"></i>
|
|
</a>
|
|
<a href="<?php echo esc_url($site_url . '/' . $post['post_name'] . '/'); ?>" target="_blank" class="btn btn-outline-secondary" title="<?php esc_attr_e('Ver', 'roi-apu-search'); ?>">
|
|
<i class="bi bi-box-arrow-up-right"></i>
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- 🟢 MANTENER: Tu Contenido Estrella -->
|
|
<?php if (!empty($contenido_estrella)) : ?>
|
|
<div class="card mb-4 shadow-sm" style="border-left: 4px solid #22c55e;">
|
|
<div class="card-header d-flex justify-content-between align-items-center" style="background-color: #22c55e; color: white;">
|
|
<h6 class="mb-0 d-flex align-items-center gap-2">
|
|
<i class="bi bi-star-fill"></i>
|
|
<?php esc_html_e('MANTENER: Tu Contenido Estrella', 'roi-apu-search'); ?>
|
|
</h6>
|
|
<span class="badge bg-light text-dark"><?php echo count($contenido_estrella); ?></span>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<p class="small text-muted px-3 pt-3 mb-2">
|
|
<?php esc_html_e('Posts con más clicks. Mantén este contenido actualizado y optimizado.', 'roi-apu-search'); ?>
|
|
</p>
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th style="width: 55%;"><?php esc_html_e('Título', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Clicks', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Pos. Prom.', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Acciones', 'roi-apu-search'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($contenido_estrella as $post) : ?>
|
|
<tr>
|
|
<td>
|
|
<strong><?php echo esc_html(mb_strimwidth($post['post_title'], 0, 80, '...')); ?></strong>
|
|
</td>
|
|
<td class="text-center">
|
|
<span class="badge bg-success"><?php echo esc_html(number_format((int)$post['clicks'])); ?></span>
|
|
</td>
|
|
<td class="text-center"><?php echo esc_html($post['pos_prom']); ?></td>
|
|
<td class="text-center">
|
|
<div class="btn-group btn-group-sm" role="group">
|
|
<a href="<?php echo esc_url(admin_url('post.php?post=' . $post['post_id'] . '&action=edit')); ?>" target="_blank" class="btn btn-outline-primary" title="<?php esc_attr_e('Editar', 'roi-apu-search'); ?>">
|
|
<i class="bi bi-pencil"></i>
|
|
</a>
|
|
<a href="<?php echo esc_url($site_url . '/' . $post['post_name'] . '/'); ?>" target="_blank" class="btn btn-outline-success" title="<?php esc_attr_e('Ver', 'roi-apu-search'); ?>">
|
|
<i class="bi bi-box-arrow-up-right"></i>
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Posts infraposicionados -->
|
|
<?php if (!empty($infraposicionados)) : ?>
|
|
<div class="card mb-4 shadow-sm" style="border-left: 4px solid #f59e0b;">
|
|
<div class="card-header d-flex justify-content-between align-items-center" style="background-color: #f59e0b; color: white;">
|
|
<h6 class="mb-0 d-flex align-items-center gap-2">
|
|
<i class="bi bi-arrow-up-circle"></i>
|
|
<?php esc_html_e('OPORTUNIDAD: Posts Infraposicionados', 'roi-apu-search'); ?>
|
|
</h6>
|
|
<span class="badge bg-light text-dark"><?php echo count($infraposicionados); ?></span>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<p class="small text-muted px-3 pt-3 mb-2">
|
|
<?php esc_html_e('Estos posts reciben clicks pero aparecen muy abajo. Considera mejorar su scoring.', 'roi-apu-search'); ?>
|
|
</p>
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th style="width: 50%;"><?php esc_html_e('Título', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Clicks', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Pos. Prom.', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Acciones', 'roi-apu-search'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($infraposicionados as $post) : ?>
|
|
<tr>
|
|
<td>
|
|
<strong><?php echo esc_html(mb_strimwidth($post['post_title'], 0, 80, '...')); ?></strong>
|
|
</td>
|
|
<td class="text-center">
|
|
<span class="badge bg-warning text-dark"><?php echo esc_html(number_format((int)$post['clicks'])); ?></span>
|
|
</td>
|
|
<td class="text-center">
|
|
<span class="badge bg-secondary"><?php echo esc_html($post['pos_prom']); ?></span>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="btn-group btn-group-sm" role="group">
|
|
<a href="<?php echo esc_url(admin_url('post.php?post=' . $post['post_id'] . '&action=edit')); ?>" target="_blank" class="btn btn-outline-primary" title="<?php esc_attr_e('Editar', 'roi-apu-search'); ?>">
|
|
<i class="bi bi-pencil"></i>
|
|
</a>
|
|
<a href="<?php echo esc_url($site_url . '/' . $post['post_name'] . '/'); ?>" target="_blank" class="btn btn-outline-warning" title="<?php esc_attr_e('Ver', 'roi-apu-search'); ?>">
|
|
<i class="bi bi-box-arrow-up-right"></i>
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- ═══════════════ DATOS DETALLADOS ═══════════════ -->
|
|
<div class="mt-5 mb-4">
|
|
<h5 class="text-muted d-flex align-items-center gap-2">
|
|
<i class="bi bi-table"></i>
|
|
<?php esc_html_e('Datos Detallados', 'roi-apu-search'); ?>
|
|
</h5>
|
|
<hr class="mt-2">
|
|
</div>
|
|
|
|
<!-- 📊 Click Distribution -->
|
|
<?php if (!empty($click_distribution)) : ?>
|
|
<div class="card mb-4 shadow-sm">
|
|
<div class="card-header" style="background-color: #0E2337; color: white;">
|
|
<h6 class="mb-0 d-flex align-items-center gap-2">
|
|
<i class="bi bi-bar-chart-fill"></i>
|
|
<?php esc_html_e('Distribución de Clicks por Posición', 'roi-apu-search'); ?>
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<?php
|
|
$position_colors = [
|
|
'Pos 1' => '#FF8600',
|
|
'Pos 2' => '#1e3a5f',
|
|
'Pos 3' => '#2c5282',
|
|
'Pos 4' => '#3d6894',
|
|
'Pos 5' => '#0E2337',
|
|
'Pos 6+' => '#6b7280',
|
|
];
|
|
foreach ($click_distribution as $dist) :
|
|
$color = $position_colors[$dist['posicion']] ?? '#6b7280';
|
|
?>
|
|
<div class="mb-2">
|
|
<div class="d-flex justify-content-between mb-1">
|
|
<small><strong><?php echo esc_html($dist['posicion']); ?></strong></small>
|
|
<small><?php echo esc_html($dist['clicks']); ?> clicks (<?php echo esc_html($dist['porcentaje']); ?>%)</small>
|
|
</div>
|
|
<div class="progress" style="height: 20px;">
|
|
<div class="progress-bar" role="progressbar"
|
|
style="width: <?php echo esc_attr($dist['porcentaje']); ?>%; background-color: <?php echo esc_attr($color); ?>;"
|
|
aria-valuenow="<?php echo esc_attr($dist['porcentaje']); ?>"
|
|
aria-valuemin="0"
|
|
aria-valuemax="100">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- 📊 Top Búsquedas Table -->
|
|
<div class="card mb-4 shadow-sm">
|
|
<div class="card-header d-flex justify-content-between align-items-center" style="background-color: #1e3a5f; color: white;">
|
|
<h6 class="mb-0 d-flex align-items-center gap-2">
|
|
<i class="bi bi-search"></i>
|
|
<?php esc_html_e('Top Búsquedas', 'roi-apu-search'); ?>
|
|
</h6>
|
|
<span class="badge bg-light text-dark"><?php echo esc_html($total_counts['total_searches']); ?> <?php esc_html_e('términos', 'roi-apu-search'); ?></span>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th><?php esc_html_e('Término', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Búsquedas', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Clicks', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('CTR', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Resultados', 'roi-apu-search'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="top-searches-body">
|
|
<?php foreach ($top_searches as $search) : ?>
|
|
<tr>
|
|
<td><strong><?php echo esc_html($search['q_term']); ?></strong></td>
|
|
<td class="text-center"><?php echo esc_html(number_format((int)$search['busquedas'])); ?></td>
|
|
<td class="text-center"><?php echo esc_html(number_format((int)$search['clicks'])); ?></td>
|
|
<td class="text-center">
|
|
<span class="badge <?php echo (float)$search['ctr'] > 0 ? 'bg-success' : 'bg-secondary'; ?>">
|
|
<?php echo esc_html($search['ctr']); ?>%
|
|
</span>
|
|
</td>
|
|
<td class="text-center"><?php echo esc_html(number_format((int)$search['resultados'])); ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<?php if ($total_counts['total_searches'] > 20) : ?>
|
|
<div class="card-footer text-center">
|
|
<button class="btn btn-sm btn-outline-secondary" id="load-more-searches" data-offset="20">
|
|
<i class="bi bi-plus-circle me-1"></i>
|
|
<?php esc_html_e('Cargar más', 'roi-apu-search'); ?>
|
|
</button>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- 📄 Top Posts Clickeados Table -->
|
|
<div class="card mb-4 shadow-sm">
|
|
<div class="card-header d-flex justify-content-between align-items-center" style="background-color: #FF8600; color: white;">
|
|
<h6 class="mb-0 d-flex align-items-center gap-2">
|
|
<i class="bi bi-file-earmark-text"></i>
|
|
<?php esc_html_e('Top Posts Clickeados', 'roi-apu-search'); ?>
|
|
</h6>
|
|
<span class="badge bg-light text-dark"><?php echo esc_html($total_counts['total_clicks']); ?> <?php esc_html_e('posts', 'roi-apu-search'); ?></span>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th><?php esc_html_e('Título', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Clicks', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Pos. Prom.', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Acciones', 'roi-apu-search'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="top-clicks-body">
|
|
<?php foreach ($top_clicks as $post) : ?>
|
|
<tr>
|
|
<td>
|
|
<strong><?php echo esc_html(mb_strimwidth($post['post_title'], 0, 80, '...')); ?></strong>
|
|
<br><small class="text-muted">/<?php echo esc_html($post['post_name']); ?>/</small>
|
|
</td>
|
|
<td class="text-center"><?php echo esc_html(number_format((int)$post['clicks'])); ?></td>
|
|
<td class="text-center">
|
|
<span class="badge <?php echo (float)$post['pos_prom'] <= 3 ? 'bg-success' : ((float)$post['pos_prom'] <= 5 ? 'bg-warning text-dark' : 'bg-secondary'); ?>">
|
|
<?php echo esc_html($post['pos_prom']); ?>
|
|
</span>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="btn-group btn-group-sm" role="group">
|
|
<a href="<?php echo esc_url(admin_url('post.php?post=' . $post['post_id'] . '&action=edit')); ?>" target="_blank" class="btn btn-outline-primary" title="<?php esc_attr_e('Editar', 'roi-apu-search'); ?>">
|
|
<i class="bi bi-pencil"></i>
|
|
</a>
|
|
<a href="<?php echo esc_url($site_url . '/' . $post['post_name'] . '/'); ?>" target="_blank" class="btn btn-outline-secondary" title="<?php esc_attr_e('Ver', 'roi-apu-search'); ?>">
|
|
<i class="bi bi-box-arrow-up-right"></i>
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<?php if ($total_counts['total_clicks'] > 20) : ?>
|
|
<div class="card-footer text-center">
|
|
<button class="btn btn-sm btn-outline-secondary" id="load-more-clicks" data-offset="20">
|
|
<i class="bi bi-plus-circle me-1"></i>
|
|
<?php esc_html_e('Cargar más', 'roi-apu-search'); ?>
|
|
</button>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- ❌ Búsquedas Sin Resultados Table -->
|
|
<?php if (!empty($all_zero_results)) : ?>
|
|
<div class="card mb-4 shadow-sm">
|
|
<div class="card-header d-flex justify-content-between align-items-center" style="background-color: #ef4444; color: white;">
|
|
<h6 class="mb-0 d-flex align-items-center gap-2">
|
|
<i class="bi bi-x-circle"></i>
|
|
<?php esc_html_e('Búsquedas Sin Resultados', 'roi-apu-search'); ?>
|
|
</h6>
|
|
<span class="badge bg-light text-dark"><?php echo esc_html($total_counts['total_zero_results']); ?> <?php esc_html_e('términos', 'roi-apu-search'); ?></span>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th><?php esc_html_e('Término', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Frecuencia', 'roi-apu-search'); ?></th>
|
|
<th class="text-center"><?php esc_html_e('Última búsqueda', 'roi-apu-search'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="zero-results-body">
|
|
<?php foreach ($all_zero_results as $term) : ?>
|
|
<tr>
|
|
<td><strong><?php echo esc_html($term['q_term']); ?></strong></td>
|
|
<td class="text-center">
|
|
<span class="badge bg-danger"><?php echo esc_html(number_format((int)$term['frecuencia'])); ?></span>
|
|
</td>
|
|
<td class="text-center">
|
|
<small class="text-muted"><?php echo esc_html(date('d/m/Y H:i', strtotime($term['ultima_busqueda']))); ?></small>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<?php if ($total_counts['total_zero_results'] > 20) : ?>
|
|
<div class="card-footer text-center">
|
|
<button class="btn btn-sm btn-outline-secondary" id="load-more-zero" data-offset="20">
|
|
<i class="bi bi-plus-circle me-1"></i>
|
|
<?php esc_html_e('Cargar más', 'roi-apu-search'); ?>
|
|
</button>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Export Card -->
|
|
<div class="card export-card mt-4 shadow-sm">
|
|
<div class="card-body text-center py-4">
|
|
<i class="bi bi-file-earmark-text fs-1 text-muted mb-2 d-block"></i>
|
|
<h6 class="mb-2"><?php esc_html_e('Exportar resumen para análisis IA', 'roi-apu-search'); ?></h6>
|
|
<p class="text-muted small mb-3">
|
|
<?php esc_html_e('Genera un Markdown completo con todas las recomendaciones', 'roi-apu-search'); ?>
|
|
</p>
|
|
<button class="btn text-white"
|
|
style="background-color: #FF8600;"
|
|
id="btn-export-md">
|
|
<i class="bi bi-clipboard me-2"></i>
|
|
<?php esc_html_e('Copiar Markdown', 'roi-apu-search'); ?>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
window.roiApuDashboardData = {
|
|
period: <?php echo (int) $days; ?>,
|
|
generated: '<?php echo esc_js(current_time('Y-m-d H:i')); ?>',
|
|
siteUrl: '<?php echo esc_js($site_url); ?>',
|
|
kpis: {
|
|
totalBusquedas: '<?php echo esc_js($total_busquedas); ?>',
|
|
ctr: '<?php echo esc_js($ctr); ?>',
|
|
sinResultados: '<?php echo esc_js($sin_resultados); ?>',
|
|
posProm: '<?php echo esc_js($pos_prom); ?>'
|
|
},
|
|
// v2: Urgent - Zero Results
|
|
zeroResults: <?php echo wp_json_encode(array_map(function($term) {
|
|
return [
|
|
'term' => $term['q_term'],
|
|
'frecuencia' => (int) $term['frecuencia']
|
|
];
|
|
}, $zero_results)); ?>,
|
|
// v2: CTR Zero
|
|
ctrZero: <?php echo wp_json_encode(array_map(function($term) {
|
|
return [
|
|
'term' => $term['q_term'],
|
|
'busquedas' => (int) $term['busquedas'],
|
|
'resultados' => (int) $term['resultados']
|
|
];
|
|
}, $ctr_zero)); ?>,
|
|
// v2: Quick Wins
|
|
quickWins: <?php echo wp_json_encode(array_map(function($term) {
|
|
return [
|
|
'term' => $term['q_term'],
|
|
'busquedas' => (int) $term['busquedas'],
|
|
'ctr' => (float) $term['ctr'],
|
|
'posProm' => (float) $term['pos_prom']
|
|
];
|
|
}, $quick_wins)); ?>,
|
|
// v2: Contenido Estrella
|
|
contenidoEstrella: <?php echo wp_json_encode(array_map(function($post) use ($site_url) {
|
|
return [
|
|
'postId' => (int) $post['post_id'],
|
|
'title' => $post['post_title'],
|
|
'url' => $site_url . '/' . $post['post_name'] . '/',
|
|
'clicks' => (int) $post['clicks'],
|
|
'posProm' => (float) $post['pos_prom']
|
|
];
|
|
}, $contenido_estrella)); ?>,
|
|
// v2: Decay Content
|
|
decayContent: <?php echo wp_json_encode(array_map(function($post) use ($site_url) {
|
|
return [
|
|
'postId' => (int) $post['post_id'],
|
|
'title' => $post['post_title'],
|
|
'url' => $site_url . '/' . $post['post_name'] . '/',
|
|
'clicksActual' => (int) $post['clicks_actual'],
|
|
'clicksAnterior' => (int) $post['clicks_anterior'],
|
|
'cambioPct' => (float) $post['cambio_pct']
|
|
];
|
|
}, $decay_content)); ?>,
|
|
// v2: Click Distribution
|
|
clickDistribution: <?php echo wp_json_encode(array_map(function($dist) {
|
|
return [
|
|
'posicion' => $dist['posicion'],
|
|
'clicks' => (int) $dist['clicks'],
|
|
'porcentaje' => (float) $dist['porcentaje']
|
|
];
|
|
}, $click_distribution)); ?>,
|
|
// Legacy
|
|
infraposicionados: <?php echo wp_json_encode(array_map(function($post) {
|
|
return [
|
|
'postId' => (int) $post['post_id'],
|
|
'title' => $post['post_title'],
|
|
'clicks' => (int) $post['clicks'],
|
|
'posProm' => (float) $post['pos_prom']
|
|
];
|
|
}, $infraposicionados)); ?>,
|
|
// Totals for pagination
|
|
totals: {
|
|
searches: <?php echo (int) $total_counts['total_searches']; ?>,
|
|
clicks: <?php echo (int) $total_counts['total_clicks']; ?>,
|
|
zeroResults: <?php echo (int) $total_counts['total_zero_results']; ?>
|
|
}
|
|
};
|
|
// Hide loading, show content
|
|
document.getElementById('loadingState').style.display = 'none';
|
|
</script>
|
|
<?php
|
|
}
|
|
}
|