Files
Sistema-Educativo-Laravel/app/Http/Controllers/WhatsAppAnalyticsController.php
T
fernando 61b18df8e8 feat: WhatsApp promociones — fechas en español, status webhook y analytics inspector
- Corrige formato de fechas a español con Carbon isoFormat('D [de] MMMM')
- Webhook procesa statuses de Meta (delivered/read/failed) y actualiza pivot alumno_promocion
- Analytics: nuevas métricas de entregados/leídos, embudo de entrega, filtro por tipo
- Inspector de payload raw por log con modal y botón copiar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 15:42:26 -06:00

119 lines
4.1 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Plantel;
use App\Models\Promocion;
use App\Models\WhatsappLog;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class WhatsAppAnalyticsController extends Controller
{
public function index(Request $request)
{
$plantelesIds = Auth::user()->plantelUsuarios->pluck('id');
$planteles = Auth::user()->plantelUsuarios;
$query = WhatsappLog::query();
if ($request->filled('plantel_id')) {
$plantelId = (int) $request->plantel_id;
$query->whereNotNull('alumno_id')
->whereHas('alumno.alumnos.plantelUsuarios', function ($q) use ($plantelId) {
$q->where('plantels.id', $plantelId);
});
}
if ($request->filled('promocion_id')) {
$query->whereJsonContains('payload->promocion_id', (int) $request->promocion_id);
}
if ($request->filled('fecha_desde')) {
$query->where('created_at', '>=', $request->fecha_desde . ' 00:00:00');
}
if ($request->filled('fecha_hasta')) {
$query->where('created_at', '<=', $request->fecha_hasta . ' 23:59:59');
}
// Métricas siempre sobre todos los tipos (sin filtro de tipo)
$enviados = (clone $query)->where('tipo', 'enviado')->count();
$fallidos = (clone $query)->where('tipo', 'error')->count();
$optOuts = (clone $query)->where('tipo', 'opt_out')->count();
$recibidos = (clone $query)->where('tipo', 'recibido')->count();
$entregados = (clone $query)->where('tipo', 'status_delivered')->count();
$leidos = (clone $query)->where('tipo', 'status_read')->count();
// El filtro de tipo solo aplica al listado de logs
$logsQuery = clone $query;
if ($request->filled('tipo')) {
$logsQuery->where('tipo', $request->tipo);
}
$logsRecientes = $logsQuery
->with('alumno.alumnos')
->latest('created_at')
->paginate(20);
$promociones = Promocion::whereHas('planteles', function ($q) use ($plantelesIds) {
$q->whereIn('plantels.id', $plantelesIds);
})->latest()->get();
return view('promocion.analytics', compact(
'enviados', 'fallidos', 'optOuts', 'recibidos', 'entregados', 'leidos',
'logsRecientes', 'planteles', 'promociones'
));
}
public function stats(Request $request)
{
$query = WhatsappLog::query();
if ($request->filled('plantel_id')) {
$plantelId = (int) $request->plantel_id;
$query->whereNotNull('alumno_id')
->whereHas('alumno.alumnos.plantelUsuarios', function ($q) use ($plantelId) {
$q->where('plantels.id', $plantelId);
});
}
return response()->json([
'enviados' => (clone $query)->where('tipo', 'enviado')->count(),
'recibidos' => (clone $query)->where('tipo', 'recibido')->count(),
'fallidos' => (clone $query)->where('tipo', 'error')->count(),
'opt_outs' => (clone $query)->where('tipo', 'opt_out')->count(),
]);
}
public function logs(Request $request)
{
$query = WhatsappLog::with('alumno.alumnos')
->orderByDesc('created_at');
if ($request->filled('fecha_desde')) {
$query->where('created_at', '>=', $request->fecha_desde);
}
if ($request->filled('fecha_hasta')) {
$query->where('created_at', '<=', $request->fecha_hasta . ' 23:59:59');
}
if ($request->filled('plantel_id')) {
$plantelId = (int) $request->plantel_id;
$query->whereNotNull('alumno_id')
->whereHas('alumno.alumnos.plantelUsuarios', function ($q) use ($plantelId) {
$q->where('plantels.id', $plantelId);
});
}
if ($request->filled('tipo')) {
$query->where('tipo', $request->tipo);
}
$logs = $query->paginate(50);
return response()->json($logs);
}
}