From c0a40b82f2c3e67aba0839eca291b7c59c545e05 Mon Sep 17 00:00:00 2001 From: Fernando Escobar Robles Date: Fri, 6 Jun 2025 21:04:14 -0600 Subject: [PATCH] correciones atleta --- public/js/atleta.js | 94 +++++++++++++++++++++--------------------- public/js/piscina.js | 65 +++++++++++++++++++++++++---- public/js/simulador.js | 76 ++++++++++++++++++++-------------- public/piscina.html | 54 ++++++++++++------------ public/simulador.html | 24 +++++++---- routes/routines.js | 88 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 279 insertions(+), 122 deletions(-) diff --git a/public/js/atleta.js b/public/js/atleta.js index d421c8f..2617e28 100644 --- a/public/js/atleta.js +++ b/public/js/atleta.js @@ -1,64 +1,65 @@ -document.addEventListener("DOMContentLoaded", () => { +document.addEventListener("DOMContentLoaded", async () => { const atletaId = sessionStorage.getItem("userId"); + const contenedor = document.getElementById("rutinas-list"); if (!atletaId) { - document.getElementById("rutinas-list").innerHTML = - "

No se encontró tu sesión. Inicia sesión nuevamente.

"; + contenedor.innerHTML = "

No se encontró tu sesión. Inicia sesión nuevamente.

"; return; } - fetch(`/api/rutinas/atleta/${atletaId}`) - .then(res => { - if (!res.ok) { - throw new Error("Error al obtener rutinas"); - } - return res.json(); - }) - .then(rutinas => { - const contenedor = document.getElementById("rutinas-list"); - if (rutinas.length === 0) { - contenedor.innerHTML = "

No tienes rutinas asignadas todavía.

"; - return; - } - rutinas.forEach(rutina => { - const card = document.createElement("div"); - card.className = "card my-3"; - card.innerHTML = ` -
-
${rutina.title}
-

${rutina.nombreCompetencia || "Sin descripción"}

- -
- `; - contenedor.appendChild(card); - }); - }) - .catch(err => { - console.error(err); - document.getElementById("rutinas-list").innerHTML = - "

Error al cargar tus rutinas.

"; + try { + const res = await fetch(`/api/rutinas/atleta/${atletaId}/formaciones`); + if (!res.ok) throw new Error("Error al obtener formaciones del atleta"); + + const formaciones = await res.json(); + if (formaciones.length === 0) { + contenedor.innerHTML = "

No estás asignado a ninguna formación aún.

"; + return; + } + + formaciones.forEach(f => { + const card = document.createElement("div"); + card.className = "card text-start my-3 shadow-sm"; + + card.innerHTML = ` +
+
${f.rutinaNombre || "Rutina sin nombre"}
+
Formación: ${f.nombreColoquial || "(sin nombre)"}
+

+ Duración: ${f.duracion || "?"}s
+ Notas: ${f.notasTacticas || "Sin notas"}
+ Rol: ${f.atleta.rol || "N/A"} | + ID: ${f.atleta.idPersonalizado || "N/A"} | + Figura: ${f.atleta.figura || "—"} +

+ +
+ `; + contenedor.appendChild(card); }); + } catch (err) { + console.error("❌ Error al obtener formaciones:", err); + contenedor.innerHTML = "

Error al cargar tus asignaciones.

"; + } }); -function verRutina(id) { - window.location.href = `simulador.html?routineId=${id}`; +function verSimulador(rutinaId, index) { + window.location.href = `simulador.html?routineId=${rutinaId}&formationIndex=${index}`; } function logout() { - sessionStorage.clear(); + sessionStorage.clear(); alert("Sesión cerrada"); - window.location.href = "../index.html"; + window.location.href = "../index.html"; } -// Mostrar nombre del usuario logueado en el header -window.addEventListener('DOMContentLoaded', async () => { +// Mostrar nombre de usuario +window.addEventListener("DOMContentLoaded", async () => { const userId = sessionStorage.getItem("userId"); - if (!userId) { - alert("Sesión expirada, inicia sesión de nuevo."); - window.location.href = "index.html"; - return; - } + if (!userId) return; try { const res = await fetch(`/api/users/${userId}`); @@ -66,9 +67,8 @@ window.addEventListener('DOMContentLoaded', async () => { if (user?.name) { document.getElementById("nombreUsuarioHeader").textContent = user.name; - document.getElementById("nombreUsuarioDropdown").textContent = "Coach " + user.name; } } catch (err) { - console.error("❌ Error al obtener datos del usuario:", err); + console.error("❌ Error al obtener usuario:", err); } -}); \ No newline at end of file +}); diff --git a/public/js/piscina.js b/public/js/piscina.js index 6adce4c..480e929 100644 --- a/public/js/piscina.js +++ b/public/js/piscina.js @@ -298,15 +298,32 @@ tipoPiscinaSelect.addEventListener('change', async () => { circle.on('dblclick', () => { const i = formacionActual.findIndex(a => a.idPersonalizado === atleta.idPersonalizado); - const html = ` -
-
${atleta.figura || 'Sin figura'}
- - -
- `; +const html = ` +
+
${atleta.figura || 'Sin figura'}
+ + + +
+`; + showPopover(circle.x(), circle.y(), html); }); +window.eliminarDireccion = function(index) { + const atleta = formacionActual[index]; + if (!atleta) return; + + atleta.direccion = null; + + const visual = atletasKonva[atleta.idPersonalizado]; + if (visual?.flecha) { + visual.flecha.destroy(); + visual.flecha = null; + layer.draw(); + } + + document.getElementById('popoverAtleta')?.remove(); +}; circle.on('dragend', () => { const newX = circle.x(); @@ -644,6 +661,40 @@ formaciones.forEach((f, i) => { alert('Selecciona una formación desde la línea de tiempo primero.'); } }); +const btnEliminarFormacion = document.getElementById('btnEliminarFormacion'); + +btnEliminarFormacion.addEventListener('click', async () => { + if (indexSeleccionado === null || !formaciones[indexSeleccionado]) { + return alert('Selecciona una formación primero'); + } + + const confirm = await Swal.fire({ + title: '¿Eliminar formación?', + text: 'Esta acción no se puede deshacer', + icon: 'warning', + showCancelButton: true, + confirmButtonText: 'Sí, eliminar', + cancelButtonText: 'Cancelar' + }); + + if (!confirm.isConfirmed) return; + + try { + const res = await fetch(`/api/rutinas/${rutinaId}/formations/${indexSeleccionado}`, { + method: 'DELETE' + }); + + if (res.ok) { + Swal.fire('Eliminada', 'La formación ha sido eliminada.', 'success'); + window.location.reload(); + } else { + Swal.fire('Error', 'No se pudo eliminar la formación.', 'error'); + } + } catch (err) { + console.error(err); + Swal.fire('Error', 'Ocurrió un error de red.', 'error'); + } +}); diff --git a/public/js/simulador.js b/public/js/simulador.js index e28d7ca..7ed49fe 100644 --- a/public/js/simulador.js +++ b/public/js/simulador.js @@ -1,11 +1,16 @@ document.addEventListener("DOMContentLoaded", async () => { const rutinaId = new URLSearchParams(window.location.search).get("routineId"); - if (!rutinaId) return alert("No se proporcionó ID de rutina."); + const formationIndex = parseInt(new URLSearchParams(window.location.search).get("formationIndex")); + + if (!rutinaId || isNaN(formationIndex)) { + return alert("No se proporcionó ID de rutina o índice de formación."); + } const tituloRutina = document.getElementById("tituloRutina"); const tipoRutina = document.getElementById("tipoRutina"); const modalidadRutina = document.getElementById("modalidadRutina"); const audioPlayer = document.getElementById("audioPlayer"); + const playBtn = document.getElementById("playPauseBtn"); const timeline = document.getElementById("lineaTiempo"); const rutina = await fetch(`/api/rutinas/${rutinaId}`).then(res => res.json()); @@ -14,8 +19,20 @@ document.addEventListener("DOMContentLoaded", async () => { tituloRutina.textContent = rutina.title || rutina.nombreCompetencia; tipoRutina.textContent = rutina.tipoCompetencia; modalidadRutina.textContent = modalidad; + if (rutina.musicUrl) { audioPlayer.src = rutina.musicUrl; + playBtn.onclick = () => { + if (audioPlayer.paused) { + audioPlayer.play(); + playBtn.textContent = "⏸ Pausar"; + } else { + audioPlayer.pause(); + playBtn.textContent = "▶ Reproducir"; + } + }; + } else { + playBtn.style.display = "none"; } const piscinaWidth = rutina.piscina?.width || 850; @@ -41,6 +58,7 @@ document.addEventListener("DOMContentLoaded", async () => { const divPiscina = document.getElementById("piscina"); divPiscina.style.width = `${piscinaWidth}px`; divPiscina.style.height = `${piscinaHeight}px`; + divPiscina.style.background = "#d4f0ff"; // Azul claro document.getElementById("piscinaContainer").style.width = `${piscinaWidth + 20}px`; function dibujarCuadricula(layer, ancho, alto, tamano = 45) { @@ -79,38 +97,38 @@ document.addEventListener("DOMContentLoaded", async () => { x: atleta.x - 10, y: atleta.y - 7, text: atleta.idPersonalizado, - fontSize: 12, + fontSize: 14, + fontStyle: 'bold', fill: 'white' }); const figuraText = new Konva.Text({ - x: atleta.x - 25, - y: atleta.y + 18, + x: atleta.x - 30, + y: atleta.y + 20, text: atleta.figura || '', - fontSize: 10, + fontSize: 11, fill: '#333', fontStyle: 'italic' }); const coordText = new Konva.Text({ - x: atleta.x - 35, - y: atleta.y + 30, + x: atleta.x - 38, + y: atleta.y + 34, text: `${metros.metrosX}m, ${metros.metrosY}m`, - fontSize: 9, + fontSize: 10, fill: '#666' }); - layer.add(circle); - layer.add(text); - layer.add(figuraText); - layer.add(coordText); + layer.add(circle, text, figuraText, coordText); if (atleta.direccion) { const dir = new Konva.Line({ points: [atleta.direccion.x1, atleta.direccion.y1, atleta.direccion.x2, atleta.direccion.y2], stroke: 'black', strokeWidth: 2, - dash: [4, 4] + dash: [4, 4], + pointerLength: 10, + pointerWidth: 10 }); layer.add(dir); } @@ -119,27 +137,21 @@ document.addEventListener("DOMContentLoaded", async () => { } const formaciones = await fetch(`/api/rutinas/${rutinaId}/formations`).then(res => res.json()); + const formacion = formaciones[formationIndex]; - if (Array.isArray(formaciones)) { - formaciones.forEach((f, i) => { - const block = document.createElement("button"); - block.className = "btn btn-outline-primary btn-sm me-2 step"; - block.textContent = `${f.nombreColoquial || `Formación ${i + 1}`} (${f.duracion || '?'}s)`; - block.dataset.index = i; - block.title = f.notasTacticas || ''; - timeline.appendChild(block); - }); + if (!formacion) { + alert("La formación solicitada no existe."); + return; } - timeline.addEventListener("click", (e) => { - if (!e.target.classList.contains("step")) return; + // Pintar solo esta formación + formacion.atletas.forEach(dibujarAtleta); - const index = parseInt(e.target.dataset.index); - const formacion = formaciones[index]; - if (!formacion) return; - - layer.destroyChildren(); - dibujarCuadricula(layer, stage.width(), stage.height()); - formacion.atletas.forEach(a => dibujarAtleta(a)); - }); + // Mostrar título visual + const block = document.createElement("button"); + block.className = "btn btn-outline-primary btn-sm me-2 step"; + block.textContent = `${formacion.nombreColoquial || `Formación ${formationIndex + 1}`} (${formacion.duracion || '?'}s)`; + block.dataset.index = formationIndex; + block.title = formacion.notasTacticas || ''; + timeline.appendChild(block); }); diff --git a/public/piscina.html b/public/piscina.html index 9a39aa3..8400270 100644 --- a/public/piscina.html +++ b/public/piscina.html @@ -81,39 +81,41 @@ +
+
+
Piscina
-
-
-
Piscina
+
+ + +
-
- - -
+
+
+
-
-
-
+
+ + +
-
- -
-
-
- -
-
-
+ +
- -
-
+ +
+
+
+
+
+
+
Datos de Formación
diff --git a/public/simulador.html b/public/simulador.html index 9357b4f..a17fd69 100644 --- a/public/simulador.html +++ b/public/simulador.html @@ -49,18 +49,24 @@
-

Cargando rutina...

-

|

+

Cargando rutina...

+

+ | + +

- + +
+ +
-
-
-
+
+
+
+ +
Línea de Tiempo
+
-
Línea de Tiempo
-
-
diff --git a/routes/routines.js b/routes/routines.js index a2fff8c..2ecd796 100644 --- a/routes/routines.js +++ b/routes/routines.js @@ -258,11 +258,97 @@ router.get('/atleta/:id', async (req, res) => { const rutinas = await Routine.find({ participantes: atletaId }) .populate('participantes', 'name') .populate('formaciones.atletas.atletaId', 'name'); - res.json(rutinas); + + // Filtrar solo rutinas donde realmente aparece en alguna formación + const filtradas = rutinas.filter(rutina => + rutina.formaciones.some(f => + f.atletas.some(a => a.atletaId?.toString() === atletaId) + ) + ); + + res.json(filtradas); } catch (err) { console.error(err); res.status(500).json({ mensaje: "Error al obtener rutinas del atleta" }); } }); +// DELETE eliminar una formación específica por índice +router.delete('/:id/formations/:index', async (req, res) => { + const { id, index } = req.params; + + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json({ message: 'ID de rutina inválido' }); + } + + const i = parseInt(index); + if (isNaN(i) || i < 0) { + return res.status(400).json({ message: 'Índice de formación inválido' }); + } + + try { + const rutina = await Routine.findById(id); + if (!rutina) { + return res.status(404).json({ message: 'Rutina no encontrada' }); + } + + if (i >= rutina.formaciones.length) { + return res.status(400).json({ message: 'Índice fuera de rango' }); + } + + rutina.formaciones.splice(i, 1); + await rutina.save(); + + const nuevosParticipantes = new Set(); + rutina.formaciones.forEach(f => { + f.atletas.forEach(a => { + if (a.atletaId) nuevosParticipantes.add(a.atletaId.toString()); + }); + }); +rutina.participantes = [...nuevosParticipantes]; + + res.status(200).json({ message: 'Formación eliminada exitosamente' }); + } catch (error) { + console.error('❌ Error al eliminar formación:', error); + res.status(500).json({ message: 'Error del servidor', error }); + } +}); +router.get('/atleta/:id/formaciones', async (req, res) => { + const atletaId = req.params.id; + try { + const rutinas = await Routine.find({ participantes: atletaId }); + + const resultado = []; + + for (const rutina of rutinas) { + const formacionesAtleta = rutina.formaciones + .map((f, i) => { + const match = f.atletas.find(a => a.atletaId?.toString() === atletaId); + return match + ? { + rutinaId: rutina._id, + rutinaNombre: rutina.title || rutina.nombreCompetencia, + index: i, + nombreColoquial: f.nombreColoquial, + notasTacticas: f.notasTacticas, + duracion: f.duracion, + atleta: match + } + : null; + }) + .filter(Boolean); + + if (formacionesAtleta.length > 0) { + resultado.push(...formacionesAtleta); + } + } + + res.json(resultado); + } catch (err) { + console.error(err); + res.status(500).json({ mensaje: "Error al obtener formaciones del atleta" }); + } +}); + + module.exports = router;