Compra de Boletos

This commit is contained in:
Jorge Luis Ortega Zenteno 2025-03-06 11:30:20 -06:00
parent 4aa177c5c5
commit ce5c75fff4
11 changed files with 181 additions and 149 deletions

View File

@ -61,3 +61,30 @@ FROM conciertos c
JOIN zonas z ON c.id_concierto = z.id_concierto
WHERE c.id_concierto = 1;
-- Tabla Asientos
CREATE TABLE asientos (
id_asiento INT AUTO_INCREMENT PRIMARY KEY,
id_zona INT NOT NULL,
numero_asiento INT NOT NULL,
estado ENUM('disponible', 'ocupado') NOT NULL DEFAULT 'disponible',
FOREIGN KEY (id_zona) REFERENCES zonas(id_zona) ON DELETE CASCADE,
UNIQUE (id_zona, numero_asiento) -- Para evitar asientos duplicados en la misma zona
);
DROP TABLE asientos;
-- Tabla Boletos
CREATE TABLE boletos (
id_boleto INT AUTO_INCREMENT PRIMARY KEY,
id_concierto INT NOT NULL,
id_zona INT NOT NULL,
id_asiento INT NOT NULL,
nombre_comprador VARCHAR(255) NOT NULL,
precio DECIMAL(10,2) NOT NULL,
fecha_concierto DATE NOT NULL,
artista VARCHAR(255) NOT NULL,
FOREIGN KEY (id_concierto) REFERENCES conciertos(id_concierto) ON DELETE CASCADE,
FOREIGN KEY (id_zona) REFERENCES zonas(id_zona) ON DELETE CASCADE,
FOREIGN KEY (id_asiento) REFERENCES asientos(id_asiento) ON DELETE CASCADE,
UNIQUE (id_asiento) -- Evita que un asiento se venda dos veces
);
DROP TABLE boletos;

View File

