commit 437aa1ef068c48dbada9a11d80b99f394d0b8809 Author: Fernando Escobar Robles <zs22016116@estudiantes.uv.mx> Date: Sun Mar 9 23:02:14 2025 -0600 version 09/03/25 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..699142a --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# ---> NetBeans +**/nbproject/private/ +**/nbproject/Makefile-*.mk +**/nbproject/Package-*.bash +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + diff --git a/README.md b/README.md new file mode 100644 index 0000000..4732d8c --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ + +# Proyecto TicketFei + +Descripcion del Proyecto a realizar: + +Para el desarrollo de la aplicación de venta de boletos, se utilizarán HTML, CSS, JavaScript y PHP, ya que permiten construir una solución web eficiente, accesible y fácil de gestionar.Esta combinación de tecnologías garantiza una aplicación funcional, dinámica y escalable, cumpliendo con los requisitos del proyecto. + +Funcionalidades: +HU01: Selección y Venta de Boletos + +HU02: Generación de Reporte de Ventas + +HU03: Gestión de Eventos y Conciertos + +HU04: Cancelación de Venta de Boletos + +HU05: Edición de Boletos Vendidos o Reservados + +HU06: Búsqueda y Filtrado de Boletos +## Autores + +- [@FernandoEscobar](https://git.gumoio.com/fernando.escobar) +- [@AxelVasquez](https://git.gumoio.com/axel.vasquez) +- [@JorgeOrtega](https://git.gumoio.com/jorge.ortega) + + + + diff --git a/bd/bd.sql b/bd/bd.sql new file mode 100644 index 0000000..c28cd61 --- /dev/null +++ b/bd/bd.sql @@ -0,0 +1,5 @@ +create database TicketFei; +CREATE USER 'desarrolloTicketFei'@'localhost' IDENTIFIED BY 'password'; +GRANT CREATE,ALTER,DROP,INSERT,UPDATE,DELETE,SELECT,CREATE VIEW,REFERENCES on TicketFei.* to 'desarrolloTicketFei'@'localhost'; +GRANT LOCK TABLES, PROCESS ON *.* TO 'desarrolloTicketFei'@'localhost'; +FLUSH PRIVILEGES; diff --git a/bd/tablas.sql b/bd/tablas.sql new file mode 100644 index 0000000..6ac4921 --- /dev/null +++ b/bd/tablas.sql @@ -0,0 +1,85 @@ +USE TicketFei; + +CREATE TABLE usuarios ( + id INT AUTO_INCREMENT PRIMARY KEY, + nombre VARCHAR(255) NOT NULL, + apellidoPaterno VARCHAR(255) NOT NULL, + apellidoMaterno VARCHAR(255) NOT NULL, + usuario VARCHAR(255) NOT NULL, + contraseña VARCHAR(255) NOT NULL +); +INSERT INTO usuarios (nombre, apellidoPaterno, apellidoMaterno, usuario, contraseña) VALUES +('Aaron', 'Bonilla', 'Gonzalez', 's22', '123'), +('Carlos', 'Palestina', 'Alducin', 's23', '123'), +('Miguel', 'Diaz', 'Villa', 's24', '123'); + +SELECT * FROM usuarios; +DROP TABLE usuarios; + +-- concierto +CREATE TABLE conciertos ( + id_concierto INT AUTO_INCREMENT PRIMARY KEY, + nombre_concierto VARCHAR(255) NOT NULL, + artista VARCHAR(255) NOT NULL, + fecha DATE NOT NULL, + calle VARCHAR(255) NOT NULL, + colonia VARCHAR(255) NOT NULL, + numero_direccion VARCHAR(255) NOT NULL, + codigo_postal VARCHAR(10) NOT NULL, + estado VARCHAR(255) NOT NULL, + capacidad_total INT NOT NULL +); +INSERT INTO conciertos (nombre_concierto, artista, fecha, calle, colonia, numero_direccion, codigo_postal, estado, capacidad_total) +SELECT * FROM conciertos; +DROP TABLE conciertos; +-- Zona +CREATE TABLE zonas ( + id_zona INT AUTO_INCREMENT PRIMARY KEY, + id_concierto INT NOT NULL, + nombre_zona ENUM('General', 'Plata', 'Oro', 'VIP') NOT NULL, + capacidad INT NOT NULL, + precio DECIMAL(10,2) NOT NULL, + FOREIGN KEY (id_concierto) REFERENCES conciertos(id_concierto) ON DELETE CASCADE +); + +DROP TABLE zonas; +-- Obtener todos los conciertos con sus zonas y precios +SELECT c.id_concierto, c.nombre_concierto, c.artista, c.fecha, + z.nombre_zona, z.capacidad, z.precio +FROM conciertos c +JOIN zonas z ON c.id_concierto = z.id_concierto; + +-- Consultar un concierto específico con sus zonas +SELECT c.nombre_concierto, c.artista, c.fecha, + z.nombre_zona, z.capacidad, z.precio +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; \ No newline at end of file diff --git a/bd/ticket_fei.sql b/bd/ticket_fei.sql new file mode 100644 index 0000000..7faa400 --- /dev/null +++ b/bd/ticket_fei.sql @@ -0,0 +1,55 @@ +-- MySQL dump 10.13 Distrib 8.0.34, for Win64 (x86_64) +-- +-- Host: 127.0.0.1 Database: ticketfei +-- ------------------------------------------------------ +-- Server version 8.0.34 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `usuarios` +-- + +DROP TABLE IF EXISTS `usuarios`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `usuarios` ( + `id` int NOT NULL AUTO_INCREMENT, + `nombre` varchar(255) NOT NULL, + `apellidoPaterno` varchar(255) NOT NULL, + `apellidoMaterno` varchar(255) NOT NULL, + `usuario` varchar(255) NOT NULL, + `contraseña` varchar(255) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `usuarios` +-- + +LOCK TABLES `usuarios` WRITE; +/*!40000 ALTER TABLE `usuarios` DISABLE KEYS */; +INSERT INTO `usuarios` VALUES (1,'Aaron','Bonilla','Gonzalez','s22','123'),(2,'Carlos','Palestina','Alducin','s23','123'),(3,'Miguel','Diaz','Villa','s24','123'); +/*!40000 ALTER TABLE `usuarios` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2025-02-19 10:38:05 diff --git a/controladores/actualizar_concierto.php b/controladores/actualizar_concierto.php new file mode 100644 index 0000000..411b69b --- /dev/null +++ b/controladores/actualizar_concierto.php @@ -0,0 +1,68 @@ +<?php +include 'conexion.php'; + +if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['id'])) { + $id = intval($_GET['id']); + $consulta = "SELECT * FROM conciertos WHERE id_concierto = ?"; + $stmt = $conexionBD->prepare($consulta); + $stmt->bind_param("i", $id); + $stmt->execute(); + $resultado = $stmt->get_result(); + $concierto = $resultado->fetch_assoc(); + + if ($concierto) { + $consulta_zonas = "SELECT * FROM zonas WHERE id_concierto = ?"; + $stmt_zonas = $conexionBD->prepare($consulta_zonas); + $stmt_zonas->bind_param("i", $id); + $stmt_zonas->execute(); + $resultado_zonas = $stmt_zonas->get_result(); + while ($zona = $resultado_zonas->fetch_assoc()) { + $concierto['zonas'][] = $zona; + } + echo json_encode($concierto); + } else { + echo json_encode(["error" => "Concierto no encontrado"]); + } + exit; +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $datos = json_decode(file_get_contents("php://input"), true); + if (!$datos || !isset($datos['id'])) { + echo json_encode(["actualizacionCorrecta" => false, "error" => "Datos incompletos"]); + exit; + } + + // Actualizar la información del concierto + $consulta = "UPDATE conciertos SET nombre_concierto=?, artista=?, fecha=?, calle=?, colonia=?, numero_direccion=?, codigo_postal=?, estado=?, capacidad_total=? WHERE id_concierto=?"; + $stmt = $conexionBD->prepare($consulta); + $stmt->bind_param("ssssssissi", + $datos['nombre_concierto'], + $datos['artista'], + $datos['fecha'], + $datos['calle'], + $datos['colonia'], + $datos['numero_direccion'], + $datos['codigo_postal'], + $datos['estado'], + $datos['capacidad_total'], + $datos['id'] + ); + $stmt->execute(); + + // Actualizar la información de las zonas + foreach ($datos['zonas'] as $zona) { + $consulta_zonas = "UPDATE zonas SET capacidad=?, precio=? WHERE id_concierto=? AND nombre_zona=?"; + $stmt_zona = $conexionBD->prepare($consulta_zonas); + $stmt_zona->bind_param("idis", + $zona['capacidad'], + $zona['precio'], + $datos['id'], + $zona['nombre_zona'] + ); + $stmt_zona->execute(); + } + + echo json_encode(["actualizacionCorrecta" => true]); + exit; +} \ No newline at end of file diff --git a/controladores/cerrar_sesion.php b/controladores/cerrar_sesion.php new file mode 100644 index 0000000..6675094 --- /dev/null +++ b/controladores/cerrar_sesion.php @@ -0,0 +1,21 @@ +<?php + + include 'conexion.php'; + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + session_start(); + + session_unset(); + session_destroy(); + + echo json_encode(['success' => true]); + } else { + http_response_code(405); + echo json_encode(['success' => false, 'message' => 'Método no permitido']); + } + + if (isset($conexionBD)) { + $conexionBD->close(); + } + +?> diff --git a/controladores/comprar_asiento.php b/controladores/comprar_asiento.php new file mode 100644 index 0000000..c1327c3 --- /dev/null +++ b/controladores/comprar_asiento.php @@ -0,0 +1,41 @@ +<?php +include 'conexion.php'; + +header('Content-Type: application/json'); + +$rawData = file_get_contents("php://input"); +$data = json_decode($rawData, true); + +if (!isset($data['asientos']) || !is_array($data['asientos'])) { + echo json_encode(["error" => "Datos inválidos"]); + exit; +} + +$conexionBD->begin_transaction(); + +try { + foreach ($data['asientos'] as $idAsiento) { + // Verificar si el asiento sigue disponible + $stmtVerificar = $conexionBD->prepare("SELECT estado FROM asientos WHERE id_asiento = ?"); + $stmtVerificar->bind_param("i", $idAsiento); + $stmtVerificar->execute(); + $resultado = $stmtVerificar->get_result()->fetch_assoc(); + + if (!$resultado || $resultado['estado'] !== 'disponible') { + throw new Exception("El asiento ID $idAsiento ya está ocupado o no existe."); + } + + // Marcar el asiento como ocupado + $stmtActualizar = $conexionBD->prepare("UPDATE asientos SET estado = 'ocupado' WHERE id_asiento = ?"); + $stmtActualizar->bind_param("i", $idAsiento); + $stmtActualizar->execute(); + } + + $conexionBD->commit(); + echo json_encode(["success" => true]); +} catch (Exception $e) { + $conexionBD->rollback(); + echo json_encode(["error" => $e->getMessage()]); +} +exit; +?> diff --git a/controladores/concierto_zonas.php b/controladores/concierto_zonas.php new file mode 100644 index 0000000..f7458c1 --- /dev/null +++ b/controladores/concierto_zonas.php @@ -0,0 +1,111 @@ +<?php +include 'conexion.php'; + +header('Content-Type: application/json'); + +if ($_SERVER['REQUEST_METHOD'] === 'GET') { + // Verificar si es solicitud de concierto + if (isset($_GET['id_concierto'])) { + $idConcierto = intval($_GET['id_concierto']); + + // Obtener la información del concierto + $consultaConcierto = "SELECT * FROM conciertos WHERE id_concierto = ?"; + $stmt = $conexionBD->prepare($consultaConcierto); + $stmt->bind_param("i", $idConcierto); + $stmt->execute(); + $resultadoConcierto = $stmt->get_result(); + $concierto = $resultadoConcierto->fetch_assoc(); + + if (!$concierto) { + echo json_encode(["error" => "Concierto no encontrado"]); + exit; + } + + // Obtener zonas del concierto + $consultaZonas = "SELECT * FROM zonas WHERE id_concierto = ?"; + $stmtZonas = $conexionBD->prepare($consultaZonas); + $stmtZonas->bind_param("i", $idConcierto); + $stmtZonas->execute(); + $resultadoZonas = $stmtZonas->get_result(); + + $zonas = []; + while ($zona = $resultadoZonas->fetch_assoc()) { + // Contar asientos disponibles y ocupados + $consultaAsientos = "SELECT estado FROM asientos WHERE id_zona = ?"; + $stmtAsientos = $conexionBD->prepare($consultaAsientos); + $stmtAsientos->bind_param("i", $zona['id_zona']); + $stmtAsientos->execute(); + $resultadoAsientos = $stmtAsientos->get_result(); + + $asientos_disponibles = 0; + $asientos_ocupados = 0; + + while ($asiento = $resultadoAsientos->fetch_assoc()) { + if ($asiento['estado'] === 'disponible') { + $asientos_disponibles++; + } else { + $asientos_ocupados++; + } + } + + // Agregar datos de asientos a la zona + $zona['asientos_disponibles'] = $asientos_disponibles; + $zona['asientos_ocupados'] = $asientos_ocupados; + + $zonas[] = $zona; + } + + // Agregar las zonas al concierto + $concierto['zonas'] = $zonas; + + echo json_encode($concierto); + exit; + } + + // Verificar si es solicitud de zona + if (isset($_GET['id_zona'])) { + $idZona = intval($_GET['id_zona']); + + // Obtener la información de la zona específica + $consultaZona = "SELECT * FROM zonas WHERE id_zona = ?"; + $stmtZona = $conexionBD->prepare($consultaZona); + $stmtZona->bind_param("i", $idZona); + $stmtZona->execute(); + $resultadoZona = $stmtZona->get_result(); + $zona = $resultadoZona->fetch_assoc(); + + if (!$zona) { + echo json_encode(["error" => "Zona no encontrada"]); + exit; + } + + // Obtener asientos de la zona + $consultaAsientos = "SELECT estado FROM asientos WHERE id_zona = ?"; + $stmtAsientos = $conexionBD->prepare($consultaAsientos); + $stmtAsientos->bind_param("i", $idZona); + $stmtAsientos->execute(); + $resultadoAsientos = $stmtAsientos->get_result(); + + $asientos_disponibles = 0; + $asientos_ocupados = 0; + + while ($asiento = $resultadoAsientos->fetch_assoc()) { + if ($asiento['estado'] === 'disponible') { + $asientos_disponibles++; + } else { + $asientos_ocupados++; + } + } + + // Agregar información de asientos a la zona + $zona['asientos_disponibles'] = $asientos_disponibles; + $zona['asientos_ocupados'] = $asientos_ocupados; + + echo json_encode($zona); + exit; + } + + echo json_encode(["error" => "Solicitud incorrecta"]); + exit; +} +?> diff --git a/controladores/conciertos.php b/controladores/conciertos.php new file mode 100644 index 0000000..2d7ad36 --- /dev/null +++ b/controladores/conciertos.php @@ -0,0 +1,51 @@ +<?php +include 'conexion.php'; + +header('Content-Type: application/json'); // Asegurar que el contenido es JSON +error_reporting(E_ALL); +ini_set('display_errors', 1); + +$consulta = "SELECT c.id_concierto, c.nombre_concierto, c.artista, c.fecha, c.calle, c.colonia, c.numero_direccion, c.codigo_postal, c.estado, c.capacidad_total, + z.nombre_zona, z.capacidad AS capacidad_zona, z.precio + FROM conciertos c + LEFT JOIN zonas z ON c.id_concierto = z.id_concierto + ORDER BY c.id_concierto, + FIELD(z.nombre_zona, 'General', 'Plata', 'Oro', 'VIP')"; + +$resultado = $conexionBD->query($consulta); + +if (!$resultado) { + echo json_encode(["error" => "Error en la consulta SQL: " . $conexionBD->error]); + exit; +} + +$conciertos = []; + +while ($fila = $resultado->fetch_assoc()) { + $id_concierto = $fila['id_concierto']; + if (!isset($conciertos[$id_concierto])) { + $conciertos[$id_concierto] = [ + 'id' => $id_concierto, + 'nombre_concierto' => $fila['nombre_concierto'], + 'artista' => $fila['artista'], + 'fecha' => $fila['fecha'], + 'direccion' => $fila['calle'] . ', ' . $fila['colonia'] . ', ' . $fila['numero_direccion'] . ', CP: ' . $fila['codigo_postal'] . ', ' . $fila['estado'], + 'capacidad_total' => $fila['capacidad_total'], + 'zonas' => [] + ]; + } + + if (!empty($fila['nombre_zona'])) { + $conciertos[$id_concierto]['zonas'][] = [ + 'nombre_zona' => $fila['nombre_zona'], + 'capacidad' => $fila['capacidad_zona'], + 'precio' => $fila['precio'] + ]; + } +} + +// Convertir el array a JSON y enviarlo +echo json_encode(array_values($conciertos), JSON_PRETTY_PRINT); + +$conexionBD->close(); +?> diff --git a/controladores/conexion.php b/controladores/conexion.php new file mode 100644 index 0000000..710c5ae --- /dev/null +++ b/controladores/conexion.php @@ -0,0 +1,18 @@ +<?php + error_reporting(E_ALL); + ini_set('display_errors', 1); + + // Valores para la conexión + $direccion = "localhost:3306"; + $nombreBD = "TicketFei"; + $usuario = "desarrolloTicketFei"; + $password = "password"; + + // Obtener conexión + $conexionBD = new mysqli($direccion, $usuario, $password, $nombreBD); + + // Verificar si hay un error en la conexión + if ($conexionBD->connect_error) { + die(json_encode(["error" => "Error en la conexión: " . $conexionBD->connect_error])); + } +?> diff --git a/controladores/eliminar_concierto.php b/controladores/eliminar_concierto.php new file mode 100644 index 0000000..b1553d3 --- /dev/null +++ b/controladores/eliminar_concierto.php @@ -0,0 +1,35 @@ +<?php +include 'conexion.php'; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $datos = json_decode(file_get_contents("php://input"), true); + + if (!isset($datos['id'])) { + echo json_encode(["eliminacionCorrecta" => false, "error" => "ID de concierto no proporcionado"]); + exit; + } + + $id = intval($datos['id']); + + // Eliminar las zonas relacionadas con el concierto + $consulta_zonas = "DELETE FROM zonas WHERE id_concierto = ?"; + $stmt_zonas = $conexionBD->prepare($consulta_zonas); + $stmt_zonas->bind_param("i", $id); + $stmt_zonas->execute(); + + // Eliminar el concierto + $consulta = "DELETE FROM conciertos WHERE id_concierto = ?"; + $stmt = $conexionBD->prepare($consulta); + $stmt->bind_param("i", $id); + $stmt->execute(); + + if ($stmt->affected_rows > 0) { + echo json_encode(["eliminacionCorrecta" => true]); + } else { + echo json_encode(["eliminacionCorrecta" => false, "error" => "No se pudo eliminar el concierto"]); + } + exit; +} else { + echo json_encode(["error" => "Método no permitido"]); + exit; +} diff --git a/controladores/insertar_concierto.php b/controladores/insertar_concierto.php new file mode 100644 index 0000000..b298ea4 --- /dev/null +++ b/controladores/insertar_concierto.php @@ -0,0 +1,79 @@ +<?php +include 'conexion.php'; + +header('Content-Type: application/json'); +error_reporting(E_ALL); +ini_set('display_errors', 1); + +// Leer el JSON enviado +$datos = json_decode(file_get_contents("php://input"), true); + +if ($datos === null) { + echo json_encode(['insercionCorrecta' => false, 'error' => 'Error al decodificar JSON']); + exit; +} + +// Verificar que los datos requeridos estén presentes +if (!isset($datos['nombre_concierto'], $datos['zonas'])) { + echo json_encode(['insercionCorrecta' => false, 'error' => 'Faltan datos requeridos']); + exit; +} + +// Extraer valores +$nombre_concierto = $datos['nombre_concierto']; +$artista = $datos['artista']; +$fecha = $datos['fecha']; +$calle = $datos['calle']; +$colonia = $datos['colonia']; +$numero_direccion = $datos['numero_direccion']; +$codigo_postal = $datos['codigo_postal']; +$estado = $datos['estado']; +$capacidad_total = $datos['capacidad_total']; +$zonas = $datos['zonas']; + +$conexionBD->begin_transaction(); + +try { + // Insertar el concierto + $consulta = "INSERT INTO conciertos (nombre_concierto, artista, fecha, calle, colonia, numero_direccion, codigo_postal, estado, capacidad_total) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + $stmt = $conexionBD->prepare($consulta); + $stmt->bind_param("ssssssssi", $nombre_concierto, $artista, $fecha, $calle, $colonia, $numero_direccion, $codigo_postal, $estado, $capacidad_total); + $stmt->execute(); + + $id_concierto = $conexionBD->insert_id; + $stmt->close(); + + // 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]); + +} catch (Exception $e) { + $conexionBD->rollback(); + echo json_encode(['insercionCorrecta' => false, 'error' => $e->getMessage()]); +} + +$conexionBD->close(); +?> diff --git a/controladores/login.php b/controladores/login.php new file mode 100644 index 0000000..0de8b92 --- /dev/null +++ b/controladores/login.php @@ -0,0 +1,32 @@ +<?php + session_start(); + include 'conexion.php'; + + $usuario = $_POST['usuario']; + $password = $_POST['password']; + + // Prepara la consulta + $consulta = "SELECT id, nombre, apellidoPaterno, apellidoMaterno, usuario FROM usuarios WHERE usuario = ? AND contraseña = ?"; + + $consultaPreparada = $conexionBD->prepare($consulta); + $consultaPreparada->bind_param("ss", $usuario, $password); + $consultaPreparada->execute(); + $resultado = $consultaPreparada->get_result(); + + // Verifica si se encontró un colaborador con las credenciales correctas + if ($resultado->num_rows > 0) { + $fila = $resultado->fetch_assoc(); + + // Guarda el ID y el usuario en la sesión + $_SESSION['usuarios_id'] = $fila['id']; + $_SESSION['usuarios_nombre'] = $fila['nombre']; + + echo json_encode(['loginExitoso' => true, 'usuarios_id' => $fila['id'], 'usuarios_nombre' => $fila['nombre']]); + } else { + // Credenciales incorrectas + echo json_encode(['loginExitoso' => false, 'error' => 'Credenciales inválidas']); + } + + $consultaPreparada->close(); + $conexionBD->close(); +?> diff --git a/controladores/obtener_asiento.php b/controladores/obtener_asiento.php new file mode 100644 index 0000000..a066f1a --- /dev/null +++ b/controladores/obtener_asiento.php @@ -0,0 +1,27 @@ +<?php +include 'conexion.php'; + +header('Content-Type: application/json'); + +if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['id_zona'])) { + $idZona = intval($_GET['id_zona']); + + // Consultar los asientos de la zona específica + $consultaAsientos = "SELECT id_asiento, estado FROM asientos WHERE id_zona = ?"; + $stmtAsientos = $conexionBD->prepare($consultaAsientos); + $stmtAsientos->bind_param("i", $idZona); + $stmtAsientos->execute(); + $resultadoAsientos = $stmtAsientos->get_result(); + + $asientos = []; + while ($asiento = $resultadoAsientos->fetch_assoc()) { + $asientos[] = $asiento; + } + + echo json_encode($asientos); + exit; +} else { + echo json_encode(["error" => "Parámetro id_zona faltante"]); + exit; +} +?> diff --git a/controladores/obtener_concierto.php b/controladores/obtener_concierto.php new file mode 100644 index 0000000..f6e6e11 --- /dev/null +++ b/controladores/obtener_concierto.php @@ -0,0 +1,38 @@ +<?php +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" => "ID de concierto no proporcionado"]); + exit; +} + +$id = intval($_GET['id']); +$consulta = "SELECT * FROM conciertos WHERE id_concierto = ?"; +$stmt = $conexionBD->prepare($consulta); +$stmt->bind_param("i", $id); +$stmt->execute(); +$resultado = $stmt->get_result(); +$concierto = $resultado->fetch_assoc(); + +if (!$concierto) { + echo json_encode(["error" => "Concierto no encontrado"]); + exit; +} + +$consulta_zonas = "SELECT * FROM zonas WHERE id_concierto = ?"; +$stmt_zonas = $conexionBD->prepare($consulta_zonas); +$stmt_zonas->bind_param("i", $id); +$stmt_zonas->execute(); +$resultado_zonas = $stmt_zonas->get_result(); + +$concierto['zonas'] = []; +while ($zona = $resultado_zonas->fetch_assoc()) { + $concierto['zonas'][] = $zona; +} + +echo json_encode($concierto); +exit; diff --git a/css/conciertos.css b/css/conciertos.css new file mode 100644 index 0000000..677a340 --- /dev/null +++ b/css/conciertos.css @@ -0,0 +1,186 @@ +body { + background-color: #12122B; + font-family: 'Poppins', sans-serif; + color: #E3E3E3; + margin: 0; + padding: 0; +} + +nav { + background-color: #12122B; + padding: 1rem 2rem; + display: flex; + align-items: center; + justify-content: space-between; + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.3); + position: sticky; + top: 0; + z-index: 1000; +} + +.navbar-brand { + font-size: 1.8rem; + font-weight: bold; + color: #E3E3E3; + text-decoration: none; +} + +.nav-links { + display: flex; + gap: 25px; +} + +.nav-links a { + color: #E3E3E3; + text-decoration: none; + font-size: 1.1rem; + font-weight: 500; + transition: color 0.3s; + padding-bottom: 5px; +} + +.nav-links a:hover { + color: #AAAA91; + border-bottom: 3px solid #AAAA91; +} + +.search-container { + display: flex; + background: #383845; + padding: 10px; + border-radius: 6px; + align-items: center; +} + +.search-container input { + border: none; + background: transparent; + color: #E3E3E3; + outline: none; + padding: 8px; + flex-grow: 1; +} + +.search-container button { + background: #AAAA91; + border: none; + padding: 8px 12px; + color: #12122B; + font-weight: bold; + cursor: pointer; + border-radius: 6px; +} + +.search-container button:hover { + background: #848478; +} + +#listaConciertos { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: 24px; + padding: 40px; + max-width: 1300px; + margin: auto; +} + +.concierto-card { + background-color: #383845; + border-radius: 12px; + overflow: hidden; + box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.3); + transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out; + display: flex; + flex-direction: column; + height: 100%; +} + +.concierto-card:hover { + transform: translateY(-6px); + box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.4); +} + +.concierto-card img { + width: 100%; + height: 180px; + object-fit: cover; +} + +.card-body { + padding: 16px; + flex-grow: 1; +} + +.card-title { + font-size: 1.3rem; + font-weight: bold; + color: #E3E3E3; +} + +.card-text { + font-size: 1rem; + color: #C4C4C4; +} + +.btn-comprar { + width: 100%; + background: #5CB85C; + color: #12122B; + font-weight: bold; + padding: 12px; + border-radius: 8px; + transition: background 0.3s; + text-align: center; + display: block; + margin-top: auto; +} + +.btn-comprar:hover { + background: #4CAF50; +} + +/* Contenedor del menú */ +.menu-container { + position: absolute; + top: 10px; + right: 10px; + z-index: 10; +} + +/* Botón de los tres puntos */ +.menu-btn { + background: #12122B; + border: none; + font-size: 20px; + color: white; + cursor: pointer; + padding: 5px 10px; + border-radius: 30%; +} + +.menu { + display: none; + position: absolute; + top: 30px; + right: 0; + background: #383845; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + z-index: 20; +} + +/* Opciones del menú */ +.menu button { + display: block; + width: 100%; + padding: 10px; + border: none; + background: #383845; + cursor: pointer; + text-align: left; + border-radius: 5px; +} + +.menu button:hover { + background: #3e3e48; +} diff --git a/css/fonts/Frutiger Bold Italic/Frutiger Bold Italic.ttf b/css/fonts/Frutiger Bold Italic/Frutiger Bold Italic.ttf new file mode 100644 index 0000000..a4e8024 Binary files /dev/null and b/css/fonts/Frutiger Bold Italic/Frutiger Bold Italic.ttf differ diff --git a/css/fonts/Frutiger Bold Italic/readme.html b/css/fonts/Frutiger Bold Italic/readme.html new file mode 100644 index 0000000..8dbdb1c --- /dev/null +++ b/css/fonts/Frutiger Bold Italic/readme.html @@ -0,0 +1,188 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta name="viewport" content="width=device-width" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="refresh" content="5;url=http://fontsgeek.com/fonts/frutiger-bold-italic?ref=readme"> + <title>Frutiger Bold ItalicFontsgeek</title> + <style> +/* ------------------------------------- + GLOBAL + ------------------------------------- */ + * { + margin:0; + padding:0; + font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; + font-size: 100%; + line-height: 1.6; + } + + img { + max-width: 100%; + } + + body { + -webkit-font-smoothing:antialiased; + -webkit-text-size-adjust:none; + width: 100%!important; + height: 100%; + background:#DDD; + } + + + /* ------------------------------------- + ELEMENTS + ------------------------------------- */ + a { + color: #348eda; + } + + .btn-primary, .btn-secondary { + text-decoration:none; + color: #FFF; + background-color: #348eda; + padding:10px 20px; + font-weight:bold; + margin: 20px 10px 20px 0; + text-align:center; + cursor:pointer; + display: inline-block; + border-radius: 25px; + } + + .btn-secondary{ + background: #aaa; + } + + .last { + margin-bottom: 0; + } + + .first{ + margin-top: 0; + } + + + /* ------------------------------------- + BODY + ------------------------------------- */ + table.body-wrap { + width: 100%; + padding: 20px; + } + + table.body-wrap .container{ + border: 1px solid #f0f0f0; + } + + + /* ------------------------------------- + FOOTER + ------------------------------------- */ + table.footer-wrap { + width: 100%; + clear:both!important; + } + + .footer-wrap .container p { + font-size:12px; + color:#666; + + } + + table.footer-wrap a{ + color: #999; + } + + + /* ------------------------------------- + TYPOGRAPHY + ------------------------------------- */ + h1,h2,h3{ + font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; line-height: 1.1; margin-bottom:15px; color:#000; + margin: 40px 0 10px; + line-height: 1.2; + font-weight:200; + } + + h1 { + font-size: 36px; + } + h2 { + font-size: 28px; + } + h3 { + font-size: 22px; + } + + p, ul { + margin-bottom: 10px; + font-weight: normal; + font-size:14px; + } + + ul li { + margin-left:5px; + list-style-position: inside; + } + + /* --------------------------------------------------- + RESPONSIVENESS + Nuke it from orbit. It's the only way to be sure. + ------------------------------------------------------ */ + + /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */ + .container { + display:block!important; + max-width:600px!important; + margin:0 auto!important; /* makes it centered */ + clear:both!important; + } + + /* This should also be a block element, so that it will fill 100% of the .container */ + .content { + padding:20px; + max-width:600px; + margin:0 auto; + display:block; + } + + /* Let's make sure tables in the content area are 100% wide */ + .content table { + width: 100%; + } + + </style> + </head> + + <body bgcolor="#f6f6f6"> + + <!-- body --> + <table class="body-wrap"> + <tr> + <td></td> + <td class="container" bgcolor="#FFFFFF"> + + <!-- content --> + <div class="content"> + <table> + <tr> + <td> + <h1>Frutiger Bold Italic</h1> + <p>This font was downloaded from <a href="http://fontsgeek.com?ref=readme">fontsgeek.com</a> . You can visit <a href="http://fontsgeek.com?ref=readme">fontsgeek.com</a> for thousands of free fonts.</p> + <p><a href="http://fontsgeek.com/fonts/frutiger-bold-italic?ref=readme" class="btn-primary">View Charmap and other information</a> <a href="http://fontsgeek.com?ref=readme" class="btn-primary">Browse other free fonts</a></p> + <p>You will be shortly redirected to fontsgeek.</p> + </td> + </tr> + </table> + </div> + <!-- /content --> + + </td> + <td></td> + </tr> + </table> + <!-- /body --> + + </body> +</html> diff --git a/css/formulario.css b/css/formulario.css new file mode 100644 index 0000000..0b1dadb --- /dev/null +++ b/css/formulario.css @@ -0,0 +1,129 @@ +body { + background-color: #12122B; + color: #E3E3E3; + font-family: Arial, sans-serif; + padding: 20px; +} + +/* Navbar */ +.navbar-brand { + font-size: 1.8rem; + font-weight: bold; + color: #E3E3E3; + text-decoration: none; +} + +.nav-links { + display: flex; + gap: 25px; +} + +.nav-links a { + color: #E3E3E3; + text-decoration: none; + font-size: 1.1rem; + font-weight: 500; + transition: color 0.3s; + padding-bottom: 5px; +} + +.nav-links a:hover { + color: #AAAA91; + border-bottom: 3px solid #AAAA91; +} + +/* Contenedor */ +.container { + max-width: 600px; + margin: 50px auto; + padding: 30px; + background-color: #1E1E30; + border-radius: 12px; + box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.3); +} + +/* Formularios */ +.mb-3 { + margin-bottom: 15px; +} + +label { + font-weight: bold; + color: #C4C4C4; +} + +input { + width: 100%; + padding: 10px; + background-color: #282828; + color: #E3E3E3; + border: 1px solid #444; + border-radius: 6px; + margin-top: 5px; +} + +input:focus { + background-color: #333; + border-color: #AAAA91; + outline: none; +} + +/* Botones */ +.btn { + width: 100%; + padding: 12px; + font-size: 16px; + font-weight: bold; + border-radius: 8px; + transition: background 0.3s; + margin-top: 10px; + cursor: pointer; +} + +.next-btn { + background: #5CB85C; + color: #12122B; +} + +.next-btn:hover { + background: #4CAF50; +} + +.prev-btn { + background: #444; + color: white; +} + +.prev-btn:hover { + background: #666; +} + +.submit-btn { + background: #5CB85C; + color: white; +} + +.submit-btn:hover { + background: #4CAF50; +} +.btn next-btn { + background: #5CB85C; +} +/* Zonas */ +fieldset { + border: 2px solid #444; + padding: 20px; + border-radius: 8px; + margin-bottom: 20px; +} + +legend { + font-weight: bold; + color: #E3E3E3; + font-size: 18px; +} + +/* Ocultar pasos */ +.d-none { + display: none; +} diff --git a/css/login.css b/css/login.css new file mode 100644 index 0000000..d871327 --- /dev/null +++ b/css/login.css @@ -0,0 +1,89 @@ +body { + background-color: #12122B; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + font-family: Arial, sans-serif; +} + +.container { + text-align: center; +} + +@font-face { + font-family: 'Frutiger'; + src: url('fonts/Frutiger Bold Italic/Frutiger Bold Italic.ttf') format('truetype'); + font-weight: bold; + font-style: italic; +} + +h1 { + font-family: 'Frutiger', sans-serif; + color: #ffffff; + font-size: 100px; + margin-bottom: 20px; +} + +h1 { + font-family: 'Frutiger', sans-serif; + color: #ffffff; + font-size: 24px; + margin-bottom: 20px; +} + +h2 { + color: #ffffff; + font-size: 24px; + margin-bottom: 20px; +} + + +.card { + background-color: #1E1E30; + padding: 30px; + border-radius: 10px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + width: 300px; +} + +.datos { + display: flex; + flex-direction: column; + text-align: left; + margin-bottom: 15px; +} + +.datos label { + font-size: 14px; + color: #ffffff; + margin-bottom: 5px; +} + +.datos input { + padding: 10px; + border: 1px solid #495057; + border-radius: 5px; + background-color: #343a40; + color: white; +} + +.boton { + text-align: center; + margin-top: 15px; +} + +#btnIniciarSesion { + padding: 10px; + border-radius: 5px; + background-color: #5CB85C; + border: none; + color: white; + font-size: 16px; + cursor: pointer; + width: 100%; +} + +#btnIniciarSesion:hover { + background-color: #4CAF50; +} diff --git a/css/modalActualizar.css b/css/modalActualizar.css new file mode 100644 index 0000000..0d06d5b --- /dev/null +++ b/css/modalActualizar.css @@ -0,0 +1,40 @@ +.modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.182); + display: flex; + justify-content: center; + align-items: center; +} + +.modal-contenido { + background: #343d46; + width: 200px; + height: 100px; + padding: 20px; + border-radius: 10px; + text-align: center; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +modal-contenido p{ + color: black; +} + +.btn-modal { + padding: 10px 20px; + background: #1c1b2b; + color: white; + border: none; + cursor: pointer; + border-radius: 5px; +} + +.btn-modal:hover { + background: #0056b3; +} \ No newline at end of file diff --git a/css/modalEliminar.css b/css/modalEliminar.css new file mode 100644 index 0000000..7afa376 --- /dev/null +++ b/css/modalEliminar.css @@ -0,0 +1,69 @@ +.modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + justify-content: center; + align-items: center; +} + +.modal-contenido { + background: #25253e; + width: 300px; + padding: 20px; + border-radius: 10px; + text-align: center; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.modal-contenido p { + color: #ffffff; + font-size: 16px; + margin-bottom: 10px; +} + + +.btn-modal { + margin-top: 10px; + padding: 10px 20px; + background: #1c1b2b; + color: white; + border: none; + cursor: pointer; + border-radius: 5px; + font-size: 14px; + transition: background 0.3s ease-in-out; +} + +.btn-modal:hover { + background: #0056b3; +} + +.modal-confirmacion { + background: #2b2b3a; + width: 320px; + padding: 25px; + border-radius: 12px; + text-align: center; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3); +} + +#btnCancelarEliminar { + background: #5a6268; +} + +#btnCancelarEliminar:hover { + background: #444b50; +} + +#btnConfirmarEliminar{ + background-color: #f5524c; +} + +#btnConfirmarEliminar:hover{ + background: #c9302c; +} \ No newline at end of file diff --git a/css/ventanaBoletos.css b/css/ventanaBoletos.css new file mode 100644 index 0000000..578d8bd --- /dev/null +++ b/css/ventanaBoletos.css @@ -0,0 +1,133 @@ +/* Contenedor principal */ +.main-container { + display: flex; + height: 80vh; + gap: 20px; + padding: 20px; +} + +/* Lado izquierdo */ +.cardIzq { + flex: 2; + display: flex; + flex-direction: column; + align-items: center; +} + +.cardIzq img { + width: 100%; + max-width: 600px; + height: 400px; + margin-bottom: 20px; +} + +.cardIzq h2 { + font-size: 44px; + margin: 20px; +} + +.zones-container { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + margin-top: 15px; + width: 100%; +} + +/* Lado derecho (tarjetas en columna) */ +.cardDer { + flex: 1; + display: flex; + flex-direction: column; /* Se colocan en columna */ + align-items: center; + justify-content: center; + gap: 20px; /* Espacio entre las tarjetas */ +} + +.card { + background: #1E1E30; + border-radius: 12px; + padding: 20px; + width: 100%; + max-width: 300px; + box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.3); + color: #ffffff; +} + +/* Botón centrado en la parte inferior */ +.button-container { + position: relative; + width: 100%; + text-align: center; + margin-top: 20px; +} + +#comprarBoletos { + padding: 10px 20px; + border-radius: 8px; + font-size: 16px; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; + border: none; + background-color: #5e17eb; + color: white; + position: absolute; + left: 50%; + transform: translateX(-50%); +} + +#comprarBoletos:hover { + background-color: #4c11c9; +} +/* Contenedor de asientos dentro de la tarjeta */ +.asientos-container { + display: grid; + grid-template-columns: repeat(5, 1fr); /* 5 columnas por fila */ + gap: 10px; + justify-content: center; + width: 100%; + padding: 10px; +} + +/* Estilos para cada asiento */ +.asiento { + display: flex; + align-items: center; + justify-content: center; + width: 50px; + height: 50px; + border-radius: 8px; + cursor: pointer; + font-weight: bold; + transition: all 0.2s ease; + user-select: none; + border: 1px solid #ccc; +} + +/* Iconos dentro de los asientos */ +.asiento i { + font-size: 24px; /* Ajusta el tamaño del icono */ +} + +/* Asientos disponibles */ +.asiento.disponible { + background-color: #28a745; + color: white; +} + +/* Asientos ocupados */ +.asiento.ocupado { + background-color: #dc3545; + color: white; + cursor: not-allowed; +} + +/* Asientos seleccionados */ +.asiento.seleccionado { + background-color: #007bff; + color: white; + transform: scale(1.1); + box-shadow: 0 0 5px rgba(255, 255, 255, 0.5); +} + diff --git a/css/ventanaPrincipal.css b/css/ventanaPrincipal.css new file mode 100644 index 0000000..e89cb25 --- /dev/null +++ b/css/ventanaPrincipal.css @@ -0,0 +1,64 @@ +body { + background-color: #aab2b2; +} + +.form-control { + border-radius: 5px; +} + +.btn-outline-light { + border-color: 5CB85C; +} + +.btn-outline-light:hover { + background-color: 4CAF50; + color: black; +} + +.card { + border-radius: 10px; +} + +h2 { + color: #343a40; + text-align: center; +} + +.btn-primary { + background-color: #5CB85C; + border-color: #5CB85C; +} + +.btn-primary:hover { + background-color: #4CAF50; +} + +.btn-danger { + background-color: #dc3545; + border-color: #a71d2a; +} + +.btn-danger:hover { + background-color: #a71d2a; +} +.contenedor-conciertos { + display: flex; + flex-direction: column; + gap: 20px; +} + +.tarjeta-concierto { + background-color: #343a40; + padding: 20px; + border-radius: 10px; + box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.1); + width: 90%; + max-width: 600px; + margin: auto; +} +.tarjeta-concierto p, h2, h3, li { + color:#ffff +} +#tituloListaConciertos{ + color:#343a40 +} diff --git a/editarConciertos.html b/editarConciertos.html new file mode 100644 index 0000000..eaf3589 --- /dev/null +++ b/editarConciertos.html @@ -0,0 +1,128 @@ +<!DOCTYPE html> +<html lang="es"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Editar Concierto</title> + <link rel="stylesheet" href="css/conciertos.css"> + <link rel="stylesheet" href="css/formulario.css"> + <link rel="stylesheet" href="css/modalActualizar.css"> +</head> +<body> + + <!-- Navbar --> + <nav> + <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> + <a href="#">Reporte Ventas</a> + </div> + </nav> + + <!-- Contenedor --> + <div class="container"> + <h2 class="text-center">Editar Concierto</h2> + <form id="formulario"> + + <!-- Paso 1: Datos generales --> + <div id="paso1"> + <div class="mb-3"> + <label for="nombre_concierto">Nombre del Concierto:</label> + <input type="text" id="nombre_concierto" required> + </div> + <div class="mb-3"> + <label for="artista">Artista:</label> + <input type="text" id="artista" required> + </div> + <div class="mb-3"> + <label for="fecha">Fecha del Concierto:</label> + <input type="date" id="fecha" required> + </div> + <button type="button" class="btn next-btn" onclick="siguientePaso(2)">Siguiente</button> + </div> + + <!-- Paso 2: Dirección --> + <div id="paso2" class="d-none"> + <div class="mb-3"> + <label for="calle">Calle:</label> + <input type="text" id="calle" required> + </div> + <div class="mb-3"> + <label for="colonia">Colonia:</label> + <input type="text" id="colonia" required> + </div> + <div class="mb-3"> + <label for="numero_direccion">Número exterior:</label> + <input type="text" id="numero_direccion" required> + </div> + <div class="mb-3"> + <label for="codigo_postal">Código Postal:</label> + <input type="text" id="codigo_postal" required> + </div> + <div class="mb-3"> + <label for="estado">Estado:</label> + <input type="text" id="estado" required> + </div> + <div class="mb-3"> + <label for="capacidad_total">Capacidad Total:</label> + <input type="number" id="capacidad_total" required> + </div> + <button type="button" class="btn prev-btn" onclick="siguientePaso(1)">Anterior</button> + <button type="button" class="btn next-btn" onclick="siguientePaso(3)">Siguiente</button> + </div> + + <!-- Paso 3: Zonas --> + <div id="paso3" class="d-none"> + <fieldset> + <legend>Zonas</legend> + + <div class="mb-3"> + <label>Zona General - Capacidad:</label> + <input type="number" id="capacidad_general" required oninput="actualizarCapacidad()"> + <label>Precio:</label> + <input type="number" step="0.01" id="precio_general" required> + </div> + + <div class="mb-3"> + <label>Zona Plata - Capacidad:</label> + <input type="number" id="capacidad_plata" required oninput="actualizarCapacidad()"> + <label>Precio:</label> + <input type="number" step="0.01" id="precio_plata" required> + </div> + + <div class="mb-3"> + <label>Zona Oro - Capacidad:</label> + <input type="number" id="capacidad_oro" required oninput="actualizarCapacidad()"> + <label>Precio:</label> + <input type="number" step="0.01" id="precio_oro" required> + </div> + + <div class="mb-3"> + <label>Zona VIP - Capacidad:</label> + <input type="number" id="capacidad_vip" required oninput="actualizarCapacidad()"> + <label>Precio:</label> + <input type="number" step="0.01" id="precio_vip" required> + </div> + </fieldset> + + <!-- Agregar capacidad disponible --> + <p>Capacidad disponible: <span id="capacidad_disponible">0</span></p> + + <button type="button" class="btn prev-btn" onclick="siguientePaso(2)">Anterior</button> + <button type="submit" class="btn submit-btn">Actualizar Concierto</button> + </div> + </form> + <div id="mensaje" class="mt-3 text-center"></div> + </div> + + <div id="modalMensaje" class="modal"> + <div class="modal-contenido"> + <p id="modalTexto"></p> + <button id="cerrarModal" class="btn-modal">Cerrar</button> + </div> + </div> + + <script src="js/editarConcierto.js"></script> +</body> +</html> \ No newline at end of file diff --git a/img/concierto_1.jpg b/img/concierto_1.jpg new file mode 100644 index 0000000..0fde3c9 Binary files /dev/null and b/img/concierto_1.jpg differ diff --git a/img/concierto_5.jpg b/img/concierto_5.jpg new file mode 100644 index 0000000..712263c Binary files /dev/null and b/img/concierto_5.jpg differ diff --git a/img/mapa.png b/img/mapa.png new file mode 100644 index 0000000..69753b3 Binary files /dev/null and b/img/mapa.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..ccefe86 --- /dev/null +++ b/index.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html lang="es"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Login</title> + <link rel="stylesheet" href="css/login.css"> +</head> +<body> + <div class="container"> + <h1>TicketFei</h1> + <h2>Inicio de sesión</h2> + <div class="card"> + <form id="formularioLogin"> + <div class="datos"> + <label for="user">Usuario</label> + <input type="text" id="user" name="user" placeholder="Ingresa tu usuario" required> + </div> + <div class="datos"> + <label for="pw">Contraseña</label> + <input type="password" id="pw" name="pw" placeholder="Ingresa tu contraseña" required> + </div> + <div class="boton"> + <input type="submit" id="btnIniciarSesion" value="Ingresar"> + </div> + </form> + <div id="mensaje"></div> + </div> + </div> + <script src="js/login.js"></script> +</body> +</html> diff --git a/js/conciertos.js b/js/conciertos.js new file mode 100644 index 0000000..0494e1f --- /dev/null +++ b/js/conciertos.js @@ -0,0 +1,188 @@ +document.addEventListener("DOMContentLoaded", () => { + cargarConciertos(); +}); + +const listaConciertos = document.getElementById("listaConciertos"); +const buscadorBoton = document.getElementById("buscadorBoton"); +const buscadorInput = document.getElementById("buscadorColaborador"); +const modal = document.getElementById("modalMensaje"); +const modalTexto = document.getElementById("modalTexto"); +const cerrarModal = document.getElementById("cerrarModal"); +const modalConfirmacion = document.getElementById("modalConfirmacion"); +const modalConfirmarTexto = document.getElementById("modalConfirmarTexto"); +const btnConfirmarEliminar = document.getElementById("btnConfirmarEliminar"); +const btnCancelarEliminar = document.getElementById("btnCancelarEliminar"); +let conciertoIdEliminar = null; // Para almacenar el ID del concierto a eliminar + +// Ocultar modales al inicio +modal.style.display = "none"; +modalConfirmacion.style.display = "none"; + +// Función para mostrar modal de mensaje (confirmación de eliminación exitosa) +function mostrarModal(mensaje) { + modalTexto.textContent = mensaje; + modal.style.display = "flex"; + setTimeout(() => { + modal.style.display = "none"; + }, 2000); // Se cierra automáticamente en 2 segundos +} + +// Función para mostrar el modal de confirmación antes de eliminar +function mostrarModalConfirmacion(id) { + conciertoIdEliminar = id; + modalConfirmarTexto.textContent = "¿Estás seguro de que deseas eliminar este concierto?"; + modalConfirmacion.style.display = "flex"; +} + +// Evento para cerrar el modal de confirmación sin eliminar +btnCancelarEliminar.addEventListener("click", () => { + modalConfirmacion.style.display = "none"; +}); + +// Evento para confirmar eliminación +btnConfirmarEliminar.addEventListener("click", async () => { + if (conciertoIdEliminar) { + await eliminarConcierto(conciertoIdEliminar); + } + modalConfirmacion.style.display = "none"; // Cierra el modal después de eliminar +}); + +// Evento para cerrar el modal de mensaje manualmente +cerrarModal.addEventListener("click", () => { + modal.style.display = "none"; +}); + +window.addEventListener("click", (event) => { + if (event.target === modal) { + modal.style.display = "none"; + } + if (event.target === modalConfirmacion) { + modalConfirmacion.style.display = "none"; + } +}); + +async function cargarConciertos(filtro = "") { + try { + const respuesta = await fetch('controladores/conciertos.php'); + if (!respuesta.ok) throw new Error("Error al cargar conciertos"); + + const conciertos = await respuesta.json(); + listaConciertos.innerHTML = ""; + + const conciertosFiltrados = filtro + ? conciertos.filter(c => c.nombre_concierto.toLowerCase().includes(filtro.toLowerCase())) + : conciertos; + + if (conciertosFiltrados.length === 0) { + listaConciertos.innerHTML = `<p class="text-red-500 text-center">No se encontraron conciertos.</p>`; + return; + } + + let tarjetas = []; + + conciertosFiltrados.forEach((concierto) => { + const tarjeta = document.createElement("div"); + tarjeta.classList.add("concierto-card"); + + let zonasHTML = ""; + if (concierto.zonas.length > 0) { + zonasHTML = `<ul class="text-sm text-gray-300 mt-2">`; + concierto.zonas.forEach(zona => { + zonasHTML += `<li>🔹 ${zona.nombre_zona}: <b>${zona.capacidad} asientos</b> - $${zona.precio}</li>`; + }); + zonasHTML += `</ul>`; + } + + tarjeta.innerHTML = ` + <div class="menu-container"> + <button class="menu-btn">⋮</button> + <div class="menu"> + <button class="edit" data-id="${concierto.id}">Editar</button> + <button class="delete" data-id="${concierto.id}">Eliminar</button> + </div> + </div> + <img src="img/concierto_${concierto.id}.jpg" alt="Concierto" class="card-img"> + <div class="card-body"> + <h3 class="card-title">${concierto.nombre_concierto}</h3> + <p class="card-text">🎤 ${concierto.artista}</p> + <p class="card-text">📅 ${concierto.fecha}</p> + <p class="card-text">📍 ${concierto.direccion || 'No definida'}</p> + <p class="card-text">🎟 ${concierto.capacidad_total} Asistentes</p> + ${zonasHTML} + </div> + <button class="btn-comprar" data-id="${concierto.id}">Comprar Boletos</button> + `; + + // Evento para editar + tarjeta.querySelector(".edit").addEventListener("click", () => { + window.location.href = `editarConciertos.html?id=${concierto.id}`; + }); + + // Evento para eliminar usando el modal de confirmación + tarjeta.querySelector(".delete").addEventListener("click", (e) => { + const id = e.target.dataset.id; + mostrarModalConfirmacion(id); + }); + + tarjeta.querySelector(".btn-comprar").addEventListener("click", (e) => { + const id = e.target.dataset.id; + window.location.href = `ventaBoletos.html?id=${id}`; + }); + + listaConciertos.appendChild(tarjeta); + tarjetas.push(tarjeta); + }); + + gsap.fromTo( + tarjetas, + { opacity: 0, scale: 0.9 }, + { opacity: 1, scale: 1, duration: 0.6, stagger: 0.2, ease: "power3.out" } + ); + + } catch (error) { + console.error(error); + listaConciertos.innerHTML = `<p class="text-red-500">No se pudieron cargar los conciertos.</p>`; + } +} + +buscadorBoton.addEventListener('click', (event) => { + event.preventDefault(); + const termino = buscadorInput.value.trim(); + cargarConciertos(termino); +}); + +document.addEventListener("click", (e) => { + if (e.target.classList.contains("menu-btn")) { + e.stopPropagation(); + let menu = e.target.nextElementSibling; + menu.style.display = menu.style.display === "block" ? "none" : "block"; + } else { + document.querySelectorAll(".menu").forEach(menu => { + menu.style.display = "none"; + }); + } +}); + +/** 🔹 Función para eliminar el concierto usando modal */ +async function eliminarConcierto(id) { + try { + const respuesta = await fetch("controladores/eliminar_concierto.php", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ id }) + }); + + const resultado = await respuesta.json(); + if (resultado.eliminacionCorrecta) { + mostrarModal("Concierto eliminado correctamente"); + cargarConciertos(); // Recargar la lista + } else { + mostrarModal("Error al eliminar el concierto"); + } + } catch (error) { + console.error(error); + mostrarModal("Error de conexión con el servidor"); + } +} diff --git a/js/crearConciertos.js b/js/crearConciertos.js new file mode 100644 index 0000000..82abb6a --- /dev/null +++ b/js/crearConciertos.js @@ -0,0 +1,111 @@ +document.addEventListener("DOMContentLoaded", () => { + const formulario = document.getElementById("formulario"); + const mensajeDiv = document.getElementById("mensaje"); + let capacidadTotal = 0; + + function siguientePaso(paso) { + document.querySelectorAll("[id^='paso']").forEach(p => p.classList.add("d-none")); + document.getElementById(`paso${paso}`).classList.remove("d-none"); + + if (paso === 3) { + capacidadTotal = parseInt(document.getElementById("capacidad_total").value) || 0; + const capacidadDisponible = document.getElementById("capacidad_disponible"); + if (capacidadDisponible) { + capacidadDisponible.textContent = capacidadTotal; + } + } + } + + function actualizarCapacidad() { + let asignado = 0; + ["capacidad_general", "capacidad_plata", "capacidad_oro", "capacidad_vip"].forEach(id => { + const input = document.getElementById(id); + if (input) { + asignado += parseInt(input.value) || 0; + } + }); + + const restante = capacidadTotal - asignado; + const capacidadDisponible = document.getElementById("capacidad_disponible"); + if (capacidadDisponible) { + capacidadDisponible.textContent = restante < 0 ? "Excede la capacidad total" : restante; + } + } + + formulario.addEventListener("submit", async (event) => { + event.preventDefault(); + + const capacidadDisponible = document.getElementById("capacidad_disponible"); + if (capacidadDisponible && parseInt(capacidadDisponible.textContent) < 0) { + mensajeDiv.innerHTML = '<div class="alert alert-danger">Error: La suma de capacidades de zonas no puede exceder la capacidad total.</div>'; + return; + } + + // 🔹 Construir objeto JSON con todas las zonas + const datosConcierto = { + nombre_concierto: document.getElementById("nombre_concierto").value.trim(), + artista: document.getElementById("artista").value.trim(), + fecha: document.getElementById("fecha").value, + calle: document.getElementById("calle").value.trim(), + colonia: document.getElementById("colonia").value.trim(), + numero_direccion: document.getElementById("numero_direccion").value.trim(), + codigo_postal: document.getElementById("codigo_postal").value.trim(), + estado: document.getElementById("estado").value.trim(), + capacidad_total: capacidadTotal, + zonas: [ + { + nombre_zona: "General", + capacidad: parseInt(document.getElementById("capacidad_general").value) || 0, + precio: parseFloat(document.getElementById("precio_general").value) || 0 + }, + { + nombre_zona: "Plata", + capacidad: parseInt(document.getElementById("capacidad_plata").value) || 0, + precio: parseFloat(document.getElementById("precio_plata").value) || 0 + }, + { + nombre_zona: "Oro", + capacidad: parseInt(document.getElementById("capacidad_oro").value) || 0, + precio: parseFloat(document.getElementById("precio_oro").value) || 0 + }, + { + nombre_zona: "VIP", + capacidad: parseInt(document.getElementById("capacidad_vip").value) || 0, + precio: parseFloat(document.getElementById("precio_vip").value) || 0 + } + ] + }; + + console.log("Enviando datos:", JSON.stringify(datosConcierto)); // Depuración + + try { + const respuesta = await fetch("controladores/insertar_concierto.php", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(datosConcierto) + }); + + const resultado = await respuesta.json(); + console.log("Respuesta del servidor:", resultado); // Depuración + + if (!resultado.insercionCorrecta) { + throw new Error(resultado.error || "Error al guardar el concierto"); + } + + mensajeDiv.innerHTML = '<div class="alert alert-success">Concierto registrado correctamente.</div>'; + + setTimeout(() => { + window.location.href = "ventanaConciertos.html"; + }, 2000); + + } catch (error) { + console.error("Error:", error); + mensajeDiv.innerHTML = `<div class="alert alert-danger">Error: ${error.message}</div>`; + } + }); + + window.siguientePaso = siguientePaso; + window.actualizarCapacidad = actualizarCapacidad; +}); diff --git a/js/editarConcierto.js b/js/editarConcierto.js new file mode 100644 index 0000000..7c0b7b0 --- /dev/null +++ b/js/editarConcierto.js @@ -0,0 +1,194 @@ +const params = new URLSearchParams(window.location.search); +const conciertoId = params.get("id"); +const formulario = document.getElementById("formulario"); +const modal = document.getElementById("modalMensaje"); +const modalTexto = document.getElementById("modalTexto"); +const cerrarModal = document.getElementById("cerrarModal"); +let capacidadTotal = 0; + +modal.style.display = "none"; + +function mostrarModal(mensaje) { + modalTexto.textContent = mensaje; + modal.style.display = "flex"; +} + +cerrarModal.addEventListener("click", () => { + modal.style.display = "none"; +}); + +window.addEventListener("click", (event) => { + if (event.target === modal) { + modal.style.display = "none"; + } +}); + +function siguientePaso(paso) { + document.querySelectorAll("[id^='paso']").forEach(p => p.classList.add("d-none")); + document.getElementById(`paso${paso}`).classList.remove("d-none"); + + if (paso === 3) { + capacidadTotal = parseInt(document.getElementById("capacidad_total").value) || 0; + document.getElementById("capacidad_disponible").textContent = capacidadTotal; + // ✅ Llamar a actualizar capacidad después de cargar los datos + actualizarCapacidad(); + } +} + +// ✅ Función para actualizar la capacidad disponible +function actualizarCapacidad() { + let sumaZonas = 0; + ["capacidad_general", "capacidad_plata", "capacidad_oro", "capacidad_vip"].forEach(id => { + sumaZonas += parseInt(document.getElementById(id).value) || 0; + }); + + let capacidadDisponibleElement = document.getElementById("capacidad_disponible"); + let restante = capacidadTotal - sumaZonas; + + if (restante < 0) { + capacidadDisponibleElement.textContent = "⚠️ Excede la capacidad total"; + capacidadDisponibleElement.style.color = "red"; + } else if (restante > 0) { + capacidadDisponibleElement.textContent = `⚠️ Faltan ${restante} lugares`; + capacidadDisponibleElement.style.color = "orange"; + } else { + capacidadDisponibleElement.textContent = `✅ Todo correcto`; + capacidadDisponibleElement.style.color = "green"; + } +} + +// ✅ Función para validar la capacidad antes de actualizar +function validarCapacidadZonas() { + let sumaZonas = 0; + ["capacidad_general", "capacidad_plata", "capacidad_oro", "capacidad_vip"].forEach(id => { + sumaZonas += parseInt(document.getElementById(id).value) || 0; + }); + + let capacidadTotalInput = parseInt(document.getElementById("capacidad_total").value); + + if (sumaZonas < capacidadTotalInput) { + mostrarModal(`⚠️ Error: La suma de las capacidades es menor`); + return false; + } + + if (sumaZonas > capacidadTotalInput) { + mostrarModal(`⚠️ Error: La suma de las capacidades es mayor`); + return false; + } + + return true; +} + +// ✅ Bloquear la actualización si la validación no pasa +formulario.addEventListener("submit", async (event) => { + event.preventDefault(); + + if (!validarCapacidadZonas()) { + return; // 🚫 Bloquear la actualización si la validación falla + } + + await actualizarConcierto(conciertoId); +}); + +// ✅ Función para cargar los datos del concierto +async function cargarDatosConcierto(id) { + try { + const respuesta = await fetch(`/ProyectoTicketFei/controladores/obtener_concierto.php?id=${id}`); + if (!respuesta.ok) throw new Error("Error al cargar los datos del concierto"); + + const concierto = await respuesta.json(); + + document.getElementById("nombre_concierto").value = concierto.nombre_concierto; + document.getElementById("artista").value = concierto.artista; + document.getElementById("fecha").value = concierto.fecha; + document.getElementById("calle").value = concierto.calle; + document.getElementById("colonia").value = concierto.colonia; + document.getElementById("numero_direccion").value = concierto.numero_direccion; + document.getElementById("codigo_postal").value = concierto.codigo_postal; + document.getElementById("estado").value = concierto.estado; + document.getElementById("capacidad_total").value = concierto.capacidad_total; + capacidadTotal = parseInt(concierto.capacidad_total); // Guardar la capacidad total + + if (concierto.zonas && concierto.zonas.length > 0) { + concierto.zonas.forEach(zona => { + switch (zona.nombre_zona) { + case "General": + document.getElementById("capacidad_general").value = zona.capacidad; + document.getElementById("precio_general").value = zona.precio; + break; + case "Plata": + document.getElementById("capacidad_plata").value = zona.capacidad; + document.getElementById("precio_plata").value = zona.precio; + break; + case "Oro": + document.getElementById("capacidad_oro").value = zona.capacidad; + document.getElementById("precio_oro").value = zona.precio; + break; + case "VIP": + document.getElementById("capacidad_vip").value = zona.capacidad; + document.getElementById("precio_vip").value = zona.precio; + break; + } + }); + } else { + console.warn("⚠️ No se encontraron zonas para este concierto."); + } + + } catch (error) { + console.error("❌ Error en cargarDatosConcierto:", error); + } +} + +// ✅ Función para actualizar el concierto +async function actualizarConcierto(id) { + if (!validarCapacidadZonas()) return; // 🚫 Bloquear la actualización si la validación falla + + const datosConcierto = { + id: id, + nombre_concierto: document.getElementById("nombre_concierto").value.trim(), + artista: document.getElementById("artista").value.trim(), + fecha: document.getElementById("fecha").value, + calle: document.getElementById("calle").value.trim(), + colonia: document.getElementById("colonia").value.trim(), + numero_direccion: document.getElementById("numero_direccion").value.trim(), + codigo_postal: document.getElementById("codigo_postal").value.trim(), + estado: document.getElementById("estado").value.trim(), + capacidad_total: document.getElementById("capacidad_total").value, + zonas: [ + { nombre_zona: "General", capacidad: document.getElementById("capacidad_general").value, precio: document.getElementById("precio_general").value }, + { nombre_zona: "Plata", capacidad: document.getElementById("capacidad_plata").value, precio: document.getElementById("precio_plata").value }, + { nombre_zona: "Oro", capacidad: document.getElementById("capacidad_oro").value, precio: document.getElementById("precio_oro").value }, + { nombre_zona: "VIP", capacidad: document.getElementById("capacidad_vip").value, precio: document.getElementById("precio_vip").value } + ] + }; + + try { + const respuesta = await fetch("/ProyectoTicketFei/controladores/actualizar_concierto.php", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(datosConcierto) + }); + + const resultado = await respuesta.json(); + if (resultado.actualizacionCorrecta) { + mostrarModal("✅ Concierto actualizado correctamente"); + setTimeout(() => { + window.location.href = "ventanaConciertos.html"; + }, 2000); + } else { + mostrarModal("⚠️ Error al actualizar el concierto"); + } + } catch (error) { + console.error(error); + mostrarModal("⚠️ Error de conexión con el servidor"); + } +} + +// ✅ Llamar a cargar los datos cuando la página cargue +(async function () { + if (conciertoId) { + await cargarDatosConcierto(conciertoId); + } +})(); diff --git a/js/login.js b/js/login.js new file mode 100644 index 0000000..fe93fe9 --- /dev/null +++ b/js/login.js @@ -0,0 +1,32 @@ +const formulario = document.getElementById('formularioLogin'); +const notificacion = document.getElementById('mensaje'); + +formulario.addEventListener('submit', async (event) => { + + event.preventDefault(); + const usuario = document.getElementById("user").value; + const password = document.getElementById("pw").value; + + const data = new FormData(); + data.append('usuario', usuario); + data.append('password', password); + + try { + + const respuestaPeticion = await fetch('controladores/login.php', { + method: 'POST', + body: data + }); + const verificarCredenciales = await respuestaPeticion.json(); + if (verificarCredenciales.loginExitoso) { + window.location.href = 'ventanaConciertos.html'; + } else { + notificacion.textContent ="Usuario o contraseña incorrecta"; + notificacion.style.color='#ffffff'; + } + } catch (error) { + console.error(error); + notificacion.textContent = 'Lo sentimos, el servicio no está disponible por el momento.'; + notificacion.style.color = '#ff0000'; + } +}); diff --git a/js/ventaBoletos.js b/js/ventaBoletos.js new file mode 100644 index 0000000..957d44d --- /dev/null +++ b/js/ventaBoletos.js @@ -0,0 +1,137 @@ +document.addEventListener("DOMContentLoaded", async () => { + const params = new URLSearchParams(window.location.search); + const conciertoId = params.get("id"); + + if (!conciertoId) { + console.error("Error: No se proporcionó un ID de concierto en la URL."); + return; + } + + try { + const respuesta = await fetch(`controladores/concierto_zonas.php?id_concierto=${conciertoId}`); + if (!respuesta.ok) throw new Error("Error al cargar los datos del concierto"); + + const concierto = await respuesta.json(); + + if (concierto.error) { + console.error(`Error: ${concierto.error}`); + return; + } + + // Mostrar información del concierto + document.getElementById("nombre_concierto").textContent = concierto.nombre_concierto; + document.getElementById("artista").textContent = `Artista: ${concierto.artista}`; + document.getElementById("fecha").textContent = `Fecha: ${concierto.fecha}`; + + // Mostrar zonas del concierto + const zonasContainer = document.getElementById("zonas-container"); + zonasContainer.innerHTML = ""; + + if (!concierto.zonas || concierto.zonas.length === 0) { + console.error("No se encontraron zonas para este concierto."); + return; + } + + concierto.zonas.forEach(zona => { + const zonaBtn = document.createElement("button"); + zonaBtn.classList.add("boton-zona"); + zonaBtn.textContent = `Ver Zona ${zona.nombre_zona}`; + zonaBtn.dataset.idZona = zona.id_zona; + + zonaBtn.addEventListener("click", () => cargarDatosZona(zona)); + + zonasContainer.appendChild(zonaBtn); + }); + + } catch (error) { + console.error("Error al obtener los datos del concierto:", error); + } +}); + +async function cargarDatosZona(zona) { + if (!zona) { + console.error("Error: Datos de zona no disponibles"); + return; + } + + // Mostrar información de la zona + document.getElementById("zonaNombre").textContent = `Zona: ${zona.nombre_zona || 'N/A'}`; + document.getElementById("zonaCapacidad").textContent = `Capacidad: ${zona.capacidad || 'N/A'}`; + document.getElementById("zonaPrecio").textContent = `Precio: $${zona.precio || 'N/A'}`; + document.getElementById("asientosDisponibles").textContent = `Disponibles: ${zona.asientos_disponibles ?? 0}`; + document.getElementById("asientosOcupados").textContent = `Ocupados: ${zona.asientos_ocupados ?? 0}`; + + // Cargar los asientos disponibles + await cargarAsientos(zona.id_zona); +} + +async function cargarAsientos(idZona) { + try { + const respuesta = await fetch(`controladores/obtener_asiento.php?id_zona=${idZona}`); + if (!respuesta.ok) throw new Error("Error al cargar los asientos"); + + const asientos = await respuesta.json(); + + const asientosContainer = document.getElementById("asientos-container"); + asientosContainer.innerHTML = ""; // Limpiar antes de agregar nuevos + + asientos.forEach(asiento => { + const asientoDiv = document.createElement("div"); + asientoDiv.classList.add("asiento", asiento.estado); + asientoDiv.dataset.idAsiento = asiento.id_asiento; + asientoDiv.setAttribute("title", `Asiento #${asiento.id_asiento}`); + + const icono = document.createElement("i"); + icono.classList.add("bi", "bi-person-fill"); + + if (asiento.estado === "disponible") { + asientoDiv.appendChild(icono); + asientoDiv.addEventListener("click", () => seleccionarAsiento(asientoDiv, asiento.id_asiento)); + } else { + icono.style.opacity = "0.3"; // Opacidad para los asientos ocupados + asientoDiv.appendChild(icono); + } + + asientosContainer.appendChild(asientoDiv); + }); + + } catch (error) { + console.error("Error al obtener los asientos:", error); + } +} + +const asientosSeleccionados = new Set(); + +function seleccionarAsiento(elemento, idAsiento) { + if (asientosSeleccionados.has(idAsiento)) { + asientosSeleccionados.delete(idAsiento); + elemento.classList.remove("seleccionado"); + } else { + asientosSeleccionados.add(idAsiento); + elemento.classList.add("seleccionado"); + } + + document.getElementById("comprarBoletos").disabled = asientosSeleccionados.size === 0; +} + +document.getElementById("comprarBoletos").addEventListener("click", async () => { + if (asientosSeleccionados.size === 0) return; + + try { + const respuesta = await fetch("controladores/comprar_asiento.php", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ asientos: Array.from(asientosSeleccionados) }) + }); + + const resultado = await respuesta.json(); + if (resultado.success) { + alert("Compra realizada con éxito."); + location.reload(); + } else { + alert("Error al realizar la compra."); + } + } catch (error) { + console.error("Error al comprar boletos:", error); + } +}); diff --git a/ventaBoletos.html b/ventaBoletos.html new file mode 100644 index 0000000..33e5958 --- /dev/null +++ b/ventaBoletos.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html lang="es"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <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> + <link rel="stylesheet" href="css/conciertos.css"> + <link rel="stylesheet" href="css/ventanaBoletos.css"> + <link rel="stylesheet" + href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css"> +</head> +<body> + <nav> + <a href="ventanaInsertarConcierto.html" class="navbar-brand">TicketFei</a> + <div class="nav-links"> + <a href="ventanaInsertarConcierto.html">Crear Conciertos</a> + <a href="ventanaConciertos.html">Ver Conciertos</a> + <a href="#">Reporte Ventas</a> + </div> + </nav> + + <!-- Contenedor principal --> + <div class="main-container"> + <div class="cardIzq"> + <h2>Zonas del Concierto</h2> + <img src="img/mapa.png" alt="Mapa de zonas del concierto"> + <div id="zonas-container" class="zones-container"></div> + </div> + + <!-- Lado derecho con tarjetas --> + <div class="cardDer"> + <div class="card"> + <h2 class="text-xl font-bold mb-4">Detalles del concierto</h2> + <div id="zona-info" class="space-y-2"> + <h2 id="nombre_concierto">Nombre del Concierto</h2> + <p id="artista">Artista: </p> + <p id="fecha">Fecha: </p> + <h2 id="zonaNombre">Zona</h2> + <p id="zonaCapacidad">Capacidad: </p> + <p id="zonaPrecio">Precio: </p> + <p id="asientosDisponibles">Disponibles: </p> + <p id="asientosOcupados">Ocupados: </p> + </div> + </div> + <!-- Tarjeta para los asientos dentro de la columna derecha --> + <div class="card"> + <h2 class="text-xl font-bold mb-4">Asientos</h2> + <div id="asientos-container" class="asientos-container"></div> + </div> + + </div> + </div> + + <!-- Contenedor del botón centrado en la parte inferior --> + <div class="button-container"> + <button id="comprarBoletos" class="btn btn-primary" disabled>Comprar Asientos</button> + </div> + + <script defer src="js/ventaBoletos.js"></script> +</body> +</html> diff --git a/ventanaConciertos.html b/ventanaConciertos.html new file mode 100644 index 0000000..bed880f --- /dev/null +++ b/ventanaConciertos.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<html lang="es"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Registro de Conciertos</title> + <script src="https://cdn.tailwindcss.com"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script> + <link rel="stylesheet" href="css/conciertos.css"> + <link rel="stylesheet" href="css/modalEliminar.css"> +</head> +<body> + + <nav> + <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> + <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 id="listaConciertos"></div> + + <!-- Modal de mensaje --> + <div id="modalMensaje" class="modal"> + <div id="modalPanel" class="modal-contenido"> + <p id="modalTexto"></p> + <button id="cerrarModal" class="btn-modal">Cerrar</button> + </div> + </div> + + <!-- Modal de confirmación --> + <div id="modalConfirmacion" class="modal"> + <div class="modal-contenido"> + <p id="modalConfirmarTexto"></p> + <button id="btnCancelarEliminar" class="btn-modal">Cancelar</button> + <button id="btnConfirmarEliminar" class="btn-modal">Eliminar</button> + </div> + </div> + + <script src="js/conciertos.js"></script> + +</body> +</html> diff --git a/ventanaInsertarConcierto.html b/ventanaInsertarConcierto.html new file mode 100644 index 0000000..9685751 --- /dev/null +++ b/ventanaInsertarConcierto.html @@ -0,0 +1,117 @@ +<!DOCTYPE html> +<html lang="es"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Registrar Concierto</title> + <link rel="stylesheet" href="css/conciertos.css"> + <link rel="stylesheet" href="css/formulario.css"> +</head> +<body> + + <!-- Navbar --> + <nav> + <a href="ventanaInsertarConcierto.html" class="navbar-brand">TicketFei</a> + <div class="nav-links"> + <a href="ventanaInsertarConcierto.html">Crear Conciertos</a> + <a href="ventanaConciertos.html">Ver Conciertos</a> + <a href="#">Reporte Ventas</a> + </div> + </nav> + + <!-- Contenedor --> + <div class="container"> + <h2 class="text-center">Registrar Concierto</h2> + <form id="formulario"> + + <!-- Paso 1: Datos generales --> + <div id="paso1"> + <div class="mb-3"> + <label for="nombre_concierto">Nombre del Concierto:</label> + <input type="text" id="nombre_concierto" required> + </div> + <div class="mb-3"> + <label for="artista">Artista:</label> + <input type="text" id="artista" required> + </div> + <div class="mb-3"> + <label for="fecha">Fecha del Concierto:</label> + <input type="date" id="fecha" required> + </div> + <button type="button" class="btn next-btn" onclick="siguientePaso(2)">Siguiente</button> + </div> + + <!-- Paso 2: Dirección --> + <div id="paso2" class="d-none"> + <div class="mb-3"> + <label for="calle">Calle:</label> + <input type="text" id="calle" required> + </div> + <div class="mb-3"> + <label for="colonia">Colonia:</label> + <input type="text" id="colonia" required> + </div> + <div class="mb-3"> + <label for="numero_direccion">Número exterior:</label> + <input type="text" id="numero_direccion" required> + </div> + <div class="mb-3"> + <label for="codigo_postal">Código Postal:</label> + <input type="text" id="codigo_postal" required> + </div> + <div class="mb-3"> + <label for="estado">Estado:</label> + <input type="text" id="estado" required> + </div> + <div class="mb-3"> + <label for="capacidad_total">Capacidad Total:</label> + <input type="number" id="capacidad_total" required> + </div> + <button type="button" class="btn prev-btn" onclick="siguientePaso(1)">Anterior</button> + <button type="button" class="btn next-btn" onclick="siguientePaso(3)">Siguiente</button> + </div> + + <!-- Paso 3: Zonas --> + <div id="paso3" class="d-none"> + <fieldset> + <legend>Zonas</legend> + + + <div class="mb-3"> + <label>Zona General - Capacidad:</label> + <input type="number" id="capacidad_general" required oninput="actualizarCapacidad()"> + <label>Precio:</label> + <input type="number" step="0.01" id="precio_general" required> + </div> + + <div class="mb-3"> + <label>Zona Plata - Capacidad:</label> + <input type="number" id="capacidad_plata" required oninput="actualizarCapacidad()"> + <label>Precio:</label> + <input type="number" step="0.01" id="precio_plata" required> + </div> + + <div class="mb-3"> + <label>Zona Oro - Capacidad:</label> + <input type="number" id="capacidad_oro" required oninput="actualizarCapacidad()"> + <label>Precio:</label> + <input type="number" step="0.01" id="precio_oro" required> + </div> + + <div class="mb-3"> + <label>Zona VIP - Capacidad:</label> + <input type="number" id="capacidad_vip" required oninput="actualizarCapacidad()"> + <label>Precio:</label> + <input type="number" step="0.01" id="precio_vip" required> + </div> + </fieldset> + <button type="button" class="btn prev-btn" onclick="siguientePaso(2)">Anterior</button> + <button type="submit" class="btn submit-btn">Crear Concierto</button> + </div> + </form> + <div id="mensaje" class="mt-3 text-center"></div> + </div> + + <script src="js/crearConciertos.js"></script> +</body> +</html>