publish; $no_kw_count = (int) $wpdb->get_var( "SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = '_awb_seo_focus_keyword' WHERE p.post_type = 'post' AND p.post_status = 'publish' AND (pm.meta_value IS NULL OR pm.meta_value = '')" ); $no_desc_count = (int) $wpdb->get_var( "SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = '_awb_seo_description' WHERE p.post_type = 'post' AND p.post_status = 'publish' AND (pm.meta_value IS NULL OR pm.meta_value = '')" ); $no_title_count = (int) $wpdb->get_var( "SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = '_awb_seo_title' WHERE p.post_type = 'post' AND p.post_status = 'publish' AND (pm.meta_value IS NULL OR pm.meta_value = '')" ); $broken_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}awb_broken_links WHERE status_code >= 400 OR status_code = 0" ); $total_issues = $no_kw_count + $no_desc_count + $no_title_count + $broken_count; // ── Date range ───────────────────────────────────────────────────────────── $awb_range_presets = [ 7 => '7D', 28 => '28D', 90 => '90D', 180 => '6M' ]; $range_days = isset( $_GET['range'] ) ? (int) $_GET['range'] : 28; $is_awb_date = function ( $d ) { return is_string( $d ) && preg_match( '/^\d{4}-\d{2}-\d{2}$/', $d ); }; $req_start = isset( $_GET['start'] ) ? sanitize_text_field( wp_unslash( $_GET['start'] ) ) : ''; $req_end = isset( $_GET['end'] ) ? sanitize_text_field( wp_unslash( $_GET['end'] ) ) : ''; $req_custom = $is_awb_date( $req_start ) && $is_awb_date( $req_end ); $awb_dash = AWBSEO_Admin::get_dashboard_data( $range_days, $req_custom ? $req_start : null, $req_custom ? $req_end : null ); $range_days = $awb_dash['range_days']; $use_custom = $awb_dash['use_custom']; $custom_start = $awb_dash['start']; $custom_end = $awb_dash['end']; $range_label = $awb_dash['range_label']; $range_days_display = $awb_dash['range_days_display']; $gsc_on = $awb_dash['gsc_on']; $gsc_stats = $awb_dash['gsc_stats']; $gsc_summary = $awb_dash['gsc_summary']; $gsc_last_error = $awb_dash['gsc_last_error']; $top_pages = $awb_dash['top_pages']; $top_queries = $awb_dash['top_queries']; $bing_on = $awb_dash['bing_on']; $bing_stats = $awb_dash['bing_stats']; $bing_summary = $awb_dash['bing_summary']; $bing_top_pages = $awb_dash['bing_top_pages']; $bing_top_queries = $awb_dash['bing_top_queries']; $awb_sources = []; if ( $gsc_on ) $awb_sources[] = __( 'Google Search Console', 'adworksbid-seo' ); if ( $bing_on ) $awb_sources[] = __( 'Bing Webmaster Tools', 'adworksbid-seo' ); // ── AI Search Visibility (GA4 referral sessions from AI engines) ─────────── // AWBSEO_GA4::get_ai_sessions() / get_sessions_by_source() already existed // (used nowhere in the admin UI until now) - they read the same GA4 // connection Settings already offers and bucket sessions by known AI engine // referrer domains (ChatGPT, Perplexity, Gemini, Copilot, Claude, etc.). // GA4's Data API here only supports "N days ago to today", not an arbitrary // start/end, so a custom date range uses its real day count ($range_days_display) // rather than lining up to the exact custom start date - same limitation // AWBSEO_GA4::get_sessions_by_language() already has. $ga4_on = false; $ai_sessions = []; $ai_total = 0; $ai_pct_of_total = null; $ga4_data_error = ''; if ( class_exists( 'AWBSEO_GA4' ) ) { $ga4_instance = AWBSEO_GA4::get_instance(); if ( $ga4_instance->is_connected() && $ga4_instance->get_property() ) { $ga4_on = true; $ai_days = max( 1, (int) $range_days_display ); $ai_sessions = $ga4_instance->get_ai_sessions( $ai_days ); $ai_total = array_sum( $ai_sessions ); $all_source_rows = $ga4_instance->get_sessions_by_source( $ai_days ); $all_sessions = array_sum( array_column( $all_source_rows, 'sessions' ) ); $ai_pct_of_total = $all_sessions > 0 ? round( ( $ai_total / $all_sessions ) * 100, 1 ) : null; $ga4_data_error = AWBSEO_GA4::get_data_error(); } } ?>

$preset_label ) : $is_active = ! $use_custom && $range_days === $preset_days; $preset_url = add_query_arg( [ 'page' => 'awbseo-analytics', 'range' => $preset_days ], remove_query_arg( [ 'start', 'end' ], admin_url( 'admin.php' ) ) ); ?>
–

⚠️ .

%
%
%

' . esc_html( $range_label ) . '' ); ?>

__( 'Arabic queries', 'adworksbid-seo' ), 'other' => __( 'Other languages', 'adworksbid-seo' ) ] as $awb_lang_key => $awb_lang_label ) : $awb_bucket = $gsc_lang_breakdown[ $awb_lang_key ] ?? [ 'totals' => [ 'clicks' => 0, 'impressions' => 0, 'ctr' => 0, 'position' => 0, 'query_count' => 0 ], 'top_queries' => [] ]; $awb_totals = $awb_bucket['totals']; ?>
%
0 ? esc_html( $awb_totals['position'] ) : 'N/A'; ?>

📊

·

%
%
%
0 && $bing_summary['position'] <= 10 ) ? esc_html__( 'On page 1 🎉', 'adworksbid-seo' ) : esc_html__( 'Aim for top 10', 'adworksbid-seo' ); ?>

📊

·

⚠️ .

$awb_ai_count ) : $awb_ai_share = $ai_total > 0 ? round( ( $awb_ai_count / $ai_total ) * 100 ) : 0; ?>
%

🤖
$gsc_summary['dates'], 'clicks' => $gsc_summary['daily_clicks'], 'impr' => $gsc_summary['daily_impr'], ] ) . ';', 'before' ); } ?>