| 1 |
<?php |
| 2 |
/** |
| 3 |
* Position Summary Section |
| 4 |
* |
| 5 |
* Buckets tracked keywords into Top 3 / 4–10 / 11–50 / 51–100 brackets. |
| 6 |
* Source: GSC `position_distribution` already computed by Analytics_Manager. |
| 7 |
* |
| 8 |
* @package ThinkRank |
| 9 |
* @subpackage SEO\Email_Report_Sections |
| 10 |
* @since 1.9.0 |
| 11 |
*/ |
| 12 |
|
| 13 |
declare(strict_types=1); |
| 14 |
|
| 15 |
namespace ThinkRank\SEO\Email_Report_Sections; |
| 16 |
|
| 17 |
if (!defined('ABSPATH')) { |
| 18 |
exit; |
| 19 |
} |
| 20 |
|
| 21 |
final class Position_Summary_Section implements Email_Report_Section_Interface { |
| 22 |
|
| 23 |
public function key(): string { |
| 24 |
return 'position_summary'; |
| 25 |
} |
| 26 |
|
| 27 |
public function label(): string { |
| 28 |
return __('Position Summary', 'thinkrank'); |
| 29 |
} |
| 30 |
|
| 31 |
public function default_enabled(): bool { |
| 32 |
return true; |
| 33 |
} |
| 34 |
|
| 35 |
public function requires_capability(): ?string { |
| 36 |
return null; |
| 37 |
} |
| 38 |
|
| 39 |
public function collect(array $context): array { |
| 40 |
$shared = $context['shared'] ?? []; |
| 41 |
if (empty($shared['available'])) { |
| 42 |
return []; |
| 43 |
} |
| 44 |
$dist = $shared['current']['search_performance']['position_distribution'] ?? []; |
| 45 |
if (!is_array($dist) || empty($dist)) { |
| 46 |
return []; |
| 47 |
} |
| 48 |
return [ |
| 49 |
'top_3' => (int) ($dist['top_3'] ?? 0), |
| 50 |
'4_10' => (int) ($dist['4_10'] ?? 0), |
| 51 |
'11_50' => (int) ($dist['10_50'] ?? 0), // GSC's `10_50` bucket starts at 11 |
| 52 |
'51_100' => (int) ($dist['51_100'] ?? 0), |
| 53 |
]; |
| 54 |
} |
| 55 |
|
| 56 |
public function render(array $payload): string { |
| 57 |
$rows = [ |
| 58 |
__('Top 3', 'thinkrank') => $payload['top_3'], |
| 59 |
__('Position 4–10', 'thinkrank') => $payload['4_10'], |
| 60 |
__('Position 11–50', 'thinkrank') => $payload['11_50'], |
| 61 |
__('Position 51–100', 'thinkrank') => $payload['51_100'], |
| 62 |
]; |
| 63 |
|
| 64 |
$html = '<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;">'; |
| 65 |
foreach ($rows as $label => $count) { |
| 66 |
$html .= '<tr>' |
| 67 |
. '<td style="padding:10px 12px;border-bottom:1px solid #e5e7eb;color:#374151;font:500 14px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;">' . esc_html($label) . '</td>' |
| 68 |
. '<td style="padding:10px 12px;border-bottom:1px solid #e5e7eb;text-align:right;color:#111827;font:700 14px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;">' . esc_html(number_format_i18n((int) $count)) . '</td>' |
| 69 |
. '</tr>'; |
| 70 |
} |
| 71 |
$html .= '</table>'; |
| 72 |
return $html; |
| 73 |
} |
| 74 |
|
| 75 |
public function fallback_html(): string { |
| 76 |
return '<p style="color:#6b7280;font-style:italic;">' |
| 77 |
. esc_html__('Search Console data unavailable. Connect Search Console to see position distribution.', 'thinkrank') |
| 78 |
. '</p>'; |
| 79 |
} |
| 80 |
} |
| 81 |
|