Files
roi-apu-search/admin/class-analytics-dashboard.php
FrankZamora e92d2018b1 style(analytics): Convertir Distribución de Clicks a tabla
- Convertir visualización de barras de progreso a tabla estructurada
- Columnas: Posición, Clicks (badge), Porcentaje, Distribución (barra)
- Agregar badge con total de clicks en header
- Expandir colores para posiciones 1-10 individuales

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 21:57:49 -06:00

934 lines
56 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="card mb-4 shadow-sm" style="border-left: 4px solid #ef4444;">
<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-exclamation-triangle-fill"></i>
<?php esc_html_e('ACCIÓN URGENTE: Contenido a Crear', 'roi-apu-search'); ?>
</h6>
<span class="badge bg-light text-dark"><?php echo count($zero_results); ?></span>
</div>
<div class="card-body p-0">
<p class="small text-muted px-3 pt-3 mb-2">
<?php esc_html_e('Los usuarios buscan esto pero NO encuentran nada. ¡Crea este contenido!', '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: 70%;"><?php esc_html_e('Término buscado', 'roi-apu-search'); ?></th>
<th class="text-center"><?php esc_html_e('Frecuencia', 'roi-apu-search'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($zero_results as $term) : ?>
<tr>
<td><strong><?php echo esc_html(mb_strimwidth($term['q_term'], 0, 80, '...')); ?></strong></td>
<td class="text-center">
<span class="badge bg-danger"><?php echo esc_html(number_format((int)$term['frecuencia'])); ?></span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php endif; ?>
<!-- 🟡 REVISAR: Títulos con CTR 0% -->
<?php if (!empty($ctr_zero)) : ?>
<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-pencil-square"></i>
<?php esc_html_e('REVISAR: Títulos con CTR 0%', 'roi-apu-search'); ?>
</h6>
<span class="badge bg-light text-dark"><?php echo count($ctr_zero); ?></span>
</div>
<div class="card-body p-0">
<p class="small text-muted px-3 pt-3 mb-2">
<?php esc_html_e('Estos términos tienen resultados pero nadie hace click. Mejora títulos y descripciones.', '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érmino buscado', 'roi-apu-search'); ?></th>
<th class="text-center"><?php esc_html_e('Resultados', '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>
</tr>
</thead>
<tbody>
<?php foreach ($ctr_zero as $term) : ?>
<tr>
<td><strong><?php echo esc_html(mb_strimwidth($term['q_term'], 0, 60, '...')); ?></strong></td>
<td class="text-center"><?php echo esc_html(number_format((int)$term['resultados'])); ?></td>
<td class="text-center">
<span class="badge bg-warning text-dark"><?php echo esc_html(number_format((int)$term['busquedas'])); ?></span>
</td>
<td class="text-center">
<span class="badge bg-danger">0</span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php endif; ?>
<!-- 🎯 QUICK WINS: Oportunidades Fáciles -->
<?php if (!empty($quick_wins)) : ?>
<div class="card mb-4 shadow-sm" style="border-left: 4px solid #0284c7;">
<div class="card-header d-flex justify-content-between align-items-center" style="background-color: #0284c7; color: white;">
<h6 class="mb-0 d-flex align-items-center gap-2">
<i class="bi bi-bullseye"></i>
<?php esc_html_e('QUICK WINS: Oportunidades Fáciles', 'roi-apu-search'); ?>
</h6>
<span class="badge bg-light text-dark"><?php echo count($quick_wins); ?></span>
</div>
<div class="card-body p-0">
<p class="small text-muted px-3 pt-3 mb-2">
<?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="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érmino buscado', '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('CTR', 'roi-apu-search'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($quick_wins as $term) : ?>
<tr>
<td><strong><?php echo esc_html(mb_strimwidth($term['q_term'], 0, 60, '...')); ?></strong></td>
<td class="text-center">
<span class="badge bg-secondary"><?php echo esc_html($term['pos_prom']); ?></span>
</td>
<td class="text-center">
<span class="badge bg-info text-dark"><?php echo esc_html($term['ctr']); ?>%</span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</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 d-flex justify-content-between align-items-center" 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>
<span class="badge bg-light text-dark"><?php echo esc_html(array_sum(array_column($click_distribution, 'clicks'))); ?> <?php esc_html_e('clicks totales', '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 style="width: 15%;"><?php esc_html_e('Posición', 'roi-apu-search'); ?></th>
<th class="text-center" style="width: 20%;"><?php esc_html_e('Clicks', 'roi-apu-search'); ?></th>
<th class="text-center" style="width: 15%;"><?php esc_html_e('Porcentaje', 'roi-apu-search'); ?></th>
<th style="width: 50%;"><?php esc_html_e('Distribución', 'roi-apu-search'); ?></th>
</tr>
</thead>
<tbody>
<?php
$position_colors = [
'Pos 1' => '#FF8600',
'Pos 2' => '#1e3a5f',
'Pos 3' => '#2c5282',
'Pos 4' => '#3d6894',
'Pos 5' => '#4a7eb0',
'Pos 6' => '#5a8ec0',
'Pos 7' => '#6a9ed0',
'Pos 8' => '#7aaee0',
'Pos 9' => '#8abef0',
'Pos 10' => '#9aceff',
'Pos 11+' => '#6b7280',
];
foreach ($click_distribution as $dist) :
$color = $position_colors[$dist['posicion']] ?? '#6b7280';
?>
<tr>
<td><strong><?php echo esc_html($dist['posicion']); ?></strong></td>
<td class="text-center">
<span class="badge" style="background-color: <?php echo esc_attr($color); ?>;">
<?php echo esc_html(number_format((int)$dist['clicks'])); ?>
</span>
</td>
<td class="text-center"><?php echo esc_html($dist['porcentaje']); ?>%</td>
<td>
<div class="progress" style="height: 18px;">
<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>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</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>
</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
}
}