File: /home/xedaptot/ai.naniguide.com/app/Http/Controllers/Refactor/FunnelReportController.php
<?php
namespace App\Http\Controllers\Refactor;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Model\Funnel;
use App\Model\FunnelPageView;
use App\Model\FunnelConversion;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class FunnelReportController extends Controller
{
public function show(Request $request, $uid)
{
$funnel = Funnel::ofCustomer($request->user()->customer)
->notTemplate()
->where('uid', $uid)->first();
if (!$funnel) {
return $this->notAuthorized();
}
// Date range
$from = $request->from ? Carbon::parse($request->from)->startOfDay() : now()->subDays(30)->startOfDay();
$to = $request->to ? Carbon::parse($request->to)->endOfDay() : now()->endOfDay();
$customerId = $request->user()->customer->id;
// Overall stats
$totalViews = FunnelPageView::ofFunnel($funnel)->dateRange($from, $to)->count();
$uniqueVisitors = FunnelPageView::ofFunnel($funnel)->dateRange($from, $to)->distinct('session_id')->count('session_id');
$totalConversions = FunnelConversion::ofFunnel($funnel)->dateRange($from, $to)->count();
$totalRevenue = FunnelConversion::ofFunnel($funnel)->ofType('purchase')->dateRange($from, $to)->sum('value');
$optins = FunnelConversion::ofFunnel($funnel)->ofType('optin')->dateRange($from, $to)->count();
$purchases = FunnelConversion::ofFunnel($funnel)->ofType('purchase')->dateRange($from, $to)->count();
$conversionRate = $uniqueVisitors > 0 ? round(($totalConversions / $uniqueVisitors) * 100, 1) : 0;
// Per-step stats
$steps = $funnel->steps()->ordered()->get();
$stepStats = [];
foreach ($steps as $step) {
$views = FunnelPageView::where('funnel_step_id', $step->id)->dateRange($from, $to)->count();
$uniqueStepVisitors = FunnelPageView::where('funnel_step_id', $step->id)->dateRange($from, $to)->distinct('session_id')->count('session_id');
$stepConversions = FunnelConversion::where('funnel_step_id', $step->id)->dateRange($from, $to)->count();
$stepRevenue = FunnelConversion::where('funnel_step_id', $step->id)->ofType('purchase')->dateRange($from, $to)->sum('value');
$stepStats[] = [
'step' => $step,
'views' => $views,
'visitors' => $uniqueStepVisitors,
'conversions' => $stepConversions,
'revenue' => $stepRevenue,
'rate' => $uniqueStepVisitors > 0 ? round(($stepConversions / $uniqueStepVisitors) * 100, 1) : 0,
];
}
// Daily views for chart (last 30 days or selected range)
$dailyViews = FunnelPageView::ofFunnel($funnel)
->dateRange($from, $to)
->select(DB::raw('DATE(viewed_at) as date'), DB::raw('COUNT(*) as count'))
->groupBy('date')
->orderBy('date')
->pluck('count', 'date')
->toArray();
$dailyConversions = FunnelConversion::ofFunnel($funnel)
->dateRange($from, $to)
->select(DB::raw('DATE(converted_at) as date'), DB::raw('COUNT(*) as count'))
->groupBy('date')
->orderBy('date')
->pluck('count', 'date')
->toArray();
// Build full date range for chart
$chartDates = [];
$chartViews = [];
$chartConversions = [];
$period = new \DatePeriod($from->copy(), new \DateInterval('P1D'), $to->copy()->addDay());
foreach ($period as $date) {
$key = $date->format('Y-m-d');
$chartDates[] = $date->format('M j');
$chartViews[] = $dailyViews[$key] ?? 0;
$chartConversions[] = $dailyConversions[$key] ?? 0;
}
// Top referrers
$topReferrers = FunnelPageView::ofFunnel($funnel)
->dateRange($from, $to)
->whereNotNull('referrer')
->where('referrer', '!=', '')
->select('referrer', DB::raw('COUNT(*) as count'))
->groupBy('referrer')
->orderByDesc('count')
->limit(5)
->pluck('count', 'referrer')
->toArray();
// Device breakdown
$devices = FunnelPageView::ofFunnel($funnel)
->dateRange($from, $to)
->select('device_type', DB::raw('COUNT(*) as count'))
->groupBy('device_type')
->pluck('count', 'device_type')
->toArray();
return view('refactor.pages.funnels.report', [
'funnel' => $funnel,
'from' => $from,
'to' => $to,
'totalViews' => $totalViews,
'uniqueVisitors' => $uniqueVisitors,
'totalConversions' => $totalConversions,
'totalRevenue' => $totalRevenue,
'optins' => $optins,
'purchases' => $purchases,
'conversionRate' => $conversionRate,
'stepStats' => $stepStats,
'chartDates' => $chartDates,
'chartViews' => $chartViews,
'chartConversions' => $chartConversions,
'topReferrers' => $topReferrers,
'devices' => $devices,
]);
}
/**
* Page views detail page (list all views with filters).
*/
public function pageViews(Request $request, $uid)
{
$funnel = Funnel::ofCustomer($request->user()->customer)
->notTemplate()
->where('uid', $uid)->first();
if (!$funnel) {
return $this->notAuthorized();
}
$steps = $funnel->steps()->ordered()->get();
return view('refactor.pages.funnels.page_views', [
'funnel' => $funnel,
'steps' => $steps,
]);
}
/**
* AJAX listing for page views.
*/
public function pageViewsListing(Request $request, $uid)
{
$funnel = Funnel::ofCustomer($request->user()->customer)
->notTemplate()
->where('uid', $uid)->first();
if (!$funnel) {
return $this->notAuthorized();
}
$query = FunnelPageView::where('funnel_page_views.funnel_id', $funnel->id)
->leftJoin('funnel_steps', 'funnel_page_views.funnel_step_id', '=', 'funnel_steps.id')
->select('funnel_page_views.*', 'funnel_steps.name as step_name', 'funnel_steps.type as step_type');
// Date range
if ($request->from) {
$query->where('funnel_page_views.viewed_at', '>=', Carbon::parse($request->from)->startOfDay());
}
if ($request->to) {
$query->where('funnel_page_views.viewed_at', '<=', Carbon::parse($request->to)->endOfDay());
}
// Filter by step
if ($request->step_id) {
$query->where('funnel_page_views.funnel_step_id', $request->step_id);
}
// Filter by device
if ($request->device) {
$query->where('funnel_page_views.device_type', $request->device);
}
// Filter: unique visitors only
if ($request->unique == '1') {
$query->whereIn('funnel_page_views.id', function ($sub) use ($funnel, $request) {
$sub->selectRaw('MIN(funnel_page_views.id)')
->from('funnel_page_views')
->where('funnel_page_views.funnel_id', $funnel->id);
if ($request->from) {
$sub->where('funnel_page_views.viewed_at', '>=', Carbon::parse($request->from)->startOfDay());
}
if ($request->to) {
$sub->where('funnel_page_views.viewed_at', '<=', Carbon::parse($request->to)->endOfDay());
}
$sub->groupBy('funnel_page_views.session_id');
});
}
// Search (IP, referrer, user agent, session_id)
if ($keyword = $request->keyword) {
$query->where(function ($q) use ($keyword) {
$q->where('funnel_page_views.ip_address', 'like', "%{$keyword}%")
->orWhere('funnel_page_views.referrer', 'like', "%{$keyword}%")
->orWhere('funnel_page_views.user_agent', 'like', "%{$keyword}%")
->orWhere('funnel_page_views.session_id', 'like', "%{$keyword}%")
->orWhere('funnel_steps.name', 'like', "%{$keyword}%");
});
}
// Sort
$sortBy = $request->sort_by ?: 'viewed_at';
$sortDir = $request->sort_direction ?: 'desc';
$allowed = ['viewed_at', 'ip_address', 'device_type', 'referrer', 'step_name'];
if (in_array($sortBy, $allowed)) {
$query->orderBy($sortBy === 'step_name' ? 'funnel_steps.name' : "funnel_page_views.{$sortBy}", $sortDir);
} else {
$query->orderByDesc('funnel_page_views.viewed_at');
}
$views = $query->paginate($request->per_page ?: 25);
return view('refactor.pages.funnels._page_views_list', [
'views' => $views,
'funnel' => $funnel,
]);
}
}