This commit is contained in:
Fernando Escobar Robles 2025-05-07 18:51:09 -06:00
parent e80bfbcd77
commit c9afa55243
7 changed files with 453 additions and 433 deletions

View File

@ -2,93 +2,94 @@
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Inicializar rutina</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Crear Rutina SwimArt</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<style>
body {
background-color: #f8f9fa;
}
.navbar {
background-color: #0d6efd;
}
.navbar a {
color: white !important;
}
.form-section {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
max-width: 800px;
margin: auto;
}
.form-title {
font-weight: bold;
margin-bottom: 1.5rem;
}
</style>
</head>
<body class="bg-light">
<div class="container py-5">
<h1 class="mb-4 text-center">Inicializar rutina</h1>
<body>
<nav class="navbar navbar-expand-lg px-4">
<a class="navbar-brand fw-bold" href="#">SwimArt</a>
<div class="ms-auto">
<a href="coach.html" class="nav-link d-inline">Inicializar Rutina</a>
<a href="equipoDisponibles.html" class="nav-link d-inline">Equipos Disponibles</a>
<a href="piscina.html" class="nav-link d-inline">Piscina</a>
</div>
</nav>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="#inicializarRutina">Inicializar Rutina</a>
</li>
<li class="nav-item">
<a class="nav-link" href="equipoDisponibles.html">Equipos Disponibles</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#otroLink2">Catalogo de formaciones</a>
</li>
</ul>
<div class="container my-5">
<div class="form-section">
<h3 class="form-title text-center">Inicializar Nueva Rutina</h3>
<form id="rutinaForm">
<div class="row g-3">
<div class="col-md-6">
<label for="title" class="form-label">Título de la rutina</label>
<input type="text" class="form-control" id="title" required />
</div>
<div class="col-md-3">
<label for="duration" class="form-label">Duración (segundos)</label>
<input type="number" class="form-control" id="duration" required />
</div>
<div class="col-md-3">
<label for="language" class="form-label">Idioma</label>
<select id="language" class="form-select" required>
<option value="es">Español</option>
<option value="en">Inglés</option>
</select>
</div>
<div class="col-md-6">
<label for="music" class="form-label">Subir música (mp3)</label>
<input type="file" class="form-control" id="music" accept=".mp3" />
</div>
<div class="col-md-6">
<label for="nombreCompetencia" class="form-label">Nombre de la competencia</label>
<input type="text" class="form-control" id="nombreCompetencia" required />
</div>
<div class="col-md-6">
<label for="tipoCompetencia" class="form-label">Tipo de competencia</label>
<select class="form-select" id="tipoCompetencia" required>
<option value="libre">Libre</option>
<option value="técnica">Técnica</option>
</select>
</div>
<div class="col-md-6">
<label for="modalidad" class="form-label">Modalidad</label>
<select class="form-select" id="modalidad" required>
<option value="solo">Solo</option>
<option value="duo">Dúo</option>
<option value="equipo">Equipo</option>
</select>
</div>
</div>
</div>
</nav>
<form id="routineForm" class="row g-3">
<div class="col-md-6">
<label for="title" class="form-label">Título de la rutina</label>
<input type="text" class="form-control" id="title" required />
</div>
<div class="col-md-3">
<label for="duration" class="form-label">Duración (segundos)</label>
<input type="number" class="form-control" id="duration" min="0" required />
</div>
<div class="col-md-3">
<label for="language" class="form-label">Idioma</label>
<select class="form-select" id="language">
<option value="es">Español</option>
<option value="en">Inglés</option>
<option value="fr">Francés</option>
</select>
</div>
<div class="col-md-6">
<label for="music" class="form-label">Subir música (mp3)</label>
<input type="file" class="form-control" id="music" accept="audio/mp3" />
</div>
<div class="col-md-6">
<label for="nombreCompetencia" class="form-label">Nombre de la competencia</label>
<input type="text" id="nombreCompetencia" class="form-control" required />
</div>
<div class="col-md-4">
<label for="tipoCompetencia" class="form-label">Tipo de competencia</label>
<select id="tipoCompetencia" class="form-select">
<option value="libre">Libre</option>
<option value="técnica">Técnica</option>
</select>
</div>
<div class="col-md-4">
<label for="modalidad" class="form-label">Modalidad</label>
<select id="modalidad" class="form-select">
<option value="solo">Solo</option>
<option value="duo">Dúo</option>
<option value="equipo">Equipo</option>
</select>
</div>
<div class="col-md-12">
<label class="form-label">Seleccionar atletas participantes:</label>
<div id="listaAtletas" class="row"></div>
</div>
<div class="col-12 text-end mt-4">
<button type="button" class="btn btn-success btn-lg" onclick="saveRoutine()">Guardar Rutina</button>
</div>
</form>
<div class="mt-4 text-center">
<button type="submit" class="btn btn-success px-4">Guardar Rutina</button>
</div>
</form>
</div>
</div>
<script src="js/coach.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -1,44 +1,171 @@
/* editorPiscina.css */
#canvasFormacion {
width: 100%;
height: 400px;
background-color: #e3f4ff;
border-radius: 12px;
box-shadow: 0 0 8px rgba(0,0,0,0.1);
position: relative;
margin-bottom: 20px;
border: 2px solid #007bff;
background-image: linear-gradient(to right, transparent 49%, #999 50%, transparent 51%);
background-size: 100% 100%;
}
.atleta-icono {
position: absolute;
width: 34px;
height: 34px;
border-radius: 50%;
font-size: 12px;
color: white;
font-weight: bold;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
user-select: none;
}
.atleta-volador { background-color: #a259ff; }
.atleta-pilar { background-color: #007bff; }
.atleta-grupoA { border: 2px solid #28a745; }
.atleta-grupoB { border: 2px solid #ffc107; }
.flecha-mirada {
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 10px solid #444;
position: absolute;
transform-origin: center center;
}
/* Reset */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', sans-serif;
background-color: #f3f4f6;
color: #333;
}
/* Navbar */
nav {
background-color: #0d6efd;
color: white;
display: flex;
justify-content: space-between;
padding: 1rem 2rem;
align-items: center;
}
nav .logo {
font-size: 1.5rem;
font-weight: bold;
}
nav .nav-links a {
margin-left: 1.2rem;
color: white;
text-decoration: none;
font-weight: 500;
}
nav .nav-links a:hover {
text-decoration: underline;
}
/* Layout */
.container {
max-width: 1000px;
margin: 2rem auto;
background-color: white;
padding: 2rem;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}
h1 {
margin-bottom: 1rem;
}
.form-row {
display: flex;
gap: 1rem;
flex-wrap: wrap;
margin: 1rem 0;
}
.form-row select,
.form-row input {
flex: 1;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 0.95rem;
}
button {
padding: 0.6rem 1.4rem;
border: none;
border-radius: 6px;
background-color: #0d6efd;
color: white;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
button:hover {
background-color: #0b5ed7;
}
#piscinaContainer {
margin: 2rem 0;
border: 2px dashed #ccc;
padding: 1rem;
background-color: #e3f2fd;
border-radius: 10px;
overflow-x: auto;
}
#piscina {
width: 100%;
height: 400px;
background-color: #b3e5fc;
}
/* Campo de texto */
textarea {
width: 100%;
padding: 0.7rem;
border-radius: 6px;
border: 1px solid #ccc;
font-size: 0.95rem;
resize: vertical;
margin-top: 0.5rem;
}
/* Simbología */
#simbologia {
margin-top: 2rem;
}
#simbologia ul {
list-style: none;
display: flex;
gap: 2rem;
margin-top: 1rem;
}
.dot {
display: inline-block;
width: 16px;
height: 16px;
border-radius: 50%;
margin-right: 8px;
}
.volador { background-color: purple; }
.pilar { background-color: blue; }
.otro { background-color: red; }
.borde {
width: 16px;
height: 16px;
border: 3px solid;
border-radius: 50%;
display: inline-block;
margin-right: 6px;
}
.borde-verde {
border-color: limegreen;
}
.borde-amarillo {
border-color: gold;
}
/* Historial */
#historial {
margin-top: 3rem;
}
#formacionesPrevias {
margin-top: 1rem;
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
#formacionesPrevias .miniatura {
width: 120px;
height: 80px;
background-color: #f0f0f0;
border: 2px solid #ccc;
border-radius: 6px;
}

