Compare commits

...

No commits in common. "main" and "master" have entirely different histories.
main ... master

40 changed files with 1486 additions and 12 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

10
.gitignore vendored
View File

@ -1,10 +0,0 @@
# ---> NetBeans
**/nbproject/private/
**/nbproject/Makefile-*.mk
**/nbproject/Package-*.bash
build/
nbbuild/
dist/
nbdist/
.nb-gradle/

View File

@ -1,2 +0,0 @@
# BOLETC

81
css/agregar_evento.css Normal file
View File

@ -0,0 +1,81 @@
/* Estilos generales */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
text-align: center;
}
h1 {
color: #333;
margin-top: 20px;
}
/* Contenedor del formulario */
form {
background: white;
padding: 20px;
max-width: 400px;
margin: 20px auto;
border-radius: 10px;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
}
/* Estilos de etiquetas */
label {
display: block;
font-weight: bold;
margin: 10px 0 5px;
}
/* Estilos de los inputs y selects */
input, select {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border-radius: 5px;
border: 1px solid #ccc;
font-size: 16px;
}
/* Botón de agregar evento */
button {
background-color: #008cba;
color: white;
padding: 10px 15px;
border: none;
border-radius: 5px;
font-weight: bold;
font-size: 16px;
cursor: pointer;
width: 100%;
}
button:hover {
background-color: #005f7f;
}
/* Mensaje de éxito o error */
#mensaje {
margin-top: 15px;
font-size: 16px;
font-weight: bold;
color: #333;
}
/* Botón de volver */
.boton-volver {
display: inline-block;
margin-top: 20px;
padding: 10px 15px;
background-color: #008cba;
color: white;
text-decoration: none;
font-weight: bold;
border-radius: 5px;
}
.boton-volver:hover {
background-color: #005f7f;
}

60
css/comprobante.css Normal file
View File

@ -0,0 +1,60 @@
/* Estilos generales */
body {
font-family: 'Arial', sans-serif;
background-color: #f4f4f4;
color: #333;
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
/* Contenedor principal */
.comprobante-container {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
width: 50%;
text-align: center;
}
/* Título */
h1 {
color: #4d4d4d;
font-size: 24px;
margin-bottom: 20px;
}
/* Información del boleto */
.boleto {
background: #f9f9f9;
padding: 15px;
border-radius: 8px;
margin-bottom: 10px;
}
/* Separador entre boletos */
hr {
border: none;
height: 1px;
background: #ccc;
margin: 20px 0;
}
/* Botón de volver */
.boton-volver {
display: inline-block;
margin-top: 20px;
padding: 10px 15px;
background-color: #008cba;
color: white;
text-decoration: none;
font-weight: bold;
border-radius: 5px;
}
.boton-volver:hover {
background-color: #005f7f;
}

64
css/editar_evento.css Normal file
View File

@ -0,0 +1,64 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
text-align: center;
}
h1 {
color: #333;
}
form {
background: white;
padding: 20px;
max-width: 400px;
margin: 20px auto;
border-radius: 10px;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
}
label {
display: block;
font-weight: bold;
margin: 10px 0 5px;
}
input, select {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border-radius: 5px;
border: 1px solid #ccc;
}
button {
background-color: #008cba;
color: white;
padding: 10px 15px;
border: none;
border-radius: 5px;
font-weight: bold;
cursor: pointer;
}
button:hover {
background-color: #005f7f;
}
/* Botón de volver */
.boton-volver {
display: inline-block;
margin-top: 20px;
padding: 10px 15px;
background-color: #008cba;
color: white;
text-decoration: none;
font-weight: bold;
border-radius: 5px;
}
.boton-volver:hover {
background-color: #005f7f;
}

128
css/estilos.css Normal file
View File

