// Variables globales para mantener el auto-refresh let filtrosActuales = {}; let intervaloActualizacion = null; // Datos de las canchas (similar a Reservar_Turno.js) let datosCanchas = { futbol: null, padel: null, quincho: null }; // Cargar datos de las canchas async function cargarDatosCanchastabla() { try { const response = await fetch('../php/Obtener_Canchas.php'); const data = await response.json(); const canchas = Array.isArray(data) ? data : (data && Array.isArray(data.canchas) ? data.canchas : []); if (!canchas.length) 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) return; const duracion = parseInt(cancha.Duracion ?? cancha.duracion, 10) || 0; const apertura = cancha.Hora_Apertura ?? cancha.apertura ?? null; const cierre = cancha.Hora_Cierre ?? cancha.cierre ?? null; if (tipo === 'F') { datosCanchas.futbol = { duracion, apertura, cierre }; } else if (tipo === 'P') { datosCanchas.padel = { duracion, apertura, cierre }; } }); } catch (error) { console.error('Error al cargar datos de canchas:', error); } } document.addEventListener("DOMContentLoaded", async () => { // Cargar datos de canchas await cargarDatosCanchastabla(); // Establecer la fecha de hoy por defecto const hoy = new Date().toISOString().split('T')[0]; const inputFecha = document.querySelector(".filtro-formulario input[name='fecha']"); if (inputFecha) { inputFecha.value = hoy; } cargarReservas({ fecha: hoy }); // Cargar reservas del día actual iniciarActualizacionAutomatica(); // Inicia el auto-refresh const formFiltro = document.querySelector(".filtro-formulario"); const btnReestablecer = document.getElementById("btn-reestablecer-filtros"); // Asegurar estilos para reprogramar (inyección en head si el css no está presente) ensureReprogramStyles(); // Agregar event listeners para búsqueda dinámica if (formFiltro) { const inputs = formFiltro.querySelectorAll("input, select"); inputs.forEach(input => { input.addEventListener("change", () => { aplicarFiltrosDinamicos(); }); input.addEventListener("input", () => { aplicarFiltrosDinamicos(); }); }); } // Event listener para botón reestablecer if (btnReestablecer) { btnReestablecer.addEventListener("click", (e) => { e.preventDefault(); reestablecerFiltros(); }); } }); function aplicarFiltrosDinamicos() { const formFiltro = document.querySelector(".filtro-formulario"); const formData = new FormData(formFiltro); const filtros = { fecha: formData.get("fecha"), nombre: formData.get("nombre"), dni: formData.get("dni"), tipo: formData.get("tipo_cancha") }; cargarReservas(filtros); } function reestablecerFiltros() { const formFiltro = document.querySelector(".filtro-formulario"); // Limpiar todos los inputs const inputs = formFiltro.querySelectorAll("input, select"); inputs.forEach(input => { if (input.name === "fecha") { // Establecer fecha de hoy input.value = new Date().toISOString().split('T')[0]; } else { input.value = ""; } }); // Cargar reservas del día actual const hoy = new Date().toISOString().split('T')[0]; cargarReservas({ fecha: hoy }); } function cargarReservas(filtros = {}) { // Guardar los filtros actuales para el auto-refresh (solo los que tienen valor) const filtrosConValor = {}; for (const [key, value] of Object.entries(filtros)) { if (value) { filtrosConValor[key] = value; } } if (Object.keys(filtrosConValor).length > 0) { filtrosActuales = filtrosConValor; } let url = "../php/Obtener_Reservas.php?"; const params = new URLSearchParams(); // Usar los filtros guardados si no se proporcionan nuevos const filtrosAUsar = Object.keys(filtrosConValor).length > 0 ? filtrosConValor : filtrosActuales; // Solo agregamos parámetros si tienen valor if (filtrosAUsar.fecha) params.append("fecha", filtrosAUsar.fecha); if (filtrosAUsar.nombre) params.append("nombre", filtrosAUsar.nombre); if (filtrosAUsar.dni) params.append("dni", filtrosAUsar.dni); if (filtrosAUsar.tipo) params.append("tipo", filtrosAUsar.tipo); fetch(url + params.toString()) .then(res => res.json()) .then(reservas => { const tablaBody = document.querySelector("#tabla-reservas tbody"); tablaBody.innerHTML = ""; if (reservas.length === 0) { tablaBody.innerHTML = "
Error: No se pudieron cargar los datos de las canchas
'; 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] = fechaSeleccionada.split('-').map(Number); const fechaSelDate = new Date(selYear, selMonth - 1, selDay); const hoy = new Date(ahora.getFullYear(), ahora.getMonth(), ahora.getDate()); const esHoy = fechaSelDate.getTime() === hoy.getTime(); const minutoActual = ahora.getHours() * 60 + ahora.getMinutes(); let horaActualMin = aperturaTotalMin; while (horaActualMin + duracion <= cierreTotalMin) { const horas = Math.floor(horaActualMin / 60); const minutos = horaActualMin % 60; const horaFin = Math.floor((horaActualMin + duracion) / 60); const minutosFin = (horaActualMin + duracion) % 60; const inicio = `${String(horas).padStart(2, '0')}:${String(minutos).padStart(2, '0')}`; const fin = `${String(horaFin).padStart(2, '0')}:${String(minutosFin).padStart(2, '0')}`; const inicioMin = horas * 60 + minutos; const esPasado = fechaSelDate < hoy || (esHoy && inicioMin < minutoActual); const btn = document.createElement('button'); btn.className = 'horario-btn'; btn.textContent = `${inicio}\n-\n${fin}`; btn.dataset.hora = `${String(horas).padStart(2, '0')}:${String(minutos).padStart(2, '0')}:00`; // Verificar disponibilidad let disponibles = 0; if (!esPasado) { disponibles = await obtenerDisponibilidadCanchaReprogramar( fechaSeleccionada, inicio, duracion, tipoCancha ); } // Desactivar si es pasado o no hay canchas disponibles if (esPasado || disponibles === 0) { btn.disabled = true; btn.classList.add('no-disponible'); btn.style.opacity = '0.5'; btn.style.cursor = 'not-allowed'; } horariosGrid.appendChild(btn); horaActualMin += duracion; } // Limpiar grid de canchas modal.querySelector('.canchas-grid').innerHTML = ''; } // Obtener disponibilidad de cancha para reprogramación async function obtenerDisponibilidadCanchaReprogramar(fecha, horario, duracion, tipo) { try { const response = await fetch( `../php/Obtener_Disponibilidad_Cancha.php?fecha=${fecha}&horario=${horario}&duracion=${duracion}&tipo=${tipo}` ); const data = await response.json(); return data.success ? data.disponibles : 0; } catch (error) { console.error('Error obteniendo disponibilidad:', error); return 0; } } // Generar selección de canchas disponibles async function generarSeleccionCanchasReprogramar(modal, tipoCancha, fecha, horario) { const canchasGrid = modal.querySelector('.canchas-grid'); canchasGrid.innerHTML = ''; const tipoCode = tipoCancha === 'F' ? 'futbol' : 'padel'; const datos = datosCanchas[tipoCode]; if (!datos || !datos.duracion) { canchasGrid.innerHTML = 'Error: No se pudieron cargar los datos
'; return; } try { const response = await fetch( `../php/Obtener_Canchas_Numeros.php?fecha=${fecha}&horario=${horario}&duracion=${datos.duracion}&tipo=${tipoCancha}` ); const data = await response.json(); if (data.success && data.canchas) { data.canchas.forEach(cancha => { const btn = document.createElement('button'); btn.className = 'cancha-btn'; 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'; } canchasGrid.appendChild(btn); }); } } catch (error) { console.error('Error generando selección de canchas:', error); canchasGrid.innerHTML = 'Error al cargar canchas disponibles
'; } } function actualizarEstadoBotonConfirm(modal, dia, hora, cancha) { const btn = modal.querySelector('.btn-confirmar'); if (dia && hora && cancha) { btn.disabled = false; } else { btn.disabled = true; } } function closeModalReprogramar(overlay) { if (overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay); } function reprogramarReserva(idReserva, fechaHora, overlay) { fetch('../php/Actualizar_Horario_Reserva.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id_reserva: idReserva, fecha_hora: fechaHora }) }) .then(async res => { if (!res.ok) throw new Error('HTTP ' + res.status); const text = await res.text(); try { return JSON.parse(text); } catch (error) { throw new Error('Respuesta no JSON: ' + text.trim().replace(/\s+/g, ' ')); } }) .then(data => { if (data.success) { closeModalReprogramar(overlay); cargarReservas(); alert('Reserva reprogramada correctamente'); } else { alert('Error al reprogramar: ' + (data.error || 'Error desconocido')); } }) .catch(err => { console.error('Error reprogramando:', err); alert('Error al reprogramar la reserva: ' + err.message); }); } // Inyecta estilos mínimos si no están presentes en el CSS cargado function ensureReprogramStyles() { if (document.getElementById('reprogramar-styles')) return; const css = ` .reprogramar-btn{margin-left:8px;padding:6px 8px;border-radius:6px;background:rgba(30,125,30,0.12);border:1px solid rgba(30,125,30,0.2);color:#d9f2d9;cursor:pointer;font-weight:600} .reprogramar-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.6);display:flex;align-items:center;justify-content:center;z-index:9999;padding:20px} .reprogramar-modal{width:min(980px,96%);max-height:90vh;overflow:auto;background:#111;border-radius:12px;padding:18px;border:1px solid rgba(30,125,30,0.18)} .dias-grid{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:14px}.dia-btn{width:110px;height:70px;border-radius:10px;padding:8px;background:rgba(255,255,255,0.03);border:2px solid rgba(255,255,255,0.08);color:#fff;font-weight:700;cursor:pointer;white-space:pre-line;display:flex;flex-direction:column;align-items:center;justify-content:center} .dia-btn.selected{background:linear-gradient(135deg,#1e7d1e,#145214);border-color:#1e7d1e} .horarios-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:12px;margin-bottom:14px}.horario-btn{padding:12px;border-radius:10px;background:rgba(255,255,255,0.03);border:2px solid rgba(255,255,255,0.08);color:#fff;font-weight:700;cursor:pointer;white-space:pre-line} .horario-btn.selected{background:linear-gradient(135deg,#1e7d1e,#145214);border-color:#1e7d1e} .horario-btn.no-disponible{opacity:0.4 !important;cursor:not-allowed !important} .canchas-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:12px;margin-bottom:14px}.cancha-btn{padding:12px;border-radius:10px;background:rgba(255,255,255,0.03);border:2px solid rgba(255,255,255,0.08);color:#fff;font-weight:700;cursor:pointer} .cancha-btn.selected{background:linear-gradient(135deg,#1e7d1e,#145214);border-color:#1e7d1e} .cancha-btn.no-disponible{opacity:0.4 !important;cursor:not-allowed !important} .reprogramar-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}.reprogramar-actions .btn-cancelar,.reprogramar-actions .btn-confirmar{padding:10px 14px;border-radius:8px;font-weight:700;border:2px solid rgba(255,255,255,0.08);background:rgba(255,255,255,0.03);color:#fff} .reprogramar-actions .btn-confirmar:disabled{opacity:0.5;cursor:not-allowed} `; const style = document.createElement('style'); style.id = 'reprogramar-styles'; style.appendChild(document.createTextNode(css)); document.head.appendChild(style); } // Delegación: por si los listeners directos no se añadieron (tabla re-renderizada) document.addEventListener('click', (e) => { const btn = e.target.closest && e.target.closest('.reprogramar-btn'); if (!btn) return; e.stopPropagation(); if (btn.hasAttribute('disabled') || btn.classList.contains('disabled')) return; // evitar crear múltiples overlays if (document.querySelector('.reprogramar-overlay')) return; const fila = btn.closest('tr'); const idReserva = fila ? fila.getAttribute('data-id-reserva') : null; if (idReserva) crearModalReprogramar(idReserva, fila); });