@ -2,43 +2,27 @@
include 'conexion.php';
header('Content-Type: application/json');
error_reporting(E_ALL);
ini_set('display_errors', 1);
if (!isset($_GET['id'])) {
echo json_encode(["error" => "No se proporcionó un ID de concierto"]);
if (!isset($_GET['id']) || !isset($_GET['zona'])) {
echo json_encode(["error" => "Faltan parámetros"]);
exit;
}
$conciertoId = intval($_GET['id']);
$nombreZona = $_GET['zona'];
$consulta = "SELECT nombre_zona, capacidad, precio FROM zonas WHERE id_concierto = ? ORDER BY FIELD(nombre_zona, 'General', 'Plata', 'Oro', 'VIP')";
$consulta = "SELECT numero_asiento FROM boletos WHERE id_concierto = ? AND nombre_zona = ?";
$stmt = $conexionBD->prepare($consulta);
if (!$stmt) {
echo json_encode(["error" => "Error en la preparación de la consulta: " . $conexionBD->error]);
exit;
}
$stmt->bind_param('i', $conciertoId);
$stmt->bind_param('is', $conciertoId, $nombreZona);
$stmt->execute();
$resultado = $stmt->get_result();
$zonas = [];
$asientosOcupados = [];
while ($fila = $resultado->fetch_assoc()) {
$zonas[] = [
'nombre_zona' => $fila['nombre_zona'],
'capacidad' => $fila['capacidad'],
'precio' => $fila['precio']
];
}
if (empty($zonas)) {
echo json_encode(["error" => "No se encontraron zonas para este concierto"]);
} else {
echo json_encode($zonas, JSON_PRETTY_PRINT);
$asientosOcupados[] = intval($fila['numero_asiento']);
}
echo json_encode($asientosOcupados);
$stmt->close();
$conexionBD->close();
?>
?>

View File

@ -44,16 +44,28 @@ try {
$id_concierto = $conexionBD->insert_id;
$stmt->close();
// Insertar zonas
// Insertar zonas y sus asientos
$consulta_zonas = "INSERT INTO zonas (id_concierto, nombre_zona, capacidad, precio) VALUES (?, ?, ?, ?)";
$stmt_zonas = $conexionBD->prepare($consulta_zonas);
$consulta_asientos = "INSERT INTO asientos (id_zona, numero_asiento, estado) VALUES (?, ?, 'disponible')";
$stmt_asientos = $conexionBD->prepare($consulta_asientos);
foreach ($zonas as $zona) {
$stmt_zonas->bind_param("isid", $id_concierto, $zona['nombre_zona'], $zona['capacidad'], $zona['precio']);
$stmt_zonas->execute();
$id_zona = $conexionBD->insert_id;
// Insertar asientos
for ($i = 1; $i <= $zona['capacidad']; $i++) {
$stmt_asientos->bind_param("ii", $id_zona, $i);
$stmt_asientos->execute();
}
}
$stmt_zonas->close();
$stmt_asientos->close();
$conexionBD->commit();
echo json_encode(['insercionCorrecta' => true, 'id_concierto' => $id_concierto]);
@ -64,3 +76,4 @@ try {
}
$conexionBD->close();
?>

View File

@ -7,7 +7,6 @@
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}

View File

@ -1,101 +1,82 @@
.mapa-estadio {
position: relative;
width: 600px;
height: 500px;
background-color: #f0f0f0;
border-radius: 50% 50% 0 0;
margin: auto;
/* Contenedor principal que divide la pantalla */
.main-container {
display: flex;
height: 80vh; /* Ocupar 80% de la pantalla */
gap: 20px;
padding: 20px;
}
/* Lado izquierdo (imagen + botones) */
.cardIzq {
flex: 2; /* Ocupar 2/3 del ancho */
display: flex;
justify-content: center;
align-items: flex-end;
flex-direction: column;
overflow: hidden;
}
.escenario {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
height: 140px;
background-color: black;
color: white;
text-align: center;
line-height: 60px;
font-weight: bold;
border-radius: 10px 10px 0 0;
}
.zona {
position: absolute;
cursor: pointer;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
color: white;
border-radius: 50% 50% 0 0;
}
.general {
background-color: gray;
.cardIzq img {
width: 100%;
height: 40%;
bottom: 10%;
left: 0;
border-radius: 50% 50% 0 0;
max-width: 600px;
height: 400px;
}
.plata {
background-color: silver;
width: 80%;
height: 30%;
bottom: 20%;
left: 10%;
border-radius: 50% 50% 0 0;
}
.oro {
background-color: gold;
width: 60%;
height: 25%;
bottom: 30%;
left: 20%;
border-radius: 50% 50% 0 0;
}
.vip {
background-color: purple;
width: 40%;
height: 20%;
bottom: 10%;
left: 30%;
border-radius: 50% 50% 0 0;
}
.selected { border: 3px solid red; }
.asientos-container {
display: none;
margin-top: 20px;
.zones-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 15px;
}
/* Lado derecho (tarjeta con info) */
.cardDer {
flex: 1; /* Ocupar 1/3 del ancho */
display: flex;
align-items: center;
justify-content: center;
}
.card {
background: #aab2b2;
border-radius: 12px;
padding: 20px;
width: 100%;
max-width: 300px;
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.3);
}
.zone-btn {
padding: 10px;
border-radius: 8px;
color: white;
font-weight: bold;
cursor: pointer;
}
.vip-zone { background-color: #5e17eb; }
.oro-zone { background-color: #d5ce2d; }
.plata-zone { background-color: #737373; }
.general-zone { background-color: #725e2b; color: rgb(255, 255, 255); }
.asiento {
width: 30px;
height: 30px;
margin: 5px;
display: inline-block;
width: 40px;
height: 40px;
margin: 5px;
text-align: center;
line-height: 30px;
border-radius: 5px;
line-height: 40px;
font-size: 1.2em;
border: 1px solid #000;
cursor: pointer;
background-color: green;
background-color: #f8f9fa;
border-radius: 5px;
}
.asiento.seleccionado {
background-color: #28a745;
color: white;
}
.asiento.seleccionado { background-color: orange; }
.asiento.ocupado {
background-color: #dc3545;
color: white;
cursor: not-allowed;
}

View File

@ -12,7 +12,7 @@
<!-- Navbar -->
<nav>
<a href="ventanaInsertarConcierto.html" class="navbar-brand">TicketFei</a>
<a href="ventanaConciertos.html" class="navbar-brand">TicketFei</a>
<div class="nav-links">
<a href="ventanaInsertarConcierto.html">Crear Conciertos</a>
<a href="ventanaConciertos.html">Ver Conciertos</a>

BIN
img/mapa.png Normal file

Binary file not shown.

After

(image error) Size: 62 KiB

View File

@ -19,7 +19,7 @@ formulario.addEventListener('submit', async (event) => {
});
const verificarCredenciales = await respuestaPeticion.json();
if (verificarCredenciales.loginExitoso) {
window.location.href = 'ventanaInsertarConcierto.html';
window.location.href = 'ventanaConciertos.html';
} else {
notificacion.textContent ="Usuario o contraseña incorrecta";
notificacion.style.color='#ffffff';

View File

@ -5,20 +5,20 @@ document.addEventListener("DOMContentLoaded", async () => {
alert("No se encontró el ID del concierto.");
return;
}
try {
const respuesta = await fetch(`controladores/concierto_zonas.php?id=${conciertoId}`);
if (!respuesta.ok) throw new Error("Error al cargar las zonas del concierto");
const zonas = await respuesta.json();
cargarZonas(zonas);
cargarZonas(zonas, conciertoId);
} catch (error) {
console.error(error);
alert("Error al cargar los datos del concierto");
}
});
function cargarZonas(zonas) {
function cargarZonas(zonas, conciertoId) {
const container = document.getElementById('zonas-container');
container.innerHTML = "";
@ -26,28 +26,43 @@ function cargarZonas(zonas) {
let div = document.createElement('div');
div.classList.add('zona', zona.nombre_zona.toLowerCase());
div.textContent = `${zona.nombre_zona} - ${zona.capacidad} asientos - $${zona.precio}`;
div.onclick = function() {
div.onclick = async function () {
document.querySelectorAll('.zona').forEach(el => el.classList.remove('selected'));
div.classList.add('selected');
mostrarAsientos(zona.capacidad);
await mostrarAsientos(conciertoId, zona.nombre_zona, zona.capacidad);
};
container.appendChild(div);
});
}
function mostrarAsientos(capacidad) {
async function mostrarAsientos(conciertoId, nombreZona, capacidad) {
const asientosContainer = document.getElementById('asientos-container');
asientosContainer.innerHTML = "";
asientosContainer.style.display = "flex";
for (let i = 0; i < capacidad; i++) {
let asiento = document.createElement('div');
asiento.classList.add('asiento');
asiento.textContent = i + 1;
asiento.onclick = function() {
asiento.classList.toggle('seleccionado');
};
asientosContainer.appendChild(asiento);
try {
const respuesta = await fetch(`controladores/asientos_ocupados.php?id=${conciertoId}&zona=${nombreZona}`);
if (!respuesta.ok) throw new Error("Error al cargar los asientos ocupados");
const asientosOcupados = await respuesta.json();
for (let i = 1; i <= capacidad; i++) {
let asiento = document.createElement('div');
asiento.classList.add('asiento');
asiento.innerHTML = `<i class="bi bi-person-fill"></i> ${i}`;
if (asientosOcupados.includes(i)) {
asiento.classList.add('ocupado');
} else {
asiento.onclick = function () {
asiento.classList.toggle('seleccionado');
};
}
asientosContainer.appendChild(asiento);
}
} catch (error) {
console.error(error);
alert("Error al cargar los asientos");
}
}
@ -57,11 +72,13 @@ document.getElementById("confirmarSeleccion").addEventListener("click", () => {
alert("No has seleccionado ninguna zona.");
return;
}
let selectedAsientos = document.querySelectorAll('.asiento.seleccionado');
if (selectedAsientos.length === 0) {
alert("No has seleccionado ningún asiento.");
return;
}
let asientosSeleccionados = Array.from(selectedAsientos).map(asiento => asiento.textContent);
let asientosSeleccionados = Array.from(selectedAsientos).map(asiento => asiento.textContent.trim());
alert(`Zona seleccionada: ${selectedZona.textContent}\nAsientos: ${asientosSeleccionados.join(', ')}`);
});

View File

@ -6,12 +6,10 @@
<title>Compra de Boletos</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script defer src="js/ventaBoletos.js"></script>
<link rel="stylesheet" href="css/conciertos.css">
<link rel="stylesheet" href="css/conciertos.css"> <!-- Aquí se incluye el CSS -->
<link rel="stylesheet" href="css/zonas.css">
</head>
<body>
<nav>
<a href="ventanaInsertarConcierto.html" class="navbar-brand">TicketFei</a>
<div class="nav-links">
@ -19,29 +17,42 @@
<a href="ventanaConciertos.html">Ver Conciertos</a>
<a href="#">Reporte Ventas</a>
</div>
<div class="search-container">
<input type="search" id="buscadorColaborador" placeholder="Buscar...">
<button id="buscadorBoton">Buscar</button>
</div>
</nav>
<div class="container mt-4 text-center">
<h2>Selecciona tu zona</h2>
</div>
<div class="mapa-estadio">
<div class="escenario">ESCENARIO</div>
<div class="zona general" onclick="seleccionarZona('General')">General</div>
<div class="zona plata" onclick="seleccionarZona('Plata')">Plata</div>
<div class="zona oro" onclick="seleccionarZona('Oro')">Oro</div>
<div class="zona vip" onclick="seleccionarZona('VIP')">VIP</div>
<div class="main-container">
<div class="cardIzq">
<h2>Zonas del Concierto</h2>
<img src="img/mapa.png" alt="Mapa de zonas del concierto">
<div class="zones-container">
<button class="zone-btn vip-zone" onclick="seleccionarZona('vip')">Zona VIP</button>
<button class="zone-btn oro-zone" onclick="seleccionarZona('oro')">Zona ORO</button>
<button class="zone-btn plata-zone" onclick="seleccionarZona('plata')">Zona PLATA</button>
<button class="zone-btn general-zone" onclick="seleccionarZona('general')">Zona GENERAL</button>
</div>
</div>
<div class="cardDer">
<div class="card">
<h2 class="text-xl font-bold mb-4">Detalles de la Zona</h2>
<div id="zona-info" class="space-y-2">
<p><strong>Zona:</strong> <span id="zona-nombre">-</span></p>
<p><strong>Capacidad:</strong> <span id="zona-capacidad">-</span></p>
<p><strong>Precio por asiento:</strong> $<span id="zona-precio">-</span></p>
</div>
</div>
</div>
</div>
<div id="zonas-container"></div>
<div id="asientos-container" class="asientos-container"></div>
<div class="text-center mt-3">
<button class="btn btn-primary" id="confirmarSeleccion">Confirmar selección</button>
</div>
<script defer src="js/ventaBoletos.js"></script>
</body>
</html>
</html>

View File

@ -12,7 +12,7 @@
<body>
<nav>
<a href="ventanaInsertarConcierto.html" class="navbar-brand">TicketFei</a>
<a href="ventanaConciertos.html" class="navbar-brand">TicketFei</a>
<div class="nav-links">
<a href="ventanaInsertarConcierto.html">Crear Conciertos</a>
<a href="ventanaConciertos.html">Ver Conciertos</a>