@ -0,0 +1,128 @@
/* Reset básico */
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
overflow-x: hidden;
}
/* Estilos del encabezado */
header {
background-color: #4d4d4d;
color: white;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
header h1 {
margin: 0;
font-size: 24px;
}
nav a {
color: white;
text-decoration: none;
margin-right: 20px;
font-weight: bold;
}
nav a:hover {
text-decoration: underline;
}
/* Carrusel de conciertos */
.contenedor-principal {
display: flex;
justify-content: center;
width: 100%;
background: #eeeeee;
padding: 20px;
border-radius: 10px;
}
/* Contenedor de conciertos con scroll horizontal */
.contenedor-conciertos {
display: flex;
gap: 20px;
overflow-x: auto; /* Habilitar desplazamiento horizontal */
scroll-behavior: smooth;
white-space: nowrap; /* Evitar que los conciertos se apilen verticalmente */
padding: 10px;
width: 90%;
}
/* Estilo del scrollbar en navegadores modernos */
.contenedor-conciertos::-webkit-scrollbar {
height: 8px;
}
.contenedor-conciertos::-webkit-scrollbar-thumb {
background: #008cba;
border-radius: 5px;
}
.contenedor-conciertos::-webkit-scrollbar-track {
background: #f4f4f4;
}
/* Tarjetas de concierto */
.concierto {
background: white;
border-radius: 10px;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
text-align: center;
width: 250px;
display: inline-block;
padding: 15px;
}
.concierto img {
width: 100%;
height: 150px;
object-fit: cover;
border-radius: 5px;
}
.concierto h2 {
font-size: 18px;
margin: 10px 0;
}
.concierto p {
font-size: 14px;
color: #666;
}
/* Botón comprar */
.concierto button {
background-color: #008cba;
color: white;
padding: 10px 15px;
border: none;
border-radius: 5px;
font-weight: bold;
cursor: pointer;
}
.concierto button:hover {
background-color: #005f7f;
}
/* Botón de volver */
.boton-volver {
display: inline-block;
margin-top: 20px;
padding: 10px 15px;
background-color: #008cba;
color: white;
text-decoration: none;
font-weight: bold;
border-radius: 5px;
}
.boton-volver:hover {
background-color: #005f7f;
}

50
css/reporte_ventas.css Normal file
View File

@ -0,0 +1,50 @@
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
text-align: center;
}
h1 {
color: #333;
}
table {
width: 90%;
margin: 20px auto;
border-collapse: collapse;
}
th, td {
border: 1px solid #ddd;
padding: 10px;
text-align: center;
}
th {
background-color: #008cba;
color: white;
}
button {
padding: 10px 15px;
background: #008cba;
color: white;
border: none;
cursor: pointer;
}
/* Botón de volver */
.boton-volver {
display: inline-block;
margin-top: 20px;
padding: 10px 15px;
background-color: #008cba;
color: white;
text-decoration: none;
font-weight: bold;
border-radius: 5px;
}
.boton-volver:hover {
background-color: #005f7f;
}

143
css/venta_boletos.css Normal file
View File

@ -0,0 +1,143 @@
/* Estilos generales */
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
text-align: center;
}
/* Contenedor principal */
.container {
width: 90%;
max-width: 800px;
margin: 20px auto;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
}
/* Título */
h1 {
color: #4d4d4d;
}
/* Select de conciertos */
select {
padding: 10px;
margin: 10px 0;
border-radius: 5px;
border: 1px solid #ccc;
font-size: 16px;
}
/* Contenedor de zonas */
.zona-container {
margin-top: 20px;
}
/* Nombre de la zona */
.zona-titulo {
font-size: 20px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
text-align: center;
}
/* Contenedor de asientos */
#asientosContainer {
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
}
/* Fila de asientos */
.fila-asientos {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
}
/* VIP: en una sola fila */
#asientosVIP {
display: flex;
gap: 10px;
justify-content: center;
}
/* General: en 2 filas de 5 */
#asientosGeneral {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
justify-content: center;
}
/* Estilos de los asientos */
.asiento {
width: 50px;
height: 50px;
line-height: 50px;
text-align: center;
border-radius: 5px;
font-weight: bold;
cursor: pointer;
}
/* Asientos disponibles */
.asiento.disponible {
background-color: #4CAF50;
color: white;
border: 2px solid #3e8e41;
}
/* Asientos vendidos */
.asiento.vendido {
background-color: #d9534f;
color: white;
border: 2px solid #a94442;
cursor: not-allowed;
}
/* Asientos seleccionados */
.asiento.seleccionado {
background-color: #FFD700;
color: black;
border: 2px solid #DAA520;
}
/* Botón de compra */
button {
background-color: #008cba;
color: white;
padding: 12px 20px;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
margin-top: 20px;
}
button:hover {
background-color: #005f7f;
}
/* Botón de volver */
.boton-volver {
display: inline-block;
margin-top: 20px;
padding: 10px 15px;
background-color: #008cba;
color: white;
text-decoration: none;
font-weight: bold;
border-radius: 5px;
}
.boton-volver:hover {
background-color: #005f7f;
}

BIN
img/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

(image error) Size: 107 KiB

BIN
img/1741881316_cerati.jpg Normal file

Binary file not shown.

After

(image error) Size: 159 KiB

Binary file not shown.

After

(image error) Size: 83 KiB

Binary file not shown.

After

(image error) Size: 52 KiB

Binary file not shown.

After

(image error) Size: 12 KiB

Binary file not shown.

After

(image error) Size: 31 KiB

BIN
img/default.jpg Normal file

Binary file not shown.

After

(image error) Size: 107 KiB

17
js/agregar_evento.js Normal file
View File

@ -0,0 +1,17 @@
document.getElementById("formConcierto").addEventListener("submit", function(e) {
e.preventDefault();
let formData = new FormData(this);
fetch("../php/insertar_concierto.php", {
method: "POST",
body: formData
})
.then(response => response.text())
.then(data => {
console.log("Respuesta del servidor:", data);
this.reset();
window.location.href = "../views/index.html"; // Redirigir al inicio sin alert
})
.catch(error => console.error("Error:", error));
});

42
js/conciertos.js Normal file
View File

