1ra Versión

This commit is contained in:
MarcosFlorSalcedo
2026-07-14 18:24:27 -03:00
commit 8ec55e40fc
86 changed files with 15839 additions and 0 deletions
+621
View File
@@ -0,0 +1,621 @@
// Estado global de la reserva
let estadoReserva = {
tipoCancha: null,
fecha: null,
horario: null,
numeroCancha: null,
quincho: false,
descuento: 0,
cuponeado: false,
reservasRestantes: null,
cantidadReservas: 0,
umbral: 0
};
// Obtener datos de las canchas al cargar
let datoscCanchas = { futbol: null, padel: null, quincho: null };
// Variable global para guardar el código de WhatsApp temporalmente
let codigoCorrecto = null;
// Cargar datos de las canchas desde la base de datos
async function cargarDatosCanchas() {
try {
const response = await fetch('../php/Obtener_Canchas.php');
const data = await response.json();
console.log('Datos recibidos de Obtener_Canchas.php:', data);
const canchas = Array.isArray(data)
? data
: (data && Array.isArray(data.canchas) ? data.canchas : []);
if (!canchas.length) {
console.warn('No se recibieron canchas válidas desde el servidor:', data);
return;
}
canchas.forEach(cancha => {
const rawTipo = String(cancha.Tipo ?? cancha.tipo ?? '').trim().toUpperCase();
const tipo = rawTipo === 'F' || rawTipo === 'FUTBOL' ? 'F'
: rawTipo === 'P' || rawTipo === 'PADEL' ? 'P'
: rawTipo === 'Q' || rawTipo === 'QUINCHO' ? 'Q'
: null;
if (!tipo) {
console.warn('Tipo de cancha desconocido en la respuesta:', rawTipo, cancha);
return;
}
const duracion = parseInt(cancha.Duracion ?? cancha.duracion, 10) || 0;
const precio = parseFloat(cancha.Precio ?? cancha.precio_venta ?? cancha.precio) || 0;
const apertura = cancha.Hora_Apertura ?? cancha.apertura ?? null;
const cierre = cancha.Hora_Cierre ?? cancha.cierre ?? null;
const cantidad = parseInt(cancha.Cant_Canchas ?? cancha.cantidad, 10) || 0;
const reservas = parseInt(cancha.Cant_Reservas_Cupon ?? cancha.reservas, 10) || 0;
const descuento = parseFloat(cancha.Descuento_Cupon ?? cancha.descuento_cupon) || 0;
const maxFaltas = parseInt(cancha.Cant_Faltas ?? cancha.cant_faltas, 10) || 0;
const duracionCupon = parseInt(cancha.Duracion_Cupon ?? cancha.duracion_cupon, 10) || 0;
if (tipo === 'F') {
datoscCanchas.futbol = {
duracion,
precio,
apertura,
cierre,
cantidad,
reservasParaCupon: reservas,
descuentoCupon: descuento,
maxFaltas,
duracionCupon
};
} else if (tipo === 'P') {
datoscCanchas.padel = {
duracion,
precio,
apertura,
cierre,
cantidad,
reservasParaCupon: reservas,
descuentoCupon: descuento,
maxFaltas,
duracionCupon
};
} else if (tipo === 'Q') {
datoscCanchas.quincho = {
precio,
reservasMax: reservas
};
}
});
console.log('Datos de canchas cargados:', datoscCanchas);
} catch (error) {
console.error('Error al cargar datos de canchas desde BD:', error);
}
}
// Inicializar la interfaz
function inicializarReserva() {
const botonesCancha = document.querySelectorAll('.btn-cancha-tipo');
botonesCancha.forEach(btn => {
btn.addEventListener('click', (e) => {
botonesCancha.forEach(b => b.classList.remove('activo'));
btn.classList.add('activo');
estadoReserva.tipoCancha = btn.dataset.tipo;
estadoReserva.fecha = null;
estadoReserva.horario = null;
document.querySelector('.selector-fecha').style.display = 'block';
document.querySelector('.selector-horario').style.display = 'none';
document.querySelector('.datos-cliente').style.display = 'none';
document.querySelector('.resumen-reserva').style.display = 'none';
generarDiasDisponibles();
});
});
// Botón volver
document.querySelector('.btn-volver').addEventListener('click', () => {
document.querySelector('.resumen-reserva').style.display = 'none';
});
// --- LÓGICA DE DATOS DEL CLIENTE ---
document.querySelector('.btn-verificar').addEventListener('click', async () => {
const nombre = document.getElementById('cliente-nombre').value.trim();
const telefono = document.getElementById('cliente-telefono').value.trim();
const dni = document.getElementById('cliente-dni').value.trim();
if (!nombre || !telefono || !dni) {
alert('Por favor completa todos tus datos antes de continuar');
return;
}
if (!estadoReserva.numeroCancha) {
alert('Selecciona primero el número de cancha disponible.');
return;
}
await obtenerEstadoCupon(dni, estadoReserva.tipoCancha);
actualizarResumen();
document.querySelector('.resumen-reserva').style.display = 'block';
});
// Botón "Confirmar Reserva" (Pide el código)
const btnSolicitar = document.getElementById('btn-solicitar-reserva');
if (btnSolicitar) {
btnSolicitar.addEventListener('click', async () => {
await solicitarCodigoWhatsApp();
});
}
// Botón "Validar y Reservar" (Verifica el código)
const btnVerificarWA = document.getElementById('btn-verificar-codigo');
if (btnVerificarWA) {
btnVerificarWA.addEventListener('click', async () => {
const codigoIngresado = document.getElementById('codigo-verificacion').value;
if (codigoIngresado === codigoCorrecto) {
await guardarReservaFinal();
} else {
alert("Código incorrecto. Por favor, inténtelo de nuevo.");
}
});
}
// Checkbox quincho
document.getElementById('incluir-quincho').addEventListener('change', (e) => {
estadoReserva.quincho = e.target.checked;
const precioQuincho = datoscCanchas.quincho?.precio || 0;
if (e.target.checked && precioQuincho > 0) {
document.querySelector('.precio-quincho').style.display = 'inline-block';
document.getElementById('quincho-reservas').style.display = 'block';
document.getElementById('precio-quincho-val').textContent = precioQuincho.toFixed(2);
} else {
estadoReserva.quincho = false;
document.querySelector('.precio-quincho').style.display = 'none';
document.getElementById('quincho-reservas').style.display = 'none';
e.target.checked = false;
}
actualizarPrecioTotal();
});
const formComprobante = document.getElementById('form-comprobante');
if (formComprobante) {
formComprobante.addEventListener('submit', async (e) => {
e.preventDefault();
await enviarComprobantePago();
});
}
}
// Generar días disponibles
function generarDiasDisponibles() {
const contenedor = document.getElementById('dias-disponibles');
contenedor.innerHTML = '';
const hoy = new Date();
const diasSemana = ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sab'];
for (let i = 0; i < 7; i++) {
const fecha = new Date(hoy);
fecha.setDate(fecha.getDate() + i);
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'btn-dia';
btn.innerHTML = `<div>${diasSemana[fecha.getDay()]}</div><div>${fecha.getDate()}/${fecha.getMonth() + 1}</div>`;
btn.dataset.fecha = fecha.toISOString().split('T')[0];
btn.addEventListener('click', async () => {
document.querySelectorAll('.btn-dia').forEach(b => b.classList.remove('activo'));
btn.classList.add('activo');
estadoReserva.fecha = btn.dataset.fecha;
estadoReserva.horario = null;
generarHorarios();
await actualizarDisponibilidadQuincho(btn.dataset.fecha);
document.querySelector('.selector-horario').style.display = 'block';
document.querySelector('.datos-cliente').style.display = 'none';
document.querySelector('.resumen-reserva').style.display = 'none';
});
contenedor.appendChild(btn);
}
}
// Obtener disponibilidad de quinchos
async function obtenerDisponibilidadQuincho(fecha) {
try {
const response = await fetch(`../php/Obtener_Disponibilidad_Quincho.php?fecha=${fecha}`);
const data = await response.json();
return data.success ? data.disponibles : 0;
} catch (error) { return 0; }
}
// Actualizar disponibilidad de quincho en UI
async function actualizarDisponibilidadQuincho(fecha) {
const disponibles = await obtenerDisponibilidadQuincho(fecha);
const checkboxQuincho = document.getElementById('incluir-quincho');
const quinchoInfo = document.getElementById('quincho-reservas');
const quinchoAlerta = document.getElementById('quincho-no-disponible');
document.getElementById('quincho-disp').textContent = disponibles;
if (disponibles === 0) {
checkboxQuincho.disabled = true;
checkboxQuincho.checked = false;
estadoReserva.quincho = false;
quinchoInfo.style.display = 'none';
quinchoAlerta.style.display = 'block';
document.querySelector('.precio-quincho').style.display = 'none';
} else {
checkboxQuincho.disabled = false;
quinchoAlerta.style.display = 'none';
quinchoInfo.style.display = 'block';
}
actualizarPrecioTotal();
}
// Obtener disponibilidad de cancha
async function obtenerDisponibilidadCancha(fecha, horario, duracion, tipo) {
try {
const response = await fetch(`../php/Obtener_Disponibilidad_Cancha.php?fecha=${fecha}&horario=${horario}&duracion=${duracion}&tipo=${tipo === 'futbol' ? 'F' : 'P'}`);
const data = await response.json();
return data.success ? data.disponibles : 0;
} catch (error) { return 0; }
}
// Obtener números de canchas
async function obtenerNumerosCanchas(fecha, horario, duracion, tipo) {
try {
const response = await fetch(`../php/Obtener_Canchas_Numeros.php?fecha=${fecha}&horario=${horario}&duracion=${duracion}&tipo=${tipo === 'futbol' ? 'F' : 'P'}`);
const data = await response.json();
return data.success ? data.canchas : [];
} catch (error) { return []; }
}
// Generar selección de número de cancha
async function generarSeleccionNumeroCancha(horarioInicio) {
const contenedor = document.getElementById('botones-canchas-numeros');
contenedor.innerHTML = '';
const tipo = estadoReserva.tipoCancha;
const canchas = await obtenerNumerosCanchas(estadoReserva.fecha, horarioInicio, datoscCanchas[tipo].duracion, tipo);
canchas.forEach(cancha => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'btn-numero-cancha';
btn.textContent = `Cancha ${cancha.numero}`;
btn.dataset.numero = cancha.numero;
if (!cancha.disponible) {
btn.disabled = true;
btn.classList.add('no-disponible');
btn.style.opacity = '0.5';
btn.style.cursor = 'not-allowed';
} else {
btn.addEventListener('click', () => {
document.querySelectorAll('.btn-numero-cancha').forEach(b => b.classList.remove('activo'));
btn.classList.add('activo');
estadoReserva.numeroCancha = cancha.numero;
document.querySelector('.datos-cliente').style.display = 'block';
});
}
contenedor.appendChild(btn);
});
}
// Generar horarios
async function generarHorarios() {
const contenedor = document.getElementById('grid-horarios');
contenedor.innerHTML = '';
const tipo = estadoReserva.tipoCancha;
const datos = datoscCanchas[tipo];
if (!datos || typeof datos.duracion !== 'number' || !datos.apertura || !datos.cierre) {
console.error('No hay datos de configuración válidos para la cancha seleccionada:', tipo, datos);
alert('Error al cargar los horarios. Revisa la configuración de la cancha.');
return;
}
const duracion = datos.duracion;
const [aperturaH, aperturaM] = datos.apertura.split(':').map(Number);
const [cierreH, cierreM] = datos.cierre.split(':').map(Number);
const aperturaTotalMin = aperturaH * 60 + aperturaM;
const cierreTotalMin = cierreH * 60 + cierreM;
const ahora = new Date();
const [selYear, selMonth, selDay] = estadoReserva.fecha.split('-').map(Number);
const fechaSeleccionada = new Date(selYear, selMonth - 1, selDay);
const hoy = new Date(ahora.getFullYear(), ahora.getMonth(), ahora.getDate());
const fechaSelSoloFecha = new Date(fechaSeleccionada.getFullYear(), fechaSeleccionada.getMonth(), fechaSeleccionada.getDate());
const esHoy = fechaSelSoloFecha.getTime() === hoy.getTime();
const minutoActual = ahora.getHours() * 60 + ahora.getMinutes();
let horaActualMin = aperturaTotalMin;
const horarios = [];
while (horaActualMin + duracion <= cierreTotalMin) {
const horas = Math.floor(horaActualMin / 60);
const minutos = horaActualMin % 60;
const horaFin = Math.floor((horaActualMin + duracion) / 60);
const minutesFin = (horaActualMin + duracion) % 60;
horarios.push({
inicio: `${String(horas).padStart(2, '0')}:${String(minutos).padStart(2, '0')}`,
fin: `${String(horaFin).padStart(2, '0')}:${String(minutesFin).padStart(2, '0')}`,
minutos: horaActualMin
});
horaActualMin += duracion;
}
for (const horario of horarios) {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'horario-slot';
btn.textContent = `${horario.inicio}\n-\n${horario.fin}`;
const inicioArr = horario.inicio.split(':').map(Number);
const inicioMin = inicioArr[0] * 60 + inicioArr[1];
const esPasado = fechaSelSoloFecha.getTime() < hoy.getTime() || (esHoy && inicioMin < minutoActual);
let disponibles = 0;
if (!esPasado) {
disponibles = await obtenerDisponibilidadCancha(estadoReserva.fecha, genericNormalizeTipo(horario.inicio), duracion, tipo);
}
if (esPasado || disponibles === 0) {
btn.disabled = true;
btn.classList.add('no-disponible');
btn.style.opacity = '0.5';
btn.style.cursor = 'not-allowed';
} else {
btn.addEventListener('click', async () => {
document.querySelectorAll('.horario-slot').forEach(b => b.classList.remove('activo'));
btn.classList.add('activo');
estadoReserva.horario = `${horario.inicio} - ${horario.fin}`;
estadoReserva.numeroCancha = null;
document.getElementById('incluir-quincho').checked = false;
estadoReserva.quincho = false;
document.querySelector('.precio-quincho').style.display = 'none';
document.getElementById('quincho-reservas').style.display = 'none';
await generarSeleccionNumeroCancha(horario.inicio);
document.querySelector('.selector-numero-cancha').style.display = 'block';
document.querySelector('.datos-cliente').style.display = 'none';
document.querySelector('.resumen-reserva').style.display = 'none';
});
}
contenedor.appendChild(btn);
}
}
function genericNormalizeTipo(inicio) {
return inicio;
}
// Actualizar resumen
function actualizarResumen() {
const tipo = estadoReserva.tipoCancha;
const datos = datoscCanchas[tipo];
document.getElementById('res-cancha').textContent = `${tipo === 'futbol' ? 'Fútbol' : 'Pádel'} - Cancha ${estadoReserva.numeroCancha}`;
document.getElementById('res-fecha').textContent = new Date(estadoReserva.fecha).toLocaleDateString('es-AR', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
document.getElementById('res-horario').textContent = estadoReserva.horario;
const duracionHoras = Math.floor(datos.duracion / 60);
const duracionMinutos = datos.duracion % 60;
document.getElementById('res-duracion').textContent = duracionHoras > 0 ? (duracionMinutos > 0 ? `${duracionHoras}h ${duracionMinutos}m` : `${duracionHoras}h`) : `${duracionMinutos}m`;
if (estadoReserva.cuponeado) {
document.getElementById('res-cupon').textContent = `Cupón activo: ${(estadoReserva.descuento * 100).toFixed(0)}% aplicado`;
} else if (estadoReserva.reservasRestantes !== null) {
document.getElementById('res-cupon').textContent = estadoReserva.reservasRestantes > 0 ? `Faltan ${estadoReserva.reservasRestantes} reserva(s) para cupón` : 'Sin cupón activo';
} else {
document.getElementById('res-cupon').textContent = '-';
}
actualizarPrecioTotal();
}
function actualizarPrecioTotal() {
const tipo = estadoReserva.tipoCancha;
const datos = datoscCanchas[tipo];
if (!datos || typeof datos.precio !== 'number') {
console.error('No se puede actualizar el precio porque no se cargaron los datos de cancha:', tipo, datos);
return;
}
const basePrecio = datos.precio;
const descuentoMonto = basePrecio * (estadoReserva.descuento || 0);
const precioCancha = basePrecio - descuentoMonto;
const totalPrecio = precioCancha + (estadoReserva.quincho ? (datoscCanchas.quincho?.precio || 0) : 0);
const precioOriginalElement = document.getElementById('precio-original');
const precioOriginalValor = document.getElementById('res-precio-original');
const labelPrecio = document.getElementById('label-precio');
if (estadoReserva.descuento > 0) {
// Mostrar precio original tachado
precioOriginalValor.textContent = `$${basePrecio.toFixed(2)}`;
precioOriginalElement.style.display = 'block';
labelPrecio.textContent = 'Precio Final:';
} else {
// Ocultar precio original
precioOriginalElement.style.display = 'none';
labelPrecio.textContent = 'Precio:';
}
document.getElementById('res-precio').textContent = `$${totalPrecio.toFixed(2)}`;
}
async function obtenerEstadoCupon(dni, tipo) {
try {
const response = await fetch(`../php/Obtener_Cupon.php?dni=${encodeURIComponent(dni)}&tipo=${encodeURIComponent(tipo)}`);
const data = await response.json();
if (!data.success) {
throw new Error(data.message || 'No se pudo obtener el estado del cupón');
}
estadoReserva.cuponeado = data.aplicarDescuento;
estadoReserva.descuento = data.aplicarDescuento ? parseFloat(data.descuentoCanchas) : 0;
estadoReserva.cantidadReservas = data.cantidadReservas;
estadoReserva.umbral = data.umbral;
estadoReserva.reservasRestantes = data.reservasRestantes;
if (!estadoReserva.cuponeado && data.reservasRestantes === 0 && data.umbral === 0) {
estadoReserva.reservasRestantes = null;
}
return data;
} catch (error) {
console.error('Error al obtener estado de cupón:', error);
alert('No se pudo determinar el estado del cupón. Intenta nuevamente.');
estadoReserva.cuponeado = false;
estadoReserva.descuento = 0;
estadoReserva.reservasRestantes = null;
return null;
}
}
// --- FUNCIONES DE WHATSAPP Y GUARDADO ---
async function solicitarCodigoWhatsApp() {
const nombre = document.getElementById('cliente-nombre').value.trim();
const telefono = document.getElementById('cliente-telefono').value.trim();
const btnSolicitar = document.getElementById('btn-solicitar-reserva');
btnSolicitar.disabled = true;
btnSolicitar.textContent = 'Enviando código...';
try {
const response = await fetch('../php/Enviar_Codigo_WSP.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ telefono: telefono, nombre: nombre })
});
const data = await response.json();
if (data.success) {
codigoCorrecto = data.codigo;
// Ocultar botones de Confirmar/Volver y mostrar campo de validación
document.querySelector('.botones-reserva').style.display = 'none';
document.getElementById('lbl-telefono').textContent = telefono;
document.getElementById('seccion-verificacion').style.display = 'block';
console.log('Código enviado a Selenium:', data.codigo);
} else {
alert('Error al enviar el mensaje de WhatsApp.');
btnSolicitar.disabled = false;
btnSolicitar.textContent = 'Confirmar Reserva';
}
} catch (error) {
console.error('Error:', error);
alert('Error de conexión con el servidor.');
btnSolicitar.disabled = false;
btnSolicitar.textContent = 'Confirmar Reserva';
}
}
async function guardarReservaFinal() {
const tipo = estadoReserva.tipoCancha;
const datos = {
nombre: document.getElementById('cliente-nombre').value.trim(),
telefono: document.getElementById('cliente-telefono').value.trim(),
dni: document.getElementById('cliente-dni').value.trim(),
fecha: estadoReserva.fecha,
horario: estadoReserva.horario.split(' - ')[0],
tipo: tipo === 'futbol' ? 'F' : 'P',
numeroCancha: estadoReserva.numeroCancha,
monto: datoscCanchas[tipo].precio,
incluirQuincho: estadoReserva.quincho,
montoQuincho: estadoReserva.quincho ? datoscCanchas.quincho.precio : 0
};
try {
const response = await fetch('../php/Guardar_Reserva.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(datos)
});
const result = await response.json();
if (result.success) {
alert('¡Reserva confirmada exitosamente!');
// ADAPTACIÓN: Ocultamos la sección de reserva en vez de refrescar la pantalla
document.getElementById('seccion-verificacion').style.display = 'none';
document.querySelector('.resumen-reserva').style.display = 'none';
// Cargamos el ID retornado por tu servidor en el input oculto del formulario
// Nota: Tu php/Guardar_Reserva.php debe retornar un campo id_reserva (ej: result.id_reserva)
const idNuevaReserva = result.id_reserva || result.id || 0;
document.getElementById('comprobante-id-reserva').value = idNuevaReserva;
// Hacemos visible la ventana de carga para la imagen
document.getElementById('seccion-comprobante').style.display = 'block';
} else {
alert('Error al guardar: ' + result.message);
}
} catch (error) {
console.error('Error:', error);
alert('Ocurrió un error crítico al intentar guardar la reserva.');
}
}
// --- FUNCIÓN ASÍNCRONA PARA SUBIR EL COMPROBANTE DE PAGO ---
async function enviarComprobantePago() {
const idReserva = document.getElementById('comprobante-id-reserva').value;
const fileInput = document.getElementById('input-file-comprobante');
const btnSubir = document.querySelector('#form-comprobante button[type="submit"]');
if (fileInput.files.length === 0) {
alert("Por favor, selecciona una foto de tu comprobante.");
return;
}
btnSubir.disabled = true;
btnSubir.textContent = "Subiendo archivo...";
// Construimos el FormData binario
const formData = new FormData();
formData.append('id_reserva', idReserva);
formData.append('comprobante', fileInput.files[0]);
try {
const response = await fetch('../php/Subir_Comprobante.php', {
method: 'POST',
body: formData // El navegador setea automáticamente el multipart/form-data
});
const data = await response.json();
if (data.success) {
alert("¡Comprobante enviado con éxito! El administrador verificará su pago en la brevedad.");
location.reload(); // Ahora sí, reiniciamos la vista limpia del complejo
} else {
alert("Error: " + data.error);
btnSubir.disabled = false;
btnSubir.textContent = "Enviar Comprobante";
}
} catch (error) {
console.error("Error al subir el comprobante:", error);
alert("Error de conexión al procesar el archivo.");
btnSubir.disabled = false;
btnSubir.textContent = "Enviar Comprobante";
}
}
// Inicializar cuando el DOM esté listo
document.addEventListener('DOMContentLoaded', async () => {
await cargarDatosCanchas();
inicializarReserva();
});