Files
portal-epromos/landings_public.php
T

283 lines
11 KiB
PHP

<?php
// landings_public.php
// Lista pública de landings con estado 'publicado' + menú + filtros
require_once __DIR__ . '/admin/db.php'; // ajusta la ruta si tu db.php está en otra carpeta
// Ruta pública para ver un landing por slug
const PUBLIC_LANDING_ROUTE = '/ruleta/landing.php?slug='; // p.ej. /landing.php?slug=mi-landing
function e($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
function valid_date($s){ return (bool)preg_match('/^\d{4}-\d{2}-\d{2}$/', $s ?? ''); }
// Parámetros de filtros
$term = trim($_GET['q'] ?? '');
$with_game = isset($_GET['con_juego']) ? 1 : 0;
$with_image = isset($_GET['con_imagen']) ? 1 : 0;
$desde = $_GET['desde'] ?? '';
$hasta = $_GET['hasta'] ?? '';
$sort = $_GET['orden'] ?? 'recientes'; // recientes | alfa
$pageSize = (int)($_GET['pp'] ?? 12);
$page = max(1, (int)($_GET['page'] ?? 1));
if (!in_array($pageSize, [6,12,24,48,96], true)) $pageSize = 12;
$offset = ($page - 1) * $pageSize;
// Construcción del WHERE dinámico
$where = "estado='publicado'";
$params = []; $types = '';
if ($term !== '') {
$where .= " AND (titulo LIKE ? OR slug LIKE ? OR meta_title LIKE ? OR meta_desc LIKE ?)";
$like = '%'.$term.'%';
array_push($params, $like, $like, $like, $like); $types .= 'ssss';
}
if ($with_game) {
$where .= " AND game_url IS NOT NULL AND game_url <> ''";
}
if ($with_image) {
$where .= " AND hero_image IS NOT NULL AND hero_image <> ''";
}
if (valid_date($desde)) {
$where .= " AND (updated_at IS NULL OR updated_at >= ?)";
$params[] = $desde . ' 00:00:00';
$types .= 's';
}
if (valid_date($hasta)) {
$where .= " AND (updated_at IS NULL OR updated_at <= ?)";
$params[] = $hasta . ' 23:59:59';
$types .= 's';
}
// Orden
switch ($sort) {
case 'alfa':
$orderSql = "titulo ASC, id DESC";
break;
case 'recientes':
default:
$orderSql = "COALESCE(updated_at, '1970-01-01 00:00:00') DESC, id DESC";
$sort = 'recientes';
}
// ---- Total para paginación ----
$sqlCount = "SELECT COUNT(*) AS total FROM landing_pages WHERE $where";
$stmt = $conn->prepare($sqlCount);
if ($types) { $stmt->bind_param($types, ...$params); }
$stmt->execute();
$total = (int)($stmt->get_result()->fetch_assoc()['total'] ?? 0);
$stmt->close();
$total_pages = max(1, (int)ceil($total / $pageSize));
if ($page > $total_pages) { $page = $total_pages; $offset = ($page - 1) * $pageSize; }
// ---- Consulta principal ----
$sql = "SELECT id, titulo, slug, hero_image, meta_desc, updated_at, game_url
FROM landing_pages
WHERE $where
ORDER BY $orderSql
LIMIT ? OFFSET ?";
$stmt = $conn->prepare($sql);
// Bind evitando “positional after unpacking”
$limitVal = $pageSize;
$offsetVal = $offset;
if ($types) {
$types2 = $types . 'ii';
$bindValues = array_merge($params, [$limitVal, $offsetVal]);
$stmt->bind_param($types2, ...$bindValues);
} else {
$stmt->bind_param('ii', $limitVal, $offsetVal);
}
$stmt->execute();
$rows = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
$stmt->close();
// Fallback de imagen
$fallbackImg = 'https://picsum.photos/seed/landings/800/450';
// Helper para mantener parámetros en paginación
$baseParams = $_GET; unset($baseParams['page']);
$base = $_SERVER['PHP_SELF'].'?'.http_build_query($baseParams);
$prev = max(1, $page-1); $next = min($total_pages, $page+1);
?>
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Landings activos</title>
<meta name="description" content="Explora los landings activos y participa en las campañas disponibles.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.card-img-top{ aspect-ratio: 16/9; object-fit: cover; }
.line-clamp-2{ display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.filter-toggle { cursor: pointer; }
</style>
</head>
<body>
<!-- MENÚ -->
<nav class="navbar navbar-expand-lg bg-dark navbar-dark">
<div class="container">
<a class="navbar-brand" href="<?= e($_SERVER['PHP_SELF']) ?>">GLM · Landings</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mainNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"><a class="nav-link active" href="<?= e($_SERVER['PHP_SELF']) ?>">Landings</a></li>
<li class="nav-item"><a class="nav-link" href="/">Inicio</a></li>
<li class="nav-item"><a class="nav-link" href="/contacto.php">Contacto</a></li>
</ul>
<!-- BÚSQUEDA RÁPIDA EN MENÚ -->
<form class="d-flex gap-2" method="get" action="">
<input name="q" class="form-control form-control-sm" placeholder="Buscar..." value="<?= e($term) ?>">
<button class="btn btn-outline-light btn-sm">Buscar</button>
</form>
</div>
</div>
</nav>
<main class="container py-4">
<div class="d-flex flex-wrap justify-content-between align-items-center mb-3 gap-2">
<h1 class="h4 mb-0">Landings activos</h1>
<span class="text-muted"><?= $total ?> resultado<?= $total===1?'':'s' ?></span>
</div>
<!-- PANEL DE FILTROS -->
<div class="card mb-4">
<div class="card-body">
<form method="get" action="">
<div class="row g-3 align-items-end">
<div class="col-12 col-md-4">
<label class="form-label">Buscar</label>
<input name="q" class="form-control" placeholder="Título, slug, meta..."
value="<?= e($term) ?>">
</div>
<div class="col-6 col-md-2">
<div class="form-check mt-4">
<input class="form-check-input" type="checkbox" id="con_juego" name="con_juego" <?= $with_game?'checked':'' ?>>
<label class="form-check-label" for="con_juego">Con juego</label>
</div>
</div>
<div class="col-6 col-md-2">
<div class="form-check mt-4">
<input class="form-check-input" type="checkbox" id="con_imagen" name="con_imagen" <?= $with_image?'checked':'' ?>>
<label class="form-check-label" for="con_imagen">Con imagen</label>
</div>
</div>
<div class="col-6 col-md-2">
<label class="form-label">Desde</label>
<input type="date" name="desde" class="form-control" value="<?= e(valid_date($desde)?$desde:'') ?>">
</div>
<div class="col-6 col-md-2">
<label class="form-label">Hasta</label>
<input type="date" name="hasta" class="form-control" value="<?= e(valid_date($hasta)?$hasta:'') ?>">
</div>
<div class="col-6 col-md-2">
<label class="form-label">Orden</label>
<select name="orden" class="form-select">
<option value="recientes" <?= $sort==='recientes'?'selected':'' ?>>Más recientes</option>
<option value="alfa" <?= $sort==='alfa'?'selected':'' ?>>Alfabético (A-Z)</option>
</select>
</div>
<div class="col-6 col-md-2">
<label class="form-label">Por página</label>
<select name="pp" class="form-select">
<?php foreach ([6,12,24,48,96] as $pp): ?>
<option value="<?= $pp ?>" <?= $pageSize===$pp?'selected':'' ?>><?= $pp ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12 col-md-auto ms-auto">
<button class="btn btn-primary">Aplicar</button>
<a class="btn btn-outline-secondary" href="<?= e($_SERVER['PHP_SELF']) ?>">Limpiar</a>
</div>
</div>
</form>
</div>
</div>
<!-- LISTADO -->
<?php if (!$rows): ?>
<div class="alert alert-info">No hay landings publicados que coincidan con los filtros.</div>
<?php else: ?>
<div class="row g-4">
<?php foreach ($rows as $r): ?>
<div class="col-12 col-sm-6 col-lg-4">
<div class="card h-100 shadow-sm">
<img class="card-img-top" loading="lazy"
src="<?= e($r['hero_image'] ?: $fallbackImg) ?>"
alt="<?= e($r['titulo']) ?>">
<div class="card-body d-flex flex-column">
<h2 class="h6 card-title mb-2"><?= e($r['titulo']) ?></h2>
<?php if (!empty($r['meta_desc'])): ?>
<p class="card-text text-muted small line-clamp-2"><?= e($r['meta_desc']) ?></p>
<?php endif; ?>
<div class="mt-auto d-flex flex-wrap gap-2">
<a class="btn btn-primary btn-sm"
href="<?= e(PUBLIC_LANDING_ROUTE . urlencode($r['slug'])) ?>">
Ver landing
</a>
<?php if (!empty($r['game_url'])): ?>
<a class="btn btn-outline-success btn-sm" target="_blank" rel="noopener"
href="<?= e($r['game_url']) ?>">
Jugar
</a>
<?php endif; ?>
</div>
</div>
<div class="card-footer bg-white d-flex justify-content-between align-items-center">
<small class="text-muted">Actualizado: <?= e($r['updated_at'] ?: '—') ?></small>
<?php if (!empty($r['slug'])): ?>
<span class="badge text-bg-light"><?= e($r['slug']) ?></span>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- Paginación -->
<?php if ($total_pages > 1): ?>
<nav class="mt-4" aria-label="Paginación">
<ul class="pagination justify-content-center">
<li class="page-item <?= $page<=1?'disabled':'' ?>">
<a class="page-link" href="<?= e($base.'&page='.$prev) ?>">Anterior</a>
</li>
<?php
$start = max(1, $page-2); $end = min($total_pages, $page+2);
for ($i=$start; $i<=$end; $i++): ?>
<li class="page-item <?= $i===$page?'active':'' ?>">
<a class="page-link" href="<?= e($base.'&page='.$i) ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
<li class="page-item <?= $page>=$total_pages?'disabled':'' ?>">
<a class="page-link" href="<?= e($base.'&page='.$next) ?>">Siguiente</a>
</li>
</ul>
</nav>
<?php endif; ?>
<?php endif; ?>
</main>
<footer class="border-top py-4">
<div class="container text-center text-muted small">
© <?= date('Y') ?> — Landings activos
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>