@ -0,0 +1,42 @@
document.addEventListener("DOMContentLoaded", function() {
fetch("../php/conciertos.php")
.then(response => response.json())
.then(data => {
let div = document.getElementById("conciertos");
div.innerHTML = ""; // Limpiar contenido previo
if (data.length === 0) {
div.innerHTML = "<p>No hay conciertos disponibles.</p>";
return;
}
data.forEach(concierto => {
let evento = document.createElement("div");
evento.classList.add("concierto");
evento.innerHTML = `
<img src="../img/${concierto.imagen}" alt="${concierto.nombre}" onerror="this.src='../img/default.jpg';">
<h2>${concierto.nombre}</h2>
<p><strong>Artista:</strong> ${concierto.artista}</p>
<p><strong>Ubicación:</strong> ${concierto.direccion}</p>
<p><strong>Fecha:</strong> ${concierto.fecha} - ${concierto.hora}</p>
<button onclick="comprarBoletos(${concierto.id})">COMPRAR</button>
`;
div.appendChild(evento);
});
configurarScroll();
})
.catch(error => console.error("Error cargando conciertos:", error));
});
// Función para redirigir a la compra de boletos
function comprarBoletos(conciertoId) {
window.location.href = `venta_boletos.html?concierto_id=${conciertoId}`;
}
// Configurar la barra de desplazamiento
function configurarScroll() {
const container = document.getElementById("conciertos");
container.style.overflowX = "auto"; // Habilitar desplazamiento horizontal
container.style.whiteSpace = "nowrap"; // Asegurar que los elementos estén en línea
}

85
js/editar_evento.js Normal file
View File

@ -0,0 +1,85 @@
document.addEventListener("DOMContentLoaded", function() {
cargarEventos();
});
function cargarEventos() {
fetch("../php/obtener_conciertos.php")
.then(response => response.json())
.then(data => {
let select = document.getElementById("eventoSeleccionado");
select.innerHTML = '<option value="">Seleccione un evento</option>';
data.forEach(concierto => {
let option = document.createElement("option");
option.value = concierto.id;
option.textContent = concierto.nombre;
select.appendChild(option);
});
select.addEventListener("change", function() {
cargarDatosEvento(select.value);
});
})
.catch(error => console.error("Error cargando conciertos:", error));
}
function cargarDatosEvento(eventoId) {
if (!eventoId) return;
fetch(`../php/obtener_detalle_concierto.php?id=${eventoId}`)
.then(response => response.json())
.then(data => {
document.querySelector("input[name='nombre']").value = data.nombre;
document.querySelector("input[name='artista']").value = data.artista;
document.querySelector("input[name='fecha']").value = data.fecha;
document.querySelector("input[name='hora']").value = data.hora;
document.querySelector("input[name='direccion']").value = data.direccion;
document.querySelector("input[name='precio_vip']").value = data.precio_vip;
document.querySelector("input[name='precio_general']").value = data.precio_general;
})
.catch(error => console.error("Error cargando detalles del evento:", error));
}
document.getElementById("formEditarEvento").addEventListener("submit", function(e) {
e.preventDefault();
let formData = new FormData(this);
fetch("../php/editar_concierto.php", {
method: "POST",
body: formData
})
.then(response => response.text())
.then(() => {
window.location.href = "../views/index.html"; // Redirigir sin alert
})
.catch(error => console.error("Error:", error));
});
document.getElementById("btnEliminar").addEventListener("click", function() {
let eventoId = document.getElementById("eventoSeleccionado").value;
if (!eventoId) {
alert("Selecciona un evento para eliminar.");
return;
}
if (!confirm("¿Estás seguro de que quieres eliminar este evento?")) {
return;
}
fetch("../php/eliminar_concierto.php", {
method: "POST",
body: JSON.stringify({ id: eventoId }),
headers: { "Content-Type": "application/json" }
})
.then(response => response.text())
.then(() => {
window.location.href = "../views/index.html"; // Redirigir sin alert
})
.catch(error => console.error("Error eliminando evento:", error));
});
document.getElementById("btnVolver").addEventListener("click", function() {
window.location.href = "../views/index.html";
});

43
js/reporte_ventas.js Normal file
View File