View File

@ -1,82 +1,76 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Equipos Disponibles</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="css/equipoDisponible.css" rel="stylesheet">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Equipos Disponibles</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="css/equipoDisponible.css" rel="stylesheet">
<style>
.card-container {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
padding: 2rem 0;
}
.card {
width: 300px;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="coach.html">Inicializar Rutina</a>
</li>
<li class="nav-item">
<a class="nav-link" href="equipoDisponibles.html">Equipos Disponibles</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#otroLink2">Catalogo de formaciones</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-5">
<h2>Equipos Disponibles</h2>
<div class="card-container" id="routinesList"></div>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary px-4">
<a class="navbar-brand fw-bold text-white" href="#">SwimArt</a>
<div class="ms-auto">
<a class="nav-link d-inline text-white" href="coach.html">Inicializar Rutina</a>
<a class="nav-link d-inline text-white" href="equipoDisponibles.html">Equipos Disponibles</a>
<a class="nav-link d-inline text-white" href="piscina.html">Piscina</a>
</div>
</nav>
<script>
// Función para cargar las rutinas
async function loadRoutines() {
const response = await fetch('/routines');
const routines = await response.json();
const routinesList = document.getElementById('routinesList');
routinesList.innerHTML = '';
routines.forEach(routine => {
const card = document.createElement('div');
card.classList.add('card', 'text-center', 'border-primary', 'm-2');
card.innerHTML = `
<div class="card-body">
<h5 class="card-title">${routine.nombreCompetencia}</h5>
<p class="card-text"><strong>Competencia:</strong> ${routine.tipoCompetencia}</p>
<p class="card-text"><strong>Modalidad:</strong> ${routine.modalidad}</p>
<h6>Atletas:</h6>
<ul>
${
Array.isArray(routine.participantes) && routine.participantes.length > 0
? routine.participantes.map(p =>
`<li>${p.atletaId?.name || 'Atleta desconocido'} - ${p.rol}</li>`
).join('')
: '<li>No hay atletas asignados</li>'
}
</ul>
<button class="btn btn-primary mt-2" onclick="redirectToPrueba('${routine._id}')">
Ver detalles
</button>
<div class="container mt-5">
<h2 class="text-center my-4">Equipos Disponibles</h2>
<div class="card-container" id="routinesList"></div>
</div>
</div>
`;
routinesList.appendChild(card);
});
}
function redirectToPrueba(routineId) {
window.location.href = `piscina.html?routineId=${routineId}`;
}
<script>
async function loadRoutines() {
const response = await fetch('/routines');
const routines = await response.json();
const routinesList = document.getElementById('routinesList');
routinesList.innerHTML = '';
document.addEventListener('DOMContentLoaded', loadRoutines);
routines.forEach(routine => {
const card = document.createElement('div');
card.classList.add('card', 'border-primary', 'shadow');
const atletasHTML = Array.isArray(routine.participantes) && routine.participantes.length > 0
? routine.participantes.map(p =>
`<li>${p.name || 'Atleta desconocido'} - ${p.rol || ''}</li>`).join('')
: '<li>No hay atletas asignados</li>';
card.innerHTML = `
<div class="card-body text-center">
<h5 class="card-title fw-bold">${routine.nombreCompetencia}</h5>
<p><strong>Competencia:</strong> ${routine.tipoCompetencia}</p>
<p><strong>Modalidad:</strong> ${routine.modalidad}</p>
<h6 class="mt-2">Atletas:</h6>
<ul class="text-start px-4">${atletasHTML}</ul>
<button class="btn btn-primary mt-3" onclick="redirectToPiscina('${routine._id}')">
Ver detalles
</button>
</div>
`;
routinesList.appendChild(card);
});
}
function redirectToPiscina(routineId) {
window.location.href = `piscina.html?routineId=${routineId}`;
}
document.addEventListener('DOMContentLoaded', loadRoutines);
</script>
</body>
</html>

View File

@ -1,35 +1,6 @@
document.addEventListener('DOMContentLoaded', async () => {
const listaAtletas = document.getElementById('listaAtletas');
document.getElementById('rutinaForm').addEventListener('submit', async function (e) {
e.preventDefault();
try {
const res = await fetch('/users/athletes');
const atletas = await res.json();
atletas.forEach(atleta => {
const div = document.createElement('div');
div.className = 'col-md-6 mb-3';
div.innerHTML = `
<div class="form-check">
<input class="form-check-input" type="checkbox" value="${atleta._id}" id="atleta-${atleta._id}" name="participantes">
<label class="form-check-label" for="atleta-${atleta._id}">
${atleta.name || atleta.username || atleta.email}
</label>
<input type="text" class="form-control form-control-sm mt-1" placeholder="Rol (ej: volador)" name="rol-${atleta._id}">
<input type="text" class="form-control form-control-sm mt-1" placeholder="ID personalizado (ej: A, 1)" name="id-${atleta._id}">
</div>
`;
listaAtletas.appendChild(div);
});
} catch (err) {
console.error('Error al cargar atletas:', err);
listaAtletas.innerHTML = '<p class="text-danger">No se pudieron cargar los atletas.</p>';
}
});
async function saveRoutine() {
const title = document.getElementById('title').value.trim();
const duration = parseInt(document.getElementById('duration').value.trim());
const language = document.getElementById('language').value;
@ -37,28 +8,8 @@ async function saveRoutine() {
const tipoCompetencia = document.getElementById('tipoCompetencia').value;
const modalidad = document.getElementById('modalidad').value;
const checkboxes = document.querySelectorAll('input[name="participantes"]:checked');
const participantes = [];
for (const checkbox of checkboxes) {
const id = checkbox.value;
const rol = document.querySelector(`input[name="rol-${id}"]`).value.trim();
const idPersonalizado = document.querySelector(`input[name="id-${id}"]`).value.trim();
if (!rol || !idPersonalizado) {
alert("Falta el rol o el ID personalizado de uno de los atletas seleccionados.");
return;
}
participantes.push({
atletaId: id,
rol,
idPersonalizado
});
}
if (!title || isNaN(duration) || !language || !nombreCompetencia || !tipoCompetencia || !modalidad || participantes.length === 0) {
alert("Por favor completa todos los campos obligatorios.");
if (!title || isNaN(duration) || !language || !nombreCompetencia || !tipoCompetencia || !modalidad) {
alert("Completa todos los campos.");
return;
}
@ -69,10 +20,11 @@ async function saveRoutine() {
nombreCompetencia,
tipoCompetencia,
modalidad,
participantes,
createdBy: "coach-id-ejemplo", // Ajusta esto si ya manejas sesiones
participantes: [], // Los atletas se asignan visualmente en piscina.html
createdBy: "coach-id-ejemplo", // Puedes reemplazar esto luego por el ID del usuario logueado
musicUrl: "",
elements: [] // Esto se agregará después en otra vista
elements: [],
formaciones: []
};
try {
@ -82,15 +34,16 @@ async function saveRoutine() {
body: JSON.stringify(routine)
});
const result = await res.json();
const data = await res.json();
if (res.ok) {
alert(" Rutina guardada correctamente.");
window.location.reload();
alert("Rutina guardada correctamente.");
window.location.href = "equipoDisponibles.html";
} else {
alert("Error: " + result.error);
alert("Error al guardar la rutina: " + (data.message || "Error desconocido"));
}
} catch (err) {
console.error(err);
alert(" Error al guardar la rutina.");
alert("Error al guardar la rutina.");
}
}
});

View File

@ -1,143 +1,82 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Editor de Formación</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Editor de Formación SwimArt</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="css/editorPiscina.css">
<link rel="stylesheet" href="css/piscina.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/konva@9.2.0/konva.min.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="coach.html">Inicializar Rutina</a>
</li>
<li class="nav-item">
<a class="nav-link" href="equipoDisponibles.html">Equipos Disponibles</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#otroLink2">Catalogo de formaciones</a>
</li>
</ul>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary px-4">
<a class="navbar-brand fw-bold text-white" href="#">SwimArt</a>
<div class="ms-auto">
<a href="coach.html" class="nav-link d-inline text-white">Inicializar Rutina</a>
<a href="equipoDisponibles.html" class="nav-link d-inline text-white">Equipos Disponibles</a>
</div>
</nav>
<div class="container my-5">
<h2 id="tituloRutina" class="mb-3">Nombre de la Rutina</h2>
<p><strong>Tipo:</strong> <span id="tipoRutina">Cargando...</span></p>
<p><strong>Modalidad:</strong> <span id="modalidadRutina">Cargando...</span></p>
<div class="row my-4">
<div class="col-md-4">
<label for="selectAtleta" class="form-label">Seleccionar atleta:</label>
<select id="selectAtleta" class="form-select mb-2"></select>
<label for="rolAtleta" class="form-label">Rol:</label>
<input type="text" id="rolAtleta" class="form-control mb-2" placeholder="volador / pilar / otro">
<label for="idPersonalizado" class="form-label">ID personalizado:</label>
<input type="text" id="idPersonalizado" class="form-control mb-3" placeholder="Ej: A, 1, V2">
<button id="btnAgregarAtleta" class="btn btn-success w-100">Añadir atleta a la formación</button>
</div>
<div class="col-md-8">
<div id="piscinaContainer" class="border rounded shadow p-2" style="height: auto;">
<div id="piscina" style="height: 400px;"></div>
</div>
</div>
</div>
</nav>
<div class="container mt-5 pt-5">
<h2 id="routineTitle">Cargando nombre...</h2>
<p><strong>Tipo:</strong> <span id="routineType">Cargando...</span></p>
<p><strong>Modalidad:</strong> <span id="routineMode">Cargando...</span></p>
<div class="mb-3">
<label for="athleteSelect" class="form-label">Selecciona atleta:</label>
<select id="athleteSelect" class="form-select"></select>
<div class="row mb-4">
<div class="col-md-6">
<label for="nombreFormacion" class="form-label">Nombre coloquial de la formación:</label>
<input type="text" id="nombreFormacion" class="form-control" placeholder="Ej: miniportés" />
</div>
<div class="col-md-6">
<label for="notasFormacion" class="form-label">Notas tácticas:</label>
<textarea id="notasFormacion" class="form-control" rows="3" placeholder="Ej: mirar izquierda, separarse a los 3s"></textarea>
</div>
</div>
<button class="btn btn-success mb-3" onclick="enablePlacement()">Añadir atleta a la formación</button>
<canvas id="poolCanvas" width="1000" height="300"></canvas>
<button class="btn btn-primary mt-3">Guardar Formación</button>
<div class="info">
<ul id="legend" class="mt-3"></ul>
<div class="text-center mb-4">
<button id="btnGuardarFormacion" class="btn btn-primary btn-lg px-5">Guardar Formación</button>
</div>
<h5 class="mt-4">Simbología</h5>
<ul>
<li><strong>1</strong>: Axel</li>
<li><strong>axel, =</strong>: Pilar</li>
</ul>
<hr>
<div id="simbologia" class="my-4">
<h4>Simbología</h4>
<ul>
<li><span class="dot volador"></span> Volador (morado)</li>
<li><span class="dot pilar"></span> Pilar (azul)</li>
<li><span class="dot otro"></span> Otro (rojo)</li>
<li><span class="borde borde-verde"></span> Grupo A / <span class="borde borde-amarillo"></span> Grupo B</li>
<li><span>🧿</span> Dirección de mirada (se activa con <kbd>Ctrl + Clic</kbd>)</li>
</ul>
</div>
<div id="historial">
<h4>Historial de Formaciones</h4>
<div id="formacionesPrevias" class="row"></div>
</div>
</div>
<script src="js/piscina.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Detalles de la Rutina</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="coach.html">Inicializar Rutina</a>
</li>
<li class="nav-item">
<a class="nav-link" href="equipoDisponibles.html">Equipos Disponibles</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#otroLink2">Catalogo de formaciones</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-5 pt-4">
<h2 id="routineName">Cargando nombre...</h2>
<p><strong>Tipo:</strong> <span id="routineType">Cargando...</span></p>
<p><strong>Modalidad:</strong> <span id="routineModalidad">Cargando...</span></p>
<h4>Atletas:</h4>
<ul id="atletasList">
<li>Cargando lista de atletas...</li>
</ul>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const urlParams = new URLSearchParams(window.location.search);
const routineId = urlParams.get('routineId');
if (routineId) {
fetch(`/routines/${routineId}`)
.then(response => {
if (!response.ok) {
throw new Error('No se pudo cargar la rutina');
}
return response.json();
})
.then(routine => {
document.getElementById('routineName').textContent = routine.nombreCompetencia;
document.getElementById('routineType').textContent = routine.tipoCompetencia;
document.getElementById('routineModalidad').textContent = routine.modalidad;
const atletasList = document.getElementById('atletasList');
atletasList.innerHTML = '';
if (Array.isArray(routine.participantes) && routine.participantes.length > 0) {
routine.participantes.forEach(p => {
const li = document.createElement('li');
li.textContent = `${p.atletaId?.name || 'Atleta desconocido'} - ${p.rol}`;
atletasList.appendChild(li);
});
} else {
atletasList.innerHTML = '<li>No hay atletas asignados</li>';
}
})
.catch(error => {
console.error('Error:', error);
alert('Error al cargar la rutina.');
});
} else {
alert('No se proporcionó ID de rutina.');
}
});
</script>
</body>
</html>

