Initial commit: Proyecto Portal ePromos completo con documentación unificada y seguridad base

This commit is contained in:
Isaac Aracena
2026-05-09 12:46:24 -04:00
commit c44f1a69f1
65 changed files with 7223 additions and 0 deletions
+285
View File
@@ -0,0 +1,285 @@
<?php
/**
* /ruleta/admin/rasca_premios.php
* - Vista 1 (sin rasca_id): lista de rasca configurados para elegir
* - Vista 2 (con rasca_id): CRUD de premios de ese rasca
*/
session_start();
require_once __DIR__ . '/db.php';
if (empty($_SESSION['admin_id'])) { header('Location: ../index.php'); exit; }
//
// Config de uploads
//
$UPLOAD_DIR = __DIR__ . '/../uploads/rasca_premios';
$PUBLIC_DIR = '/ruleta/uploads/rasca_premios'; // ajusta si tu raíz no es /ruleta
if (!is_dir($UPLOAD_DIR)) { @mkdir($UPLOAD_DIR, 0775, true); }
//
// Helpers
//
function sanitize_filename($n){ $n = preg_replace('/[^A-Za-z0-9._-]/','_', $n); return substr($n,0,180); }
function upload_image($field,$UPLOAD_DIR,$PUBLIC_DIR){
if (!isset($_FILES[$field]) || $_FILES[$field]['error']===UPLOAD_ERR_NO_FILE) return null;
$f = $_FILES[$field];
if ($f['error']!==UPLOAD_ERR_OK) throw new RuntimeException("Error subiendo $field (código {$f['error']}).");
$mime = (new finfo(FILEINFO_MIME_TYPE))->file($f['tmp_name']);
$allowed = ['image/png'=>'png','image/jpeg'=>'jpg','image/webp'=>'webp'];
if (!isset($allowed[$mime])) throw new RuntimeException("Formato no permitido. Usa PNG/JPG/WEBP.");
if ($f['size'] > 5*1024*1024) throw new RuntimeException("Archivo muy grande (máx 5MB).");
$ext = $allowed[$mime]; $base = sanitize_filename(pathinfo($f['name'], PATHINFO_FILENAME));
$name = $base.'-'.uniqid().'.'.$ext; $dest = rtrim($UPLOAD_DIR,'/\\').DIRECTORY_SEPARATOR.$name;
if (!move_uploaded_file($f['tmp_name'],$dest)) throw new RuntimeException("No se pudo guardar el archivo.");
return rtrim($PUBLIC_DIR,'/').'/'.$name;
}
//
// Routing params
//
$action = $_GET['action'] ?? '';
$id = (int)($_GET['id'] ?? 0);
$rasca_id = isset($_GET['rasca_id']) ? (int)$_GET['rasca_id'] : null;
$msg = $err = '';
//
// Form handling (solo si hay rasca_id seleccionado)
//
if ($rasca_id && $_SERVER['REQUEST_METHOD']==='POST') {
$pid = (int)($_POST['id'] ?? 0);
$rasca_id = (int)($_POST['rasca_id'] ?? 0);
$nombre = trim($_POST['nombre'] ?? '');
$prob = (float)($_POST['probabilidad'] ?? 0);
$stock = ($_POST['stock'] !== '' ? (int)$_POST['stock'] : null);
$activo = isset($_POST['activo']) ? 1 : 0;
$old_img = $_POST['old_imagen_url'] ?? '';
if ($rasca_id<=0 || $nombre==='') {
$err = 'Indica rasca_id y nombre.';
} else {
try {
$img = upload_image('imagen',$UPLOAD_DIR,$PUBLIC_DIR) ?? ($old_img ?: null);
if ($pid > 0) {
$st = $conn->prepare("UPDATE rasca_premios SET rasca_id=?, nombre=?, probabilidad=?, stock=?, activo=?, imagen_url=? WHERE id=?");
// stock puede ser NULL: usa bind con "d" para probabilidad y "i" para stock; si es NULL, usa null y types "isdiisi" sigue válido porque mysqli convierte nulls
$st->bind_param("isdiisi", $rasca_id, $nombre, $prob, $stock, $activo, $img, $pid);
$st->execute();
$msg = 'Premio actualizado.';
} else {
$st = $conn->prepare("INSERT INTO rasca_premios (rasca_id, nombre, probabilidad, stock, activo, imagen_url) VALUES (?,?,?,?,?,?)");
$st->bind_param("isdiis", $rasca_id, $nombre, $prob, $stock, $activo, $img);
$st->execute();
$msg = 'Premio creado.';
}
} catch (Throwable $e) {
$err = $e->getMessage();
}
}
}
//
// Delete premio
//
if ($rasca_id && $action==='delete' && $id>0) {
$st = $conn->prepare("DELETE FROM rasca_premios WHERE id=? AND rasca_id=?");
$st->bind_param("ii", $id, $rasca_id);
$st->execute();
$msg = 'Premio eliminado.';
}
//
// Datos para vistas
//
$rasca_list = []; // para Vista 1
$rasca_info = null; // info del rasca seleccionado
$edit = null; // en Vista 2, premio a editar
$premios = []; // lista de premios del rasca
if (!$rasca_id) {
// Vista 1: listar rasca_config + conteo de premios
$sql = "SELECT r.rasca_id, r.nombre,
COALESCE(p.cnt,0) AS total_premios
FROM rasca_config r
LEFT JOIN (
SELECT rasca_id, COUNT(*) cnt
FROM rasca_premios
GROUP BY rasca_id
) p ON p.rasca_id = r.rasca_id
ORDER BY r.rasca_id ASC";
$rs = $conn->query($sql);
if ($rs) $rasca_list = $rs->fetch_all(MYSQLI_ASSOC);
} else {
// Vista 2: CRUD de premios para rasca_id
$st = $conn->prepare("SELECT rasca_id, nombre FROM rasca_config WHERE rasca_id=?");
$st->bind_param("i", $rasca_id);
$st->execute();
$rasca_info = $st->get_result()->fetch_assoc();
if ($action==='edit' && $id>0) {
$st = $conn->prepare("SELECT * FROM rasca_premios WHERE id=? AND rasca_id=?");
$st->bind_param("ii", $id, $rasca_id);
$st->execute();
$edit = $st->get_result()->fetch_assoc();
}
$st = $conn->prepare("SELECT * FROM rasca_premios WHERE rasca_id=? ORDER BY id DESC");
$st->bind_param("i", $rasca_id);
$st->execute();
$premios = $st->get_result()->fetch_all(MYSQLI_ASSOC);
}
?>
<!doctype html>
<html lang="es"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Rasca | Premios</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.thumb{width:70px;height:70px;object-fit:cover;border-radius:.5rem}
.card-hover:hover{box-shadow:0 0.5rem 1rem rgba(0,0,0,.15);}
</style>
</head>
<body>
<nav class="navbar navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="dashboard.php">Admin</a>
<div class="d-flex gap-2">
<a class="btn btn-outline-light btn-sm" href="rasca_config.php">Config Rasca</a>
<a class="btn btn-outline-light btn-sm" href="rasca_premios.php">Premios Rasca</a>
<a class="btn btn-outline-light btn-sm" href="rasca_jugadas.php">Jugadas Rasca</a>
<a class="btn btn-outline-light btn-sm" href="../index.php?action=logout">Salir</a>
</div>
</div>
</nav>
<div class="container py-4">
<?php if($msg):?><div class="alert alert-success py-2"><?= htmlspecialchars($msg) ?></div><?php endif; ?>
<?php if($err):?><div class="alert alert-danger py-2"><?= htmlspecialchars($err) ?></div><?php endif; ?>
<?php if(!$rasca_id): ?>
<!-- VISTA 1: Selector de juegos Rasca -->
<div class="d-flex align-items-center justify-content-between mb-3">
<h4 class="mb-0">Selecciona un juego de Rasca</h4>
<a class="btn btn-primary" href="rasca_config.php">Crear/editar Rasca</a>
</div>
<?php if(!$rasca_list): ?>
<div class="alert alert-info">No hay juegos de rasca configurados todavía.</div>
<?php else: ?>
<div class="row g-3">
<?php foreach ($rasca_list as $rc): ?>
<div class="col-md-4">
<a href="rasca_premios.php?rasca_id=<?= (int)$rc['rasca_id'] ?>" class="text-decoration-none text-dark">
<div class="card card-hover h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Rasca #<?= (int)$rc['rasca_id'] ?></h5>
<span class="badge text-bg-secondary"><?= (int)$rc['total_premios'] ?> premios</span>
</div>
<p class="mt-2 mb-0 text-muted"><?= htmlspecialchars($rc['nombre'] ?? '') ?></p>
</div>
<div class="card-footer bg-transparent border-0 pt-0">
<button class="btn btn-sm btn-outline-primary">Administrar premios</button>
</div>
</div>
</a>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php else: ?>
<!-- VISTA 2: CRUD premios de un Rasca -->
<div class="d-flex align-items-center justify-content-between mb-3">
<div>
<a href="rasca_premios.php" class="btn btn-link">&larr; Volver</a>
<h4 class="d-inline-block ms-2 mb-0">Premios Rasca #<?= (int)$rasca_id ?> <?= $rasca_info ? '— '.htmlspecialchars($rasca_info['nombre']) : '' ?></h4>
</div>
</div>
<div class="card shadow-sm mb-4">
<div class="card-body">
<h5 class="mb-3"><?= $edit ? 'Editar premio' : 'Nuevo premio' ?></h5>
<form method="post" enctype="multipart/form-data" class="row g-3">
<input type="hidden" name="id" value="<?= (int)($edit['id'] ?? 0) ?>">
<input type="hidden" name="old_imagen_url" value="<?= htmlspecialchars($edit['imagen_url'] ?? '') ?>">
<div class="col-md-2">
<label class="form-label">Rasca ID</label>
<input type="number" name="rasca_id" class="form-control" required value="<?= htmlspecialchars($rasca_id) ?>">
</div>
<div class="col-md-4">
<label class="form-label">Nombre</label>
<input name="nombre" class="form-control" required value="<?= htmlspecialchars($edit['nombre'] ?? '') ?>">
</div>
<div class="col-md-3">
<label class="form-label">Probabilidad (peso)</label>
<input type="number" step="0.001" name="probabilidad" class="form-control" required value="<?= htmlspecialchars($edit['probabilidad'] ?? '0') ?>">
</div>
<div class="col-md-3">
<label class="form-label">Stock (vacío = ilimitado)</label>
<input type="number" name="stock" class="form-control" value="<?= htmlspecialchars(isset($edit['stock']) ? $edit['stock'] : '') ?>">
</div>
<div class="col-md-6">
<label class="form-label">Imagen del premio</label>
<input type="file" name="imagen" class="form-control" accept=".png,.jpg,.jpeg,.webp">
<?php if(!empty($edit['imagen_url'])): ?>
<div class="mt-2"><img src="<?= htmlspecialchars($edit['imagen_url']) ?>" class="thumb"></div>
<?php endif; ?>
</div>
<div class="col-md-2 d-flex align-items-end">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="activo" id="activo" <?= (isset($edit['activo']) ? ((int)$edit['activo']===1) : true) ? 'checked' : '' ?>>
<label class="form-check-label" for="activo">Activo</label>
</div>
</div>
<div class="col-12">
<button class="btn btn-primary"><?= $edit ? 'Actualizar' : 'Crear' ?></button>
<?php if($edit): ?><a href="rasca_premios.php?rasca_id=<?= (int)$rasca_id ?>" class="btn btn-secondary">Cancelar</a><?php endif; ?>
</div>
</form>
</div>
</div>
<div class="card shadow-sm">
<div class="card-body table-responsive">
<table class="table table-striped table-sm align-middle">
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Prob</th>
<th>Stock</th>
<th>Activo</th>
<th>Imagen</th>
<th class="text-nowrap">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach($premios as $r): ?>
<tr>
<td><?= (int)$r['id'] ?></td>
<td><?= htmlspecialchars($r['nombre']) ?></td>
<td><?= htmlspecialchars($r['probabilidad']) ?></td>
<td><?= is_null($r['stock']) ? '∞' : (int)$r['stock'] ?></td>
<td><?= ((int)$r['activo']===1 ? 'Sí' : 'No') ?></td>
<td><?= $r['imagen_url'] ? '<img class="thumb" src="'.htmlspecialchars($r['imagen_url']).'">' : '—' ?></td>
<td class="text-nowrap">
<a class="btn btn-sm btn-outline-primary" href="rasca_premios.php?rasca_id=<?= (int)$rasca_id ?>&action=edit&id=<?= (int)$r['id'] ?>">Editar</a>
<a class="btn btn-sm btn-outline-danger" href="rasca_premios.php?rasca_id=<?= (int)$rasca_id ?>&action=delete&id=<?= (int)$r['id'] ?>" onclick="return confirm('¿Eliminar premio?')">Eliminar</a>
</td>
</tr>
<?php endforeach; ?>
<?php if(!$premios): ?>
<tr><td colspan="7" class="text-center text-muted">Sin premios</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?php endif; ?>
</div>
</body>
</html>