@ -0,0 +1,43 @@
document.getElementById("generarReporte").addEventListener("click", function() {
let fechaInicio = document.getElementById("fechaInicio").value;
let fechaFin = document.getElementById("fechaFin").value;
if (!fechaInicio || !fechaFin) {
alert("Por favor, selecciona un período de fechas.");
return;
}
fetch(`../php/reporte_ventas.php?fechaInicio=${fechaInicio}&fechaFin=${fechaFin}`)
.then(response => response.json())
.then(data => {
let tabla = document.getElementById("tablaReporte");
tabla.innerHTML = ""; // Limpiar datos previos
data.forEach(venta => {
let row = `<tr>
<td>${venta.id}</td>
<td>${venta.concierto}</td>
<td>${venta.fecha}</td>
<td>${venta.hora}</td>
<td>${venta.zona}</td>
<td>${venta.asiento}</td>
<td>$${parseFloat(venta.precio).toFixed(2)}</td>
<td>${venta.fecha_venta}</td>
</tr>`;
tabla.innerHTML += row;
});
})
.catch(error => console.error("Error cargando el reporte:", error));
});
document.getElementById("exportarCSV").addEventListener("click", function() {
let fechaInicio = document.getElementById("fechaInicio").value;
let fechaFin = document.getElementById("fechaFin").value;
if (!fechaInicio || !fechaFin) {
alert("Por favor, selecciona un período de fechas.");
return;
}
window.location.href = `../php/exportar_csv.php?fechaInicio=${fechaInicio}&fechaFin=${fechaFin}`;
});

98
js/venta_boletos.js Normal file
View File

@ -0,0 +1,98 @@
document.addEventListener("DOMContentLoaded", function() {
cargarConciertos();
});
function cargarConciertos() {
fetch("../php/conciertos.php")
.then(response => response.json())
.then(data => {
let select = document.getElementById("concierto");
select.innerHTML = '<option value="">Selecciona un concierto</option>';
data.forEach(concierto => {
let option = document.createElement("option");
option.value = concierto.id;
option.textContent = concierto.nombre;
select.appendChild(option);
});
select.addEventListener("change", function() {
cargarAsientos(select.value);
});
})
.catch(error => console.error("Error cargando conciertos:", error));
}
function cargarAsientos(conciertoId) {
fetch(`../php/asientos.php?concierto_id=${conciertoId}`)
.then(response => response.json())
.then(data => {
if (!Array.isArray(data)) {
console.error("Error: La respuesta del servidor no es un array.", data);
return;
}
let containerVIP = document.getElementById("asientosVIP");
let containerGeneral = document.getElementById("asientosGeneral");
containerVIP.innerHTML = "";
containerGeneral.innerHTML = "";
if (data.length === 0) {
containerVIP.innerHTML = "<p>No hay asientos VIP disponibles.</p>";
containerGeneral.innerHTML = "<p>No hay asientos Generales disponibles.</p>";
return;
}
data.forEach(asiento => {
let div = document.createElement("div");
div.classList.add("asiento", asiento.estado);
div.textContent = `Asiento ${asiento.numero}`;
if (asiento.estado === "disponible") {
div.addEventListener("click", () => seleccionarAsiento(div, asiento.id));
}
if (asiento.zona === "VIP") {
containerVIP.appendChild(div);
} else {
containerGeneral.appendChild(div);
}
});
})
.catch(error => console.error("Error cargando asientos:", error));
}
let asientosSeleccionados = [];
function seleccionarAsiento(element, asientoId) {
if (asientosSeleccionados.includes(asientoId)) {
asientosSeleccionados = asientosSeleccionados.filter(id => id !== asientoId);
element.classList.remove("seleccionado");
} else {
asientosSeleccionados.push(asientoId);
element.classList.add("seleccionado");
}
}
document.getElementById("confirmarCompra").addEventListener("click", function() {
if (asientosSeleccionados.length === 0) {
alert("Selecciona al menos un asiento.");
return;
}
const conciertoId = document.getElementById("concierto").value;
fetch("../php/comprar_asiento.php", {
method: "POST",
body: JSON.stringify({ concierto_id: conciertoId, asientos: asientosSeleccionados }),
headers: { "Content-Type": "application/json" }
})
.then(response => response.json())
.then(data => {
if (!data.error) {
window.location.href = `../php/comprobante.php?transaction_id=${data.transaction_id}`;
}
})
.catch(error => console.error("Error al comprar boletos:", error));
});

36
php/asientos.php Normal file
View File

@ -0,0 +1,36 @@
<?php
include 'conexion.php';
$concierto_id = $_GET['concierto_id'] ?? null;
if (!$concierto_id) {
die(json_encode(["error" => "No se proporcionó un ID de concierto."]));
}
// Revisar si existen zonas para el concierto
$sql_zonas = "SELECT id FROM zonas WHERE concierto_id = ?";
$stmt_zonas = $conn->prepare($sql_zonas);
$stmt_zonas->bind_param("i", $concierto_id);
$stmt_zonas->execute();
$result_zonas = $stmt_zonas->get_result();
if ($result_zonas->num_rows === 0) {
die(json_encode(["error" => "No hay zonas registradas para este concierto."]));
}
$sql = "SELECT asientos.id, asientos.numero, asientos.estado, zonas.nombre AS zona
FROM asientos
JOIN zonas ON asientos.zona_id = zonas.id
WHERE zonas.concierto_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $concierto_id);
$stmt->execute();
$result = $stmt->get_result();
$asientos = [];
while ($row = $result->fetch_assoc()) {
$asientos[] = $row;
}
echo json_encode($asientos);
?>

57
php/comprar_asiento.php Normal file
View File

