<?php if (!defined('DASHBOARD_ROOT')) { http_response_code(403); exit; } RBAC::enforce('view.marketing'); try { $accounts = Database::fetchAll("SELECT id, account_name, meta_account_id, status, updated_at FROM meta_ad_accounts ORDER BY account_name"); } catch (\Throwable $e) { $accounts = []; } try { $campaigns = Database::fetchAll("SELECT mc.id, mc.name, mc.campaign_id, mc.status, mc.objective, mc.daily_budget, mc.total_spend, mc.impressions, mc.clicks, mc.last_synced_at, ma.account_name FROM marketing_campaigns mc JOIN meta_ad_accounts ma ON mc.ad_account_id = ma.meta_account_id ORDER BY mc.last_synced_at DESC LIMIT 50"); } catch (\Throwable $e) { $campaigns = []; } ?> <div id="meta-monitor" class="module-panel"> <span class="section-label">Meta Ads Performance</span> <div id="meta-kpi-grid" class="kpi-grid"><?php $totalSpend = 0; $totalImps = 0; $totalClicks = 0; foreach ($campaigns as $c) { $totalSpend += (float)($c['total_spend'] ?? 0); $totalImps += (int)($c['impressions'] ?? 0); $totalClicks += (int)($c['clicks'] ?? 0); } $ctr = $totalImps > 0 ? round(($totalClicks / $totalImps) * 100, 2) : 0; ?> <div class="kpi-card"><span class="kpi-label">Total Spend</span><span class="kpi-value">$<?= number_format($totalSpend, 2) ?> </span></div> <div class="kpi-card"><span class="kpi-label">Impressions</span><span class="kpi-value"><?= number_format($totalImps) ?> </span></div> <div class="kpi-card"><span class="kpi-label">Clicks</span><span class="kpi-value"><?= number_format($totalClicks) ?> </span></div> <div class="kpi-card"><span class="kpi-label">CTR</span><span class="kpi-value"><?= $ctr ?> %</span></div> </div><?php if (!empty($accounts)): ?> <div class="panel"> <span class="section-label">Connected Accounts</span><?php foreach ($accounts as $a): ?> <div class="list-row"> <span><?= UI::e($a['account_name']) ?> </span> <span class="text-muted"><?= UI::e($a['meta_account_id']) ?> </span><?= UI::badge(UI::e($a['status']), $a['status'] === 'active' ? 'success' : 'default') ?> <span class="text-muted">Synced:<?= $a['updated_at'] ?? 'Never' ?> </span> <a class="btn btn-sm" href="javascript:void(0)" data-action="syncMetaAccount" data-id="<?= (int)$a['id'] ?> "><span>Sync Now</span></a> </div><?php endforeach; ?> </div><?php endif; ?> <?php if (!empty($campaigns)): ?> <div class="panel"> <span class="section-label">Campaign Performance</span> <div class="table-scroll"> <div class="table-row table-header"> <span>Campaign</span><span>Account</span><span>Objective</span><span>Budget</span><span>Spend</span><span>Impressions</span><span>Clicks</span><span>Status</span> </div><?php foreach ($campaigns as $c): ?> <div class="table-row"> <span><?= UI::e($c['name']) ?> </span> <span class="text-muted"><?= UI::e($c['account_name'] ?? '') ?> </span> <span><?= UI::e($c['objective'] ?? '-') ?> </span> <span>$<?= number_format((float)($c['daily_budget'] ?? 0), 2) ?> /day</span> <span>$<?= number_format((float)($c['total_spend'] ?? 0), 2) ?> </span> <span><?= number_format((int)($c['impressions'] ?? 0)) ?> </span> <span><?= number_format((int)($c['clicks'] ?? 0)) ?> </span><?= UI::badge(UI::e($c['status'] ?? ''), ($c['status'] ?? '') === 'ACTIVE' ? 'success' : 'default') ?> </div><?php endforeach; ?> </div> </div><?php endif; ?> </div> <script nonce="<?= $cspNonce ?? '' ?> "> window.syncMetaAccount = function(el) { if (!el) return; var id = el.dataset.id; window.dashFetch('post', {entity:'meta_sync', 'data[account_id]': id}, function(r) { if (r.ok) { alert('Sync started.'); location.reload(); } else { alert(r.error || 'Sync failed'); } }); }; </script>