This commit is contained in:
parent
e80bfbcd77
commit
c9afa55243
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
13
server.js
13
server.js
|
@ -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}`);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue