1ra Versión
This commit is contained in:
@@ -0,0 +1,673 @@
|
||||
// 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 = "<tr><td colspan='9'>No se encontraron resultados.</td></tr>";
|
||||
return;
|
||||
}
|
||||
|
||||
reservas.forEach(r => {
|
||||
const tipoTexto = r.Tipo === 'F' ? 'Fútbol' : (r.Tipo === 'P' ? 'Pádel' : (r.Tipo === 'Q' ? 'Quincho' : 'Otro'));
|
||||
|
||||
// Convertir estado numérico a texto
|
||||
let estadoTexto = '';
|
||||
let estadoValor = parseInt(r.Estado);
|
||||
|
||||
if (estadoValor === 0) {
|
||||
estadoTexto = 'No Pagado';
|
||||
} else if (estadoValor === 1) {
|
||||
estadoTexto = 'Pagado';
|
||||
} else if (estadoValor === 2) {
|
||||
estadoTexto = 'Cancelado';
|
||||
}
|
||||
|
||||
// Verificar si la reserva es del pasado
|
||||
const fechaReserva = new Date(r.Fecha_Hora);
|
||||
const hoy = new Date();
|
||||
hoy.setHours(0, 0, 0, 0);
|
||||
fechaReserva.setHours(0, 0, 0, 0);
|
||||
|
||||
const esDelPasado = fechaReserva < hoy;
|
||||
const disabledAttr = esDelPasado ? 'disabled' : '';
|
||||
const disabledClass = esDelPasado ? 'disabled' : '';
|
||||
|
||||
const fila = document.createElement("tr");
|
||||
fila.setAttribute("data-id-reserva", r.ID_Reserva);
|
||||
fila.setAttribute("data-tipo-cancha", r.Tipo);
|
||||
fila.setAttribute("data-numero-cancha", r.Numero);
|
||||
fila.setAttribute("data-fecha-reserva", r.Fecha_Hora);
|
||||
fila.innerHTML = `
|
||||
<td>${r.Nombre}</td>
|
||||
<td>${r.DNI}</td>
|
||||
<td>${r.Telefono}</td>
|
||||
<td>${new Date(r.Fecha_Hora).toLocaleString()}</td>
|
||||
<td>${tipoTexto}</td>
|
||||
<td>${r.Numero}</td>
|
||||
<td>${parseFloat(r.Descuento_Cupon) > 0 ? (parseFloat(r.Descuento_Cupon) * 100).toFixed(0) + '%' : '0%'}</td>
|
||||
<td>$${parseFloat(r.Monto).toFixed(2)}</td>
|
||||
<td>
|
||||
<div class="estado-dropdown">
|
||||
<button class="estado-btn ${disabledClass}" data-estado="${estadoValor}" ${disabledAttr} title="${esDelPasado ? 'No se pueden modificar reservas pasadas' : ''}">
|
||||
<span class="estado-texto">${estadoTexto}</span>
|
||||
<span class="estado-flecha">▼</span>
|
||||
</button>
|
||||
<div class="estado-opciones" style="display: none;">
|
||||
<div class="estado-opcion" data-valor="0">No Pagado</div>
|
||||
<div class="estado-opcion" data-valor="1">Pagado</div>
|
||||
<div class="estado-opcion" data-valor="2">Cancelado</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="reprogramar-btn ${disabledClass}" ${disabledAttr} title="Reprogramar reserva">Reprogramar</button>
|
||||
<div class="reprogramar-container" style="display:none;"></div>
|
||||
</td>
|
||||
`;
|
||||
tablaBody.appendChild(fila);
|
||||
});
|
||||
|
||||
// Agregar event listeners al dropdown
|
||||
agregarListenersDropdown();
|
||||
agregarListenersReprogramar();
|
||||
})
|
||||
.catch(err => console.error("Error en fetch:", err));
|
||||
}
|
||||
|
||||
function agregarListenersDropdown() {
|
||||
const botonesEstado = document.querySelectorAll(".estado-btn");
|
||||
|
||||
botonesEstado.forEach(btn => {
|
||||
// Marcar la opción activa al inicio
|
||||
const estadoActual = btn.getAttribute("data-estado");
|
||||
const dropdown = btn.closest(".estado-dropdown");
|
||||
const opciones = dropdown.querySelectorAll(".estado-opcion");
|
||||
|
||||
opciones.forEach(op => {
|
||||
if (op.getAttribute("data-valor") === estadoActual) {
|
||||
op.classList.add("activo");
|
||||
} else {
|
||||
op.classList.remove("activo");
|
||||
}
|
||||
});
|
||||
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
// Si el botón está deshabilitado, no hacer nada
|
||||
if (btn.hasAttribute("disabled") || btn.classList.contains("disabled")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const opcionesDiv = dropdown.querySelector(".estado-opciones");
|
||||
|
||||
// Cerrar otros dropdowns abiertos
|
||||
document.querySelectorAll(".estado-opciones").forEach(op => {
|
||||
if (op !== opcionesDiv) op.style.display = "none";
|
||||
});
|
||||
|
||||
// Toggle del dropdown actual
|
||||
const estaAbierto = opcionesDiv.style.display === "block";
|
||||
opcionesDiv.style.display = estaAbierto ? "none" : "block";
|
||||
btn.setAttribute("aria-expanded", !estaAbierto);
|
||||
});
|
||||
});
|
||||
|
||||
// Click en opciones
|
||||
const opcionesEstado = document.querySelectorAll(".estado-opcion");
|
||||
opcionesEstado.forEach(opcion => {
|
||||
opcion.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const dropdown = opcion.closest(".estado-dropdown");
|
||||
const btn = dropdown.querySelector(".estado-btn");
|
||||
const textoSpan = btn.querySelector(".estado-texto");
|
||||
const opcionesDiv = dropdown.querySelector(".estado-opciones");
|
||||
|
||||
const nuevoValor = opcion.getAttribute("data-valor");
|
||||
const textoOpcion = opcion.textContent;
|
||||
|
||||
// Remover clase activo de todas las opciones
|
||||
dropdown.querySelectorAll(".estado-opcion").forEach(op => {
|
||||
op.classList.remove("activo");
|
||||
});
|
||||
|
||||
// Agregar clase activo a la opción seleccionada
|
||||
opcion.classList.add("activo");
|
||||
|
||||
// Actualizar el botón
|
||||
textoSpan.textContent = textoOpcion;
|
||||
btn.setAttribute("data-estado", nuevoValor);
|
||||
|
||||
// Ocultar dropdown
|
||||
opcionesDiv.style.display = "none";
|
||||
btn.setAttribute("aria-expanded", false);
|
||||
|
||||
// Guardar cambio en BD
|
||||
const fila = opcion.closest("tr");
|
||||
const idReserva = fila.getAttribute("data-id-reserva");
|
||||
actualizarEstadoReserva(idReserva, parseInt(nuevoValor));
|
||||
});
|
||||
});
|
||||
|
||||
// Cerrar dropdown si se hace clic fuera
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!e.target.closest(".estado-dropdown")) {
|
||||
document.querySelectorAll(".estado-opciones").forEach(op => {
|
||||
op.style.display = "none";
|
||||
});
|
||||
document.querySelectorAll(".estado-btn").forEach(btn => {
|
||||
btn.setAttribute("aria-expanded", false);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function actualizarEstadoReserva(idReserva, nuevoEstado) {
|
||||
fetch("../php/Actualizar_Estado_Reserva.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ id_reserva: idReserva, estado: nuevoEstado })
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
console.log("Estado actualizado correctamente");
|
||||
} else {
|
||||
alert("Error al actualizar: " + data.error);
|
||||
// Recargar para mostrar el estado anterior
|
||||
cargarReservas();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Error:", err);
|
||||
alert("Error al actualizar el estado");
|
||||
cargarReservas();
|
||||
});
|
||||
}
|
||||
|
||||
// Función para iniciar la actualización automática
|
||||
function iniciarActualizacionAutomatica() {
|
||||
// Actualizar cada 5 segundos (5000 milisegundos)
|
||||
intervaloActualizacion = setInterval(() => {
|
||||
cargarReservas();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Función para detener la actualización automática (opcional, si es necesario)
|
||||
function detenerActualizacionAutomatica() {
|
||||
if (intervaloActualizacion) {
|
||||
clearInterval(intervaloActualizacion);
|
||||
intervaloActualizacion = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Listeners y modal para reprogramar
|
||||
function agregarListenersReprogramar() {
|
||||
const botones = document.querySelectorAll('.reprogramar-btn');
|
||||
botones.forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (btn.hasAttribute('disabled') || btn.classList.contains('disabled')) return;
|
||||
const fila = btn.closest('tr');
|
||||
const idReserva = fila.getAttribute('data-id-reserva');
|
||||
crearModalReprogramar(idReserva, fila);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function crearModalReprogramar(idReserva, fila) {
|
||||
// Obtener datos de la reserva
|
||||
const tipoCancha = fila.getAttribute('data-tipo-cancha'); // 'F' o 'P'
|
||||
const fechaActualReserva = fila.getAttribute('data-fecha-reserva');
|
||||
|
||||
// Crear overlay
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'reprogramar-overlay';
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'reprogramar-modal';
|
||||
|
||||
modal.innerHTML = `
|
||||
<h3 class="modal-title">Elige un día</h3>
|
||||
<div class="dias-grid"></div>
|
||||
<h3 class="modal-title">Selecciona horario</h3>
|
||||
<div class="horarios-grid"></div>
|
||||
<h3 class="modal-title">Selecciona cancha</h3>
|
||||
<div class="canchas-grid"></div>
|
||||
<div class="reprogramar-actions">
|
||||
<button class="btn-cancelar">Cancelar</button>
|
||||
<button class="btn-confirmar" disabled>Confirmar</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
overlay.appendChild(modal);
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
// Generar los próximos 7 días
|
||||
const diasGrid = modal.querySelector('.dias-grid');
|
||||
const hoy = new Date();
|
||||
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const dia = new Date(hoy);
|
||||
dia.setDate(hoy.getDate() + i);
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'dia-btn';
|
||||
const options = { weekday: 'short', day: 'numeric', month: 'numeric' };
|
||||
btn.textContent = `${dia.toLocaleDateString(undefined, { weekday: 'short' })}\n${dia.getDate()}/${dia.getMonth()+1}`;
|
||||
btn.dataset.iso = dia.toISOString().split('T')[0];
|
||||
diasGrid.appendChild(btn);
|
||||
}
|
||||
|
||||
let diaSeleccionado = null;
|
||||
let horaSeleccionada = null;
|
||||
let canchaSeleccionada = null;
|
||||
|
||||
// Handlers selección de día
|
||||
modal.querySelectorAll('.dia-btn').forEach(d => {
|
||||
d.addEventListener('click', async () => {
|
||||
modal.querySelectorAll('.dia-btn').forEach(x => x.classList.remove('selected'));
|
||||
d.classList.add('selected');
|
||||
diaSeleccionado = d.dataset.iso;
|
||||
|
||||
// Generar horarios cuando se selecciona un día
|
||||
await generarHorariosReprogramar(modal, tipoCancha, diaSeleccionado);
|
||||
|
||||
// Limpiar selección de horario y cancha
|
||||
horaSeleccionada = null;
|
||||
canchaSeleccionada = null;
|
||||
actualizarEstadoBotonConfirm(modal, diaSeleccionado, horaSeleccionada, canchaSeleccionada);
|
||||
});
|
||||
});
|
||||
|
||||
// Delegation para horarios (se generan dinámicamente)
|
||||
modal.addEventListener('click', async (e) => {
|
||||
if (e.target.classList.contains('horario-btn') && !e.target.disabled) {
|
||||
modal.querySelectorAll('.horario-btn').forEach(x => x.classList.remove('selected'));
|
||||
e.target.classList.add('selected');
|
||||
horaSeleccionada = e.target.dataset.hora;
|
||||
canchaSeleccionada = null;
|
||||
|
||||
// Generar selección de canchas
|
||||
await generarSeleccionCanchasReprogramar(modal, tipoCancha, diaSeleccionado, horaSeleccionada);
|
||||
|
||||
actualizarEstadoBotonConfirm(modal, diaSeleccionado, horaSeleccionada, canchaSeleccionada);
|
||||
}
|
||||
});
|
||||
|
||||
// Delegation para canchas
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('cancha-btn') && !e.target.disabled) {
|
||||
modal.querySelectorAll('.cancha-btn').forEach(x => x.classList.remove('selected'));
|
||||
e.target.classList.add('selected');
|
||||
canchaSeleccionada = e.target.dataset.numero;
|
||||
actualizarEstadoBotonConfirm(modal, diaSeleccionado, horaSeleccionada, canchaSeleccionada);
|
||||
}
|
||||
});
|
||||
|
||||
// Cancelar
|
||||
modal.querySelector('.btn-cancelar').addEventListener('click', () => {
|
||||
closeModalReprogramar(overlay);
|
||||
});
|
||||
|
||||
// Confirmar
|
||||
modal.querySelector('.btn-confirmar').addEventListener('click', () => {
|
||||
if (!diaSeleccionado || !horaSeleccionada || !canchaSeleccionada) return;
|
||||
const fechaHora = diaSeleccionado + ' ' + horaSeleccionada;
|
||||
reprogramarReserva(idReserva, fechaHora, overlay);
|
||||
});
|
||||
|
||||
// Cerrar al hacer click fuera del modal
|
||||
overlay.addEventListener('click', (e) => {
|
||||
if (e.target === overlay) closeModalReprogramar(overlay);
|
||||
});
|
||||
}
|
||||
|
||||
// Generar horarios para reprogramación
|
||||
async function generarHorariosReprogramar(modal, tipoCancha, fechaSeleccionada) {
|
||||
const horariosGrid = modal.querySelector('.horarios-grid');
|
||||
horariosGrid.innerHTML = '';
|
||||
|
||||
const tipoCode = tipoCancha === 'F' ? 'futbol' : 'padel';
|
||||
const datos = datosCanchas[tipoCode];
|
||||
|
||||
if (!datos || !datos.duracion || !datos.apertura || !datos.cierre) {
|
||||
horariosGrid.innerHTML = '<p>Error: No se pudieron cargar los datos de las canchas</p>';
|
||||
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 = '<p>Error: No se pudieron cargar los datos</p>';
|
||||
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 = '<p>Error al cargar canchas disponibles</p>';
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user