@ -0,0 +1,57 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
include 'conexion.php';
header("Content-Type: application/json");
$data = json_decode(file_get_contents("php://input"), true);
$asientos = $data['asientos'] ?? [];
$concierto_id = $data['concierto_id'] ?? null;
if (empty($asientos) || !$concierto_id) {
echo json_encode(["error" => "No se recibieron asientos para la compra."]);
exit;
}
$conn->begin_transaction();
try {
// Generar un ID de transacción único
$transaction_id = uniqid("trx_");
foreach ($asientos as $asiento_id) {
// **Depuración**
error_log("Procesando asiento ID: $asiento_id para Concierto ID: $concierto_id");
// Marcar asiento como vendido
$sql = "UPDATE asientos SET estado = 'vendido' WHERE id = ?";
$stmt = $conn->prepare($sql);
if (!$stmt) {
throw new Exception("Error en la preparación del UPDATE: " . $conn->error);
}
$stmt->bind_param("i", $asiento_id);
$stmt->execute();
// Insertar en la tabla de boletos
$sql_boleto = "INSERT INTO boletos (asiento_id, concierto_id, transaction_id, fecha_venta, precio)
SELECT ?, ?, ?, NOW(), z.precio FROM asientos a
JOIN zonas z ON a.zona_id = z.id WHERE a.id = ?";
$stmt_boleto = $conn->prepare($sql_boleto);
if (!$stmt_boleto) {
throw new Exception("Error en la preparación del INSERT: " . $conn->error);
}
$stmt_boleto->bind_param("iisi", $asiento_id, $concierto_id, $transaction_id, $asiento_id);
$stmt_boleto->execute();
}
$conn->commit();
echo json_encode(["success" => "Compra realizada con éxito", "transaction_id" => $transaction_id]);
} catch (Exception $e) {
$conn->rollback();
error_log("Error en la compra: " . $e->getMessage());
echo json_encode(["error" => "Error en la compra: " . $e->getMessage()]);
}
?>

51
php/comprobante.php Normal file
View File

@ -0,0 +1,51 @@
<?php
include 'conexion.php';
$transaction_id = $_GET['transaction_id'] ?? null;
if (!$transaction_id) {
die("Error: No se proporcionó un ID de transacción.");
}
$sql = "SELECT b.id, c.nombre AS concierto, c.hora, a.numero, z.nombre AS zona, b.fecha_venta, b.precio
FROM boletos b
JOIN asientos a ON b.asiento_id = a.id
JOIN zonas z ON a.zona_id = z.id
JOIN conciertos c ON z.concierto_id = c.id
WHERE b.transaction_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $transaction_id);
$stmt->execute();
$result = $stmt->get_result();
$boletos = $result->fetch_all(MYSQLI_ASSOC);
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Comprobante de Compra</title>
<link rel="stylesheet" href="../css/comprobante.css">
</head>
<body>
<div class="comprobante-container">
<h1>Comprobante de Compra</h1>
<?php if (empty($boletos)): ?>
<p>No hay boletos registrados para esta compra.</p>
<?php else: ?>
<?php foreach ($boletos as $boleto): ?>
<div class="boleto">
<p><strong>Concierto:</strong> <?= $boleto['concierto'] ?></p>
<p><strong>Hora:</strong> <?= $boleto['hora'] ?></p>
<p><strong>Zona:</strong> <?= $boleto['zona'] ?></p>
<p><strong>Asiento:</strong> <?= $boleto['numero'] ?></p>
<p><strong>Precio:</strong> $<?= number_format($boleto['precio'], 2) ?></p>
<p><strong>Fecha de compra:</strong> <?= $boleto['fecha_venta'] ?></p>
</div>
<hr>
<?php endforeach; ?>
<?php endif; ?>
<a href="../views/venta_boletos.html" class="boton-volver">Volver</a>
</div>
</body>
</html>

12
php/conciertos.php Normal file
View File

@ -0,0 +1,12 @@
<?php
include 'conexion.php';
$sql = "SELECT * FROM conciertos";
$result = $conn->query($sql);
$conciertos = [];
while ($row = $result->fetch_assoc()) {
$conciertos[] = $row;
}
echo json_encode($conciertos);
?>

13
php/conexion.php Normal file
View File

@ -0,0 +1,13 @@
<?php
$host = "127.0.0.1";
$user = "root";
$password = "root";
$dbname = "BoleTC";
$port = 8889; // Puerto de MySQL en MAMP
$conn = new mysqli($host, $user, $password, $dbname, $port);
if ($conn->connect_error) {
die("Error de conexión: " . $conn->connect_error);
}
?>

47
php/editar_concierto.php Normal file
View File

