File: /home/xedaptot/be.naniguide.com/app/Http/Controllers/Refactor/Admin/AdminAdsDashboardController.php
<?php
namespace App\Http\Controllers\Refactor\Admin;
use App\Http\Controllers\Controller;
use App\Model\AdActivityLog;
use App\Model\AdCampaign;
use App\Model\AdMetric;
use App\Model\AdPlatform;
use App\Model\Customer;
use App\Services\Ads\AdCredentialResolver;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class AdminAdsDashboardController extends Controller
{
public function index(Request $request)
{
$period = $request->input('period', '30d');
$days = match ($period) {
'7d' => 7,
'30d' => 30,
'90d' => 90,
default => 30,
};
$startDate = now()->startOfDay()->subDays($days - 1);
$prevStart = (clone $startDate)->subDays($days);
$prevEnd = (clone $startDate)->subDay();
// Global stats
$stats = [
'total_platforms' => AdPlatform::count(),
'system_platforms' => AdPlatform::whereNull('customer_id')->count(),
'customer_platforms' => AdPlatform::whereNotNull('customer_id')->count(),
'total_campaigns' => AdCampaign::count(),
'active_campaigns' => AdCampaign::where('status', AdCampaign::STATUS_ACTIVE)->count(),
'draft_campaigns' => AdCampaign::where('status', AdCampaign::STATUS_DRAFT)->count(),
'error_campaigns' => AdCampaign::where('status', AdCampaign::STATUS_ERROR)->count(),
];
// Totals in period (cross-customer)
$current = AdMetric::where('date', '>=', $startDate)
->selectRaw('COALESCE(SUM(impressions),0) as impressions, COALESCE(SUM(clicks),0) as clicks, COALESCE(SUM(spend),0) as spend, COALESCE(SUM(conversions),0) as conversions')
->first();
$previous = AdMetric::whereBetween('date', [$prevStart, $prevEnd])
->selectRaw('COALESCE(SUM(impressions),0) as impressions, COALESCE(SUM(clicks),0) as clicks, COALESCE(SUM(spend),0) as spend, COALESCE(SUM(conversions),0) as conversions')
->first();
$pctDelta = function ($a, $b) {
if ($b == 0) {
return $a > 0 ? 100 : 0;
}
return round((($a - $b) / $b) * 100, 1);
};
$totals = [
'impressions' => (int) $current->impressions,
'clicks' => (int) $current->clicks,
'spend' => (float) $current->spend,
'conversions' => (int) $current->conversions,
];
$deltas = [
'spend' => $pctDelta((float) $current->spend, (float) $previous->spend),
'impressions' => $pctDelta((int) $current->impressions, (int) $previous->impressions),
'clicks' => $pctDelta((int) $current->clicks, (int) $previous->clicks),
'conversions' => $pctDelta((int) $current->conversions, (int) $previous->conversions),
];
// Daily series for chart
$rawDaily = AdMetric::where('date', '>=', $startDate)
->selectRaw('date, SUM(impressions) as impressions, SUM(clicks) as clicks, SUM(spend) as spend, SUM(conversions) as conversions')
->groupBy('date')
->orderBy('date')
->get()
->keyBy(fn ($row) => (string) $row->date->format('Y-m-d'));
$daily = [];
for ($i = 0; $i < $days; $i++) {
$d = (clone $startDate)->addDays($i)->format('Y-m-d');
$row = $rawDaily[$d] ?? null;
$daily[] = [
'date' => $d,
'impressions' => (int) ($row->impressions ?? 0),
'clicks' => (int) ($row->clicks ?? 0),
'spend' => (float) ($row->spend ?? 0),
'conversions' => (int) ($row->conversions ?? 0),
];
}
$sparklines = [
'spend' => array_map(fn ($r) => $r['spend'], $daily),
'impressions' => array_map(fn ($r) => $r['impressions'], $daily),
'clicks' => array_map(fn ($r) => $r['clicks'], $daily),
'conversions' => array_map(fn ($r) => $r['conversions'], $daily),
];
// Per-platform provisioning status
$provisioning = [];
foreach (AdPlatform::PLATFORMS as $platform) {
$provisioning[$platform] = [
'label' => AdPlatform::PLATFORM_LABELS[$platform],
'configured' => AdCredentialResolver::isConfigured($platform),
'source' => AdCredentialResolver::source($platform),
'customer_connections' => AdPlatform::whereNotNull('customer_id')
->where('platform', $platform)
->count(),
'healthy' => AdPlatform::where('platform', $platform)
->where('health_status', 'healthy')
->whereNotNull('customer_id')
->count(),
'error' => AdPlatform::where('platform', $platform)
->where('health_status', 'error')
->whereNotNull('customer_id')
->count(),
];
}
// Customer adoption
$totalCustomers = Customer::count();
$customersWithPlatform = AdPlatform::whereNotNull('customer_id')
->distinct('customer_id')
->count('customer_id');
$adoptionPercent = $totalCustomers > 0 ? round(($customersWithPlatform / $totalCustomers) * 100, 1) : 0;
// Top customers by spend in period — raw DB query returns stdClass rows
$prefix = DB::getTablePrefix();
$topCustomerStats = DB::table("{$prefix}ad_metrics")
->join("{$prefix}ad_campaigns", "{$prefix}ad_metrics.ad_campaign_id", '=', "{$prefix}ad_campaigns.id")
->where("{$prefix}ad_metrics.date", '>=', $startDate)
->selectRaw("{$prefix}ad_campaigns.customer_id as customer_id, SUM({$prefix}ad_metrics.spend) as spend, SUM({$prefix}ad_metrics.impressions) as impressions, SUM({$prefix}ad_metrics.conversions) as conversions, COUNT(DISTINCT {$prefix}ad_campaigns.id) as campaign_count")
->groupBy("{$prefix}ad_campaigns.customer_id")
->orderByDesc('spend')
->limit(5)
->get();
$topCustomerModels = Customer::whereIn('id', $topCustomerStats->pluck('customer_id'))->get()->keyBy('id');
$topCustomers = $topCustomerStats->map(function ($row) use ($topCustomerModels) {
$customer = $topCustomerModels[$row->customer_id] ?? null;
if (!$customer) return null;
return [
'model' => $customer,
'spend' => (float) $row->spend,
'impressions' => (int) $row->impressions,
'conversions' => (int) $row->conversions,
'campaign_count' => (int) $row->campaign_count,
];
})->filter()->values();
// Recent cross-customer activity
$recentActivity = AdActivityLog::orderByDesc('created_at')
->limit(8)
->get();
// Platform health summary
$healthSummary = [
'healthy' => AdPlatform::whereNotNull('customer_id')->where('health_status', 'healthy')->count(),
'degraded' => AdPlatform::whereNotNull('customer_id')->where('health_status', 'degraded')->count(),
'error' => AdPlatform::whereNotNull('customer_id')->where('health_status', 'error')->count(),
'unknown' => AdPlatform::whereNotNull('customer_id')->where('health_status', 'unknown')->count(),
];
// Alerts
$alerts = [];
foreach ($provisioning as $platform => $info) {
if (!$info['configured']) {
$alerts[] = [
'severity' => 'warning',
'title' => $info['label'] . ' not configured',
'desc' => 'Customers cannot connect this platform until you add OAuth credentials.',
'action_label' => 'Configure',
'action_url' => route('refactor.admin.ads.settings.show'),
];
}
}
if ($healthSummary['error'] > 0) {
$alerts[] = [
'severity' => 'error',
'title' => $healthSummary['error'] . ' platform connection(s) in error state',
'desc' => 'These customers cannot publish to the affected platforms until they reconnect.',
'action_label' => 'View platforms',
'action_url' => route('refactor.admin.ads.platforms.index'),
];
}
if ($stats['error_campaigns'] > 0) {
$alerts[] = [
'severity' => 'error',
'title' => $stats['error_campaigns'] . ' campaign(s) in error state',
'desc' => 'Publishing failed for these campaigns. Review cross-customer activity for details.',
'action_label' => 'Review',
'action_url' => route('refactor.admin.ads.platforms.index'),
];
}
// Admin setup checklist — mirrors the customer onboarding rail on the
// customer dashboard. Shows 4 platforms as 4 rows with a live stat line
// (connected-customer count + healthy count) so admin can see the state
// of the entire ads network at a glance.
$adminSetup = [];
$platformIcons = ['meta' => 'groups', 'google' => 'search', 'tiktok' => 'music_note', 'linkedin' => 'work'];
foreach (AdPlatform::PLATFORMS as $platform) {
$info = $provisioning[$platform];
if (!$info['configured']) {
$state = 'locked';
$stat = trans('refactor/ads_admin_settings.admin_dashboard.setup_stat_not_configured');
} elseif ($info['customer_connections'] === 0) {
$state = 'current';
$stat = trans('refactor/ads_admin_settings.admin_dashboard.setup_stat_configured_no_customers', [
'source' => $info['source'] === 'env'
? trans('refactor/ads_admin_settings.dashboard_enhancements.source_env')
: trans('refactor/ads_admin_settings.dashboard_enhancements.source_setting'),
]);
} else {
$state = 'done';
$stat = trans('refactor/ads_admin_settings.admin_dashboard.setup_stat_live', [
'customers' => $info['customer_connections'],
'healthy' => $info['healthy'],
'errors' => $info['error'],
]);
}
$adminSetup[] = [
'key' => $platform,
'label' => $info['label'],
'icon' => $platformIcons[$platform] ?? 'campaign',
'state' => $state,
'configured' => $info['configured'],
'stat' => $stat,
'configure_url' => route('refactor.admin.ads.settings.show'),
'view_url' => route('refactor.admin.ads.platforms.index'),
];
}
$adminSetupDone = count(array_filter($adminSetup, fn ($s) => $s['state'] === 'done'));
$adminSetupTotal = count($adminSetup);
$adminSetupPercent = $adminSetupTotal > 0 ? (int) round(($adminSetupDone / $adminSetupTotal) * 100) : 0;
return view('refactor.pages.admin.ads.dashboard', [
'period' => $period,
'days' => $days,
'stats' => $stats,
'totals' => $totals,
'deltas' => $deltas,
'daily' => $daily,
'sparklines' => $sparklines,
'provisioning' => $provisioning,
'adoption' => [
'total_customers' => $totalCustomers,
'customers_with_platform' => $customersWithPlatform,
'percent' => $adoptionPercent,
],
'topCustomers' => $topCustomers,
'recentActivity' => $recentActivity,
'healthSummary' => $healthSummary,
'alerts' => $alerts,
'adminSetup' => $adminSetup,
'adminSetupDone' => $adminSetupDone,
'adminSetupTotal' => $adminSetupTotal,
'adminSetupPercent' => $adminSetupPercent,
]);
}
}