172 lines
5.4 KiB
PHP
172 lines
5.4 KiB
PHP
<?php
|
|
// ruleta/admin/dashboard_landing_widget_data.php
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
// No HTML en errores:
|
|
ini_set('display_errors', '0');
|
|
ini_set('log_errors', '1');
|
|
|
|
// Captura warnings/notices para adjuntarlos al JSON:
|
|
$php_warnings = [];
|
|
set_error_handler(function($errno, $errstr, $errfile, $errline) use (&$php_warnings) {
|
|
if (error_reporting() === 0) return true;
|
|
$php_warnings[] = "$errstr in $errfile:$errline";
|
|
return true;
|
|
});
|
|
|
|
// Limpia cualquier output previo
|
|
while (ob_get_level() > 0) { ob_end_clean(); }
|
|
ob_start();
|
|
|
|
session_start();
|
|
require_once __DIR__ . '/db.php';
|
|
if (empty($_SESSION['admin_id'])) {
|
|
http_response_code(401);
|
|
echo json_encode(['ok'=>false, 'error'=>'auth']);
|
|
exit;
|
|
}
|
|
|
|
// Helper seguro para prepares
|
|
function must_prepare(mysqli $conn, string $sql): mysqli_stmt {
|
|
$st = $conn->prepare($sql);
|
|
if (!$st) {
|
|
throw new RuntimeException('prepare_failed: '. $conn->error .' | SQL='. preg_replace('/\s+/', ' ', $sql));
|
|
}
|
|
return $st;
|
|
}
|
|
function qcount(mysqli $conn, string $sql): int {
|
|
$rs = $conn->query($sql);
|
|
if (!$rs) throw new RuntimeException('query_failed: '. $conn->error .' | SQL='. preg_replace('/\s+/', ' ', $sql));
|
|
$row = $rs->fetch_assoc();
|
|
return (int)($row['c'] ?? 0);
|
|
}
|
|
|
|
try {
|
|
$from = trim($_GET['from'] ?? '');
|
|
$to = trim($_GET['to'] ?? '');
|
|
$landing_id = (int)($_GET['landing_id'] ?? 0);
|
|
$want_meta = (int)($_GET['meta'] ?? 0);
|
|
|
|
if ($from === '' || $to === '') {
|
|
$to = date('Y-m-d');
|
|
$from = date('Y-m-d', strtotime($to.' -30 day'));
|
|
}
|
|
$from_dt = $from.' 00:00:00';
|
|
$to_dt = $to.' 23:59:59';
|
|
|
|
// --- KPIs
|
|
$kpis = [
|
|
'landings' => qcount($conn, "SELECT COUNT(*) c FROM landing_pages"),
|
|
'variants' => qcount($conn, "SELECT COUNT(*) c FROM landing_variants"),
|
|
'blocks' => qcount($conn, "SELECT COUNT(*) c FROM landing_blocks"),
|
|
'leads_window' => 0,
|
|
'range_label' => date('d M', strtotime($from)).' - '.date('d M', strtotime($to)),
|
|
'filtered' => $landing_id > 0
|
|
];
|
|
|
|
if ($landing_id > 0) {
|
|
$st = must_prepare($conn, "SELECT COUNT(*) c FROM landing_forms WHERE landing_id=? AND created_at BETWEEN ? AND ?");
|
|
$st->bind_param('iss', $landing_id, $from_dt, $to_dt);
|
|
} else {
|
|
$st = must_prepare($conn, "SELECT COUNT(*) c FROM landing_forms WHERE created_at BETWEEN ? AND ?");
|
|
$st->bind_param('ss', $from_dt, $to_dt);
|
|
}
|
|
$st->execute();
|
|
$r = $st->get_result()->fetch_assoc();
|
|
$kpis['leads_window'] = (int)($r['c'] ?? 0);
|
|
|
|
// --- Top + series
|
|
$top = [];
|
|
if ($landing_id > 0) {
|
|
$st = must_prepare($conn, "
|
|
SELECT lp.id AS landing_id, lp.titulo,
|
|
SUM(lvs.event_type='impression') AS imps,
|
|
SUM(lvs.event_type='conversion') AS conv,
|
|
ROUND(SUM(lvs.event_type='conversion')/NULLIF(SUM(lvs.event_type='impression'),0)*100,2) AS cr
|
|
FROM landing_pages lp
|
|
LEFT JOIN landing_variant_stats lvs
|
|
ON lvs.landing_id=lp.id
|
|
AND lvs.created_at BETWEEN ? AND ?
|
|
WHERE lp.id=?
|
|
GROUP BY lp.id, lp.titulo
|
|
");
|
|
$st->bind_param('ssi', $from_dt, $to_dt, $landing_id);
|
|
} else {
|
|
// --- Top global (landing_id = 0): usar subquery para poder ordenar por 'cr'
|
|
$st = must_prepare($conn, "
|
|
SELECT landing_id, titulo, imps, conv, cr
|
|
FROM (
|
|
SELECT
|
|
lp.id AS landing_id,
|
|
lp.titulo,
|
|
SUM(lvs.event_type='impression') AS imps,
|
|
SUM(lvs.event_type='conversion') AS conv,
|
|
ROUND(
|
|
SUM(lvs.event_type='conversion') / NULLIF(SUM(lvs.event_type='impression'), 0) * 100, 2
|
|
) AS cr
|
|
FROM landing_pages lp
|
|
LEFT JOIN landing_variant_stats lvs
|
|
ON lvs.landing_id = lp.id
|
|
AND lvs.created_at BETWEEN ? AND ?
|
|
GROUP BY lp.id, lp.titulo
|
|
) t
|
|
ORDER BY (cr IS NULL), cr DESC, imps DESC
|
|
LIMIT 10
|
|
");
|
|
$st->bind_param('ss', $from_dt, $to_dt);
|
|
}
|
|
$st->execute();
|
|
$rs = $st->get_result();
|
|
|
|
while($row = $rs->fetch_assoc()){
|
|
$row['imps'] = (int)($row['imps'] ?? 0);
|
|
$row['conv'] = (int)($row['conv'] ?? 0);
|
|
$row['cr'] = $row['imps'] ? (float)$row['cr'] : null;
|
|
|
|
// Serie diaria
|
|
$st2 = must_prepare($conn, "
|
|
SELECT DATE(created_at) d,
|
|
SUM(event_type='impression') imp,
|
|
SUM(event_type='conversion') conv
|
|
FROM landing_variant_stats
|
|
WHERE landing_id=? AND created_at BETWEEN ? AND ?
|
|
GROUP BY DATE(created_at)
|
|
ORDER BY d ASC
|
|
");
|
|
$lid = (int)$row['landing_id'];
|
|
$st2->bind_param('iss', $lid, $from_dt, $to_dt);
|
|
$st2->execute();
|
|
$ser = $st2->get_result()->fetch_all(MYSQLI_ASSOC);
|
|
$row['series'] = array_map(function($p){
|
|
return ['d'=>$p['d'], 'imp'=>(int)$p['imp'], 'conv'=>(int)$p['conv']];
|
|
}, $ser);
|
|
|
|
$top[] = $row;
|
|
}
|
|
|
|
// Lista para el select (si meta=1)
|
|
$landings_list = [];
|
|
if ($want_meta === 1) {
|
|
$q = $conn->query("SELECT id, titulo FROM landing_pages ORDER BY titulo ASC");
|
|
if (!$q) throw new RuntimeException('query_failed: '. $conn->error .' | SQL=SELECT id,titulo FROM landing_pages ORDER BY titulo ASC');
|
|
while($L = $q->fetch_assoc()){
|
|
$landings_list[] = ['id'=>(int)$L['id'], 'titulo'=>$L['titulo']];
|
|
}
|
|
}
|
|
|
|
$warnings = $php_warnings;
|
|
ob_end_clean();
|
|
echo json_encode([
|
|
'ok'=>true,
|
|
'kpis'=>$kpis,
|
|
'top'=>$top,
|
|
'landings_list'=>$landings_list,
|
|
'warnings'=>$warnings
|
|
], JSON_UNESCAPED_UNICODE);
|
|
|
|
} catch (Throwable $e) {
|
|
ob_end_clean();
|
|
http_response_code(500);
|
|
echo json_encode(['ok'=>false, 'error'=>$e->getMessage()], JSON_UNESCAPED_UNICODE);
|
|
}
|