@ -0,0 +1,47 @@
<?php
include 'conexion.php';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$id = $_POST['id'];
$nombre = $_POST['nombre'];
$artista = $_POST['artista'];
$fecha = $_POST['fecha'];
$hora = $_POST['hora'];
$direccion = $_POST['direccion'];
$precio_vip = $_POST['precio_vip'];
$precio_general = $_POST['precio_general'];
// Verificar si se subió una nueva imagen
if (!empty($_FILES["imagen"]["name"])) {
$imagenNombre = time() . "_" . $_FILES["imagen"]["name"];
$rutaImagen = "../img/" . $imagenNombre;
move_uploaded_file($_FILES["imagen"]["tmp_name"], $rutaImagen);
// Actualizar con nueva imagen
$sql = "UPDATE conciertos SET nombre=?, artista=?, fecha=?, hora=?, direccion=?, imagen=? WHERE id=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ssssssi", $nombre, $artista, $fecha, $hora, $direccion, $imagenNombre, $id);
} else {
// Actualizar sin cambiar la imagen
$sql = "UPDATE conciertos SET nombre=?, artista=?, fecha=?, hora=?, direccion=? WHERE id=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("sssssi", $nombre, $artista, $fecha, $hora, $direccion, $id);
}
if ($stmt->execute()) {
// Actualizar precios de zonas
$sql_zonas = "UPDATE zonas SET precio = CASE
WHEN nombre = 'VIP' THEN ?
WHEN nombre = 'General' THEN ?
ELSE precio END
WHERE concierto_id = ?";
$stmt_zonas = $conn->prepare($sql_zonas);
$stmt_zonas->bind_param("ddi", $precio_vip, $precio_general, $id);
$stmt_zonas->execute();
echo "Concierto actualizado correctamente";
} else {
echo "Error al actualizar concierto: " . $conn->error;
}
}
?>

View File

@ -0,0 +1,20 @@
<?php
include 'conexion.php';
$data = json_decode(file_get_contents("php://input"), true);
$id = $data['id'] ?? null;
if (!$id) {
die("Error: No se recibió un ID válido.");
}
$sql = "DELETE FROM conciertos WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $id);
if ($stmt->execute()) {
echo "Concierto eliminado correctamente.";
} else {
echo "Error al eliminar concierto: " . $conn->error;
}
?>

View File

@ -0,0 +1,18 @@
<?php
include 'conexion.php';
// Obtener la fecha actual
$fecha_actual = date("Y-m-d");
// Eliminar conciertos cuya fecha ya pasó
$sql = "DELETE FROM conciertos WHERE fecha < ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $fecha_actual);
$sql = "DELETE FROM conciertos WHERE CONCAT(fecha, ' ', hora) < NOW()";
if ($stmt->execute()) {
echo "Conciertos pasados eliminados.";
} else {
echo "Error al eliminar conciertos: " . $stmt->error;
}
?>

33
php/exportar_csv.php Normal file
View File

@ -0,0 +1,33 @@
<?php
include 'conexion.php';
$fechaInicio = $_GET['fechaInicio'] ?? null;
$fechaFin = $_GET['fechaFin'] ?? null;
if (!$fechaInicio || !$fechaFin) {
die("Debe seleccionar un período de fechas.");
}
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=reporte_ventas.csv');
$output = fopen("php://output", "w");
fputcsv($output, ['ID', 'Concierto', 'Fecha', 'Hora', 'Zona', 'Asiento', 'Precio', 'Fecha Venta']);
$sql = "SELECT b.id, c.nombre AS concierto, c.fecha, c.hora, z.nombre AS zona, a.numero AS asiento, b.precio, b.fecha_venta
FROM boletos b
JOIN asientos a ON b.asiento_id = a.id
JOIN zonas z ON a.zona_id = z.id
JOIN conciertos c ON z.concierto_id = c.id
WHERE DATE(b.fecha_venta) BETWEEN ? AND ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ss", $fechaInicio, $fechaFin);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
fputcsv($output, $row);
}
fclose($output);
?>

View File