View File

@ -3,7 +3,6 @@ const mongoose = require('mongoose');
const router = express.Router();
const User = require('./user');
// Definición del esquema de rutina
const routineSchema = new mongoose.Schema({
title: String,
createdBy: { type: String, default: "coach-id-ejemplo" },
@ -13,74 +12,86 @@ const routineSchema = new mongoose.Schema({
nombreCompetencia: String,
tipoCompetencia: { type: String, enum: ['libre', 'técnica'], default: 'libre' },
modalidad: { type: String, enum: ['solo', 'duo', 'equipo'], default: 'solo' },
participantes: [
participantes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
elements: [],
formaciones: [
{
atletaId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
rol: String,
idPersonalizado: String
}
],
elements: [
{
code: String,
startTime: Number,
duration: Number,
position: {
x: Number,
y: Number
}
nombreColoquial: String,
notasTacticas: String,
atletas: [
{
idPersonalizado: String,
x: Number,
y: Number,
rol: String,
grupo: String,
direccion: String
}
]
}
],
createdAt: { type: Date, default: Date.now }
});
// Crear el modelo de rutina
const Routine = mongoose.model('Routine', routineSchema);
// Ruta para obtener todas las rutinas
router.get('/', async (req, res) => {
try {
const routines = await Routine.find().populate('participantes.atletaId', 'name');
const routines = await Routine.find().populate('participantes', 'name');
res.json(routines);
} catch (error) {
console.error("❌ Error al obtener rutinas:", error);
res.status(500).json({ message: 'Error al obtener las rutinas', error });
res.status(500).json({ message: 'Error al obtener rutinas', error });
}
});
// Ruta para crear una nueva rutina
router.post('/', async (req, res) => {
try {
const participantesConvertidos = req.body.participantes.map(part => ({
atletaId: new mongoose.Types.ObjectId(part.atletaId),
rol: part.rol,
idPersonalizado: part.idPersonalizado
}));
const newRoutine = new Routine({
...req.body,
participantes: participantesConvertidos
});
const newRoutine = new Routine(req.body);
await newRoutine.save();
res.status(201).json({ message: 'Rutina guardada exitosamente', routine: newRoutine });
} catch (error) {
console.error("❌ Error al guardar rutina:", error);
res.status(500).json({ message: 'Error al guardar la rutina', error });
}
});
module.exports = router;
// Ruta para obtener una rutina específica por ID
router.get('/:id', async (req, res) => {
try {
const routine = await Routine.findById(req.params.id)
.populate('participantes.atletaId', 'name'); // para obtener nombres
const routine = await Routine.findById(req.params.id).populate('participantes', 'name');
if (!routine) return res.status(404).json({ error: 'Rutina no encontrada' });
res.json(routine);
} catch (error) {
console.error('❌ Error al obtener rutina por ID:', error);
res.status(500).json({ error: 'Error cargando rutina' });
res.status(500).json({ error: 'Error al cargar rutina', details: error });
}
});
router.post('/:id/formations', async (req, res) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(400).json({ error: 'ID inválido para rutina' });
}
try {
const rutina = await Routine.findById(id);
if (!rutina) return res.status(404).json({ error: 'Rutina no encontrada' });
rutina.formaciones.push(req.body);
await rutina.save();
res.status(200).json({ message: 'Formación agregada exitosamente' });
} catch (error) {
res.status(500).json({ error: 'Error al guardar formación', details: error });
}
});
router.get('/:id/formations', async (req, res) => {
try {
const rutina = await Routine.findById(req.params.id);
if (!rutina) return res.status(404).json({ error: 'Rutina no encontrada' });
res.status(200).json(rutina.formaciones);
} catch (error) {
res.status(500).json({ error: 'Error al obtener formaciones', details: error });
}
});
module.exports = router;

View File

@ -5,24 +5,19 @@ require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware para parsear JSON y formularios
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Servir archivos estáticos (HTML, CSS, JS)
app.use(express.static(path.join(__dirname, 'public')));
// Rutas
// Importa y registra rutas solo UNA vez
const authRoutes = require('./routes/auth');
const routineRoutes = require('./routes/routines');
const userRoutes = require('./routes/users');
app.use('/auth', authRoutes); // Registro y login de usuarios
app.use('/routines', routineRoutes); // Guardado de rutinas
app.use('/users', userRoutes); // Obtener atletas para el formulario
app.use('/routines', require('./routes/routines'));
app.use('/auth', authRoutes);
app.use('/routines', routineRoutes);
app.use('/users', userRoutes);
// Servidor en marcha
app.listen(PORT, () => {
console.log(`Servidor corriendo en http://localhost:${PORT}`);
});