'Unauthorized'], 403); } $page = isset($_POST['page']) ? absint($_POST['page']) : 1; $days = isset($_POST['days']) ? absint($_POST['days']) : 30; $offset = ($page - 1) * self::ITEMS_PER_PAGE; $db = ROI_APU_Search_DB::get_instance(); $repository = new ROI_APU_Metrics_Repository($db->get_pdo(), $db->get_prefix()); $data = $repository->getTopSearches($days, self::ITEMS_PER_PAGE, $offset); $total = $repository->getTotalCounts($days)['total_searches']; wp_send_json_success([ 'data' => $data, 'total' => $total, 'page' => $page, 'pages' => ceil($total / self::ITEMS_PER_PAGE), ]); } /** * AJAX handler for Top Clicks pagination */ public function ajax_paginate_clicks(): void { check_ajax_referer('roi_apu_dashboard', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Unauthorized'], 403); } $page = isset($_POST['page']) ? absint($_POST['page']) : 1; $days = isset($_POST['days']) ? absint($_POST['days']) : 30; $offset = ($page - 1) * self::ITEMS_PER_PAGE; $db = ROI_APU_Search_DB::get_instance(); $repository = new ROI_APU_Metrics_Repository($db->get_pdo(), $db->get_prefix()); $data = $repository->getTopClicks($days, self::ITEMS_PER_PAGE, $offset); $total = $repository->getTotalCounts($days)['total_clicks']; wp_send_json_success([ 'data' => $data, 'total' => $total, 'page' => $page, 'pages' => ceil($total / self::ITEMS_PER_PAGE), ]); } /** * AJAX handler for Zero Results pagination */ public function ajax_paginate_zero_results(): void { check_ajax_referer('roi_apu_dashboard', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Unauthorized'], 403); } $page = isset($_POST['page']) ? absint($_POST['page']) : 1; $days = isset($_POST['days']) ? absint($_POST['days']) : 30; $offset = ($page - 1) * self::ITEMS_PER_PAGE; $db = ROI_APU_Search_DB::get_instance(); $repository = new ROI_APU_Metrics_Repository($db->get_pdo(), $db->get_prefix()); $data = $repository->getZeroResults($days, self::ITEMS_PER_PAGE, $offset); $total = $repository->getTotalCounts($days)['total_zero_results']; wp_send_json_success([ 'data' => $data, 'total' => $total, 'page' => $page, 'pages' => ceil($total / self::ITEMS_PER_PAGE), ]); } /** * Render pagination controls * * @param int $currentPage Current page number * @param int $totalPages Total number of pages * @param string $tableId ID of the table for JS targeting * @return string HTML for pagination controls */ private function render_pagination(int $currentPage, int $totalPages, string $tableId): string { if ($totalPages <= 1) { return ''; } $html = ''; return $html; } /** * 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 ); // Localize script for AJAX pagination wp_localize_script('roi-apu-dashboard', 'roiApuDashboardAjax', [ 'ajaxUrl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('roi_apu_dashboard'), ]); } /** * 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 $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); ?>

0; $icon = $isPositive ? 'bi-arrow-up' : 'bi-arrow-down'; $color = $isPositive ? '#22c55e' : '#ef4444'; $sign = $value > 0 ? '+' : ''; return sprintf( ' %s%s%% vs anterior', $icon, $color, $sign, number_format($value, 1) ); }; ?>

0

%

20% clicks vs período anterior. Revisa si están desactualizados.', 'roi-apu-search'); ?>


'#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'; ?>
%
%
1) : ?>
1) : ?>

1) : ?>