@ -0,0 +1,83 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
include 'conexion.php';
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
die("Error: La solicitud no es POST.");
}
$nombre = $_POST['nombre'] ?? '';
$artista = $_POST['artista'] ?? '';
$fecha = $_POST['fecha'] ?? '';
$hora = $_POST['hora'] ?? '';
$direccion = $_POST['direccion'] ?? '';
$precio_vip = $_POST['precio_vip'] ?? null;
$precio_general = $_POST['precio_general'] ?? null;
if (empty($nombre) || empty($artista) || empty($fecha) || empty($hora) || empty($direccion) || empty($precio_vip) || empty($precio_general)) {
die("Error: Todos los campos son obligatorios.");
}
// Manejo de imagen
if (!empty($_FILES["imagen"]["name"])) {
$extension = pathinfo($_FILES["imagen"]["name"], PATHINFO_EXTENSION);
$imagenNombre = time() . "_" . uniqid() . "." . $extension;
$rutaImagen = __DIR__ . "/../img/" . $imagenNombre;
if (!move_uploaded_file($_FILES["imagen"]["tmp_name"], $rutaImagen)) {
die("Error: No se pudo mover la imagen.");
}
} else {
$imagenNombre = "default.jpg";
}
$conn->begin_transaction();
try {
// Insertar el concierto con hora
$sql = "INSERT INTO conciertos (nombre, artista, fecha, hora, direccion, imagen) VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ssssss", $nombre, $artista, $fecha, $hora, $direccion, $imagenNombre);
$stmt->execute();
$concierto_id = $stmt->insert_id;
// Insertar las zonas (VIP y General)
$sql_zonas = "INSERT INTO zonas (concierto_id, nombre, capacidad, precio) VALUES
(?, 'VIP', 5, ?),
(?, 'General', 10, ?)";
$stmt_zonas = $conn->prepare($sql_zonas);
$stmt_zonas->bind_param("idid", $concierto_id, $precio_vip, $concierto_id, $precio_general);
$stmt_zonas->execute();
// Obtener los IDs de las zonas insertadas
$zona_vip_id = $conn->insert_id;
$zona_general_id = $zona_vip_id + 1;
// Insertar los asientos (VIP y General)
$sql_asientos = "INSERT INTO asientos (zona_id, numero, estado) VALUES ";
$values = [];
$params = [];
for ($i = 1; $i <= 5; $i++) {
$values[] = "(?, ?, 'disponible')";
array_push($params, $zona_vip_id, $i);
}
for ($i = 1; $i <= 10; $i++) {
$values[] = "(?, ?, 'disponible')";
array_push($params, $zona_general_id, $i);
}
$sql_asientos .= implode(", ", $values);
$stmt_asientos = $conn->prepare($sql_asientos);
$stmt_asientos->bind_param(str_repeat("ii", count($params) / 2), ...$params);
$stmt_asientos->execute();
$conn->commit();
echo "Concierto agregado con éxito.";
} catch (Exception $e) {
$conn->rollback();
die("Error en la transacción: " . $e->getMessage());
}
?>

View File

@ -0,0 +1,13 @@
<?php
include 'conexion.php';
$sql = "SELECT id, nombre FROM conciertos";
$result = $conn->query($sql);
$conciertos = [];
while ($row = $result->fetch_assoc()) {
$conciertos[] = $row;
}
header('Content-Type: application/json');
echo json_encode($conciertos);
?>

View File

@ -0,0 +1,38 @@
<?php
include 'conexion.php';
$id = $_GET['id'] ?? null;
if (!$id) {
die(json_encode(["error" => "No se proporcionó un ID de evento."]));
}
// Obtener detalles básicos del concierto
$sql = "SELECT nombre, artista, fecha, hora, direccion FROM conciertos WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$concierto = $result->fetch_assoc();
if (!$concierto) {
die(json_encode(["error" => "Concierto no encontrado."]));
}
// Obtener precios de zonas
$sql_zonas = "SELECT nombre, precio FROM zonas WHERE concierto_id = ?";
$stmt = $conn->prepare($sql_zonas);
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$zonas = $result->fetch_all(MYSQLI_ASSOC);
foreach ($zonas as $zona) {
if ($zona['nombre'] === 'VIP') {
$concierto['precio_vip'] = $zona['precio'];
} else if ($zona['nombre'] === 'General') {
$concierto['precio_general'] = $zona['precio'];
}
}
echo json_encode($concierto);
?>

28
php/reporte_ventas.php Normal file
View File

@ -0,0 +1,28 @@
<?php
include 'conexion.php';
$fechaInicio = $_GET['fechaInicio'] ?? null;
$fechaFin = $_GET['fechaFin'] ?? null;
if (!$fechaInicio || !$fechaFin) {
die(json_encode(["error" => "Debe seleccionar un período de fechas."]));
}
$sql = "SELECT b.id, c.nombre AS concierto, c.fecha, c.hora, z.nombre AS zona, a.numero AS asiento, b.precio, b.fecha_venta
FROM boletos b
JOIN asientos a ON b.asiento_id = a.id
JOIN zonas z ON a.zona_id = z.id
JOIN conciertos c ON z.concierto_id = c.id
WHERE DATE(b.fecha_venta) BETWEEN ? AND ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ss", $fechaInicio, $fechaFin);
$stmt->execute();
$result = $stmt->get_result();
$ventas = [];
while ($row = $result->fetch_assoc()) {
$ventas[] = $row;
}
echo json_encode($ventas);
?>

61
views/agregar_evento.html Normal file
View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agregar Evento - BoleTC</title>
<link rel="stylesheet" href="../css/agregar_evento.css">
</head>
<body>
<h1>Agregar Nuevo Evento</h1>
<form id="formConcierto" action="../php/insertar_concierto.php" method="POST" enctype="multipart/form-data">
<label>Nombre del Concierto:</label>
<input type="text" name="nombre" required>
<label>Artista:</label>
<input type="text" name="artista" required>
<label>Fecha del Concierto:</label>
<input type="date" name="fecha" required>
<label>Hora del Concierto:</label>
<input type="time" name="hora" required>
<label>Dirección:</label>
<input type="text" name="direccion" required>
<label>Imagen del Concierto:</label>
<input type="file" name="imagen" accept="image/*" required>
<h3>Zonas</h3>
<div class="zona-container">
<label>Zona: <strong>VIP</strong></label>
<input type="hidden" name="zona_vip" value="VIP">
<label>Capacidad:</label>
<input type="number" value="5" readonly> <!-- No editable -->
<label>Precio:</label>
<input type="number" name="precio_vip" min="1" required>
</div>
<div class="zona-container">
<label>Zona: <strong>General</strong></label>
<input type="hidden" name="zona_general" value="General">
<label>Capacidad:</label>
<input type="number" value="10" readonly> <!-- No editable -->
<label>Precio:</label>
<input type="number" name="precio_general" min="1" required>
</div>
<button type="submit">Agregar Concierto</button>
<a href="../views/index.html" class="boton-volver">Volver</a>
</form>
<div id="mensaje"></div>
<script src="../js/agregar_evento.js"></script>
</body>
</html>

53
views/editar_evento.html Normal file
View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Editar Evento - BoleTC</title>
<link rel="stylesheet" href="../css/editar_evento.css">
</head>
<body>
<h1>Editar Evento</h1>
<form id="formEditarEvento" enctype="multipart/form-data">
<label>Seleccionar Evento:</label>
<select id="eventoSeleccionado" name="id" required>
<option value="">Seleccione un evento</option>
</select>
<label>Nuevo Nombre:</label>
<input type="text" name="nombre">
<label>Nuevo Artista:</label>
<input type="text" name="artista">
<label>Nueva Fecha:</label>
<input type="date" name="fecha">
<label>Nueva Hora:</label>
<input type="time" name="hora">
<label>Nueva Dirección:</label>
<input type="text" name="direccion">
<label>Precio VIP:</label>
<input type="number" name="precio_vip" min="0" step="0.01" required>
<label>Precio General:</label>
<input type="number" name="precio_general" min="0" step="0.01" required>
<label>Nueva Imagen:</label>
<input type="file" name="imagen" accept="image/*">
<button type="submit">Actualizar Concierto</button>
<button type="button" id="btnEliminar">Eliminar Concierto</button>
<a href="../views/index.html" class="boton-volver">Volver</a>
</form>
<div id="mensaje"></div>
<script src="../js/editar_evento.js"></script>
</body>
</html>

29
views/index.html Normal file
View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BoleTC - Inicio</title>
<link rel="stylesheet" href="../css/estilos.css">
<script src="../js/conciertos.js" defer></script>
</head>
<body>
<header>
<h1>BoleTC</h1>
<nav>
<a href="venta_boletos.html">Comprar Boletos</a>
<a href="agregar_evento.html">Agregar Evento</a>
<a href="editar_evento.html">Editar Evento</a>
<a href="reporte_ventas.html">Reporte Ventas</a>
</nav>
</header>
<div class="contenedor-principal">
<div class="contenedor-conciertos" id="conciertos">
<!-- Los conciertos se cargarán aquí dinámicamente -->
</div>
</div>
</body>
</html>

47
views/reporte_ventas.html Normal file
View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reporte de Ventas - BoleTC</title>
<link rel="stylesheet" href="../css/reporte_ventas.css">
</head>
<body>
<h1>Reporte de Ventas</h1>
<label for="fechaInicio">Fecha de Inicio:</label>
<input type="date" id="fechaInicio">
<label for="fechaFin">Fecha Fin:</label>
<input type="date" id="fechaFin">
<button id="generarReporte">Generar Reporte</button>
<button id="exportarCSV">Exportar a CSV</button>
<div id="reporteContainer">
<table>
<thead>
<tr>
<th>ID</th>
<th>Concierto</th>
<th>Fecha</th>
<th>Hora</th>
<th>Zona</th>
<th>Asiento</th>
<th>Precio</th>
<th>Fecha Venta</th>
</tr>
</thead>
<tbody id="tablaReporte">
<!-- Los datos se insertarán aquí con JavaScript -->
</tbody>
</table>
</div>
<button onclick="window.location.href='index.html'">Volver</button>
<script src="../js/reporte_ventas.js"></script>
</body>
</html>

36
views/venta_boletos.html Normal file
View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Venta de Boletos - BoleTC</title>
<link rel="stylesheet" href="../css/venta_boletos.css">
<script src="../js/venta_boletos.js" defer></script>
</head>
<body>
<div class="container">
<h1>Venta de Boletos</h1>
<label for="concierto">Seleccionar Concierto:</label>
<select id="concierto"></select>
<div id="asientosContainer">
<div class="zona-container">
<div class="zona-titulo">VIP</div>
<div id="asientosVIP" class="fila-asientos"></div>
</div>
<div class="zona-container">
<div class="zona-titulo">General</div>
<div id="asientosGeneral" class="fila-asientos"></div>
</div>
</div>
<button id="confirmarCompra">Confirmar Compra</button>
<a href="../views/index.html" class="boton-volver">Volver</a>
</div>
</body>
</html>