258 lines
7.2 KiB
JavaScript
258 lines
7.2 KiB
JavaScript
const traducciones = {
|
|
es: {
|
|
"nav.init": "Panel Atleta",
|
|
logout: "Salir",
|
|
role: "Atleta",
|
|
"sim.title": "Cargando rutina...",
|
|
"sim.play": "▶ Reproducir",
|
|
"sim.pause": "⏸ Pausar",
|
|
"sim.timeline": "Línea de Tiempo"
|
|
},
|
|
en: {
|
|
"nav.init": "Athlete Panel",
|
|
logout: "Logout",
|
|
role: "Athlete",
|
|
"sim.title": "Loading routine...",
|
|
"sim.play": "▶ Play",
|
|
"sim.pause": "⏸ Pause",
|
|
"sim.timeline": "Timeline"
|
|
},
|
|
fr: {
|
|
"nav.init": "Panneau Athlète",
|
|
logout: "Se déconnecter",
|
|
role: "Athlète",
|
|
"sim.title": "Chargement de la routine...",
|
|
"sim.play": "▶ Lecture",
|
|
"sim.pause": "⏸ Pause",
|
|
"sim.timeline": "Ligne du Temps"
|
|
}
|
|
};
|
|
|
|
let langActual = 'es';
|
|
|
|
function t(clave) {
|
|
return traducciones[langActual]?.[clave] || clave;
|
|
}
|
|
|
|
function translateStatic() {
|
|
const mapping = {
|
|
'nav.init': '[data-i18n="nav.init"]',
|
|
'logout': '[data-i18n="logout"]',
|
|
'sim.title': '#tituloRutina',
|
|
'sim.timeline': '[data-i18n="sim.timeline"]',
|
|
'sim.play': '#playPauseBtn',
|
|
'role': '[data-i18n="role"]'
|
|
};
|
|
|
|
for (const [clave, selector] of Object.entries(mapping)) {
|
|
const el = document.querySelector(selector);
|
|
if (el) el.textContent = t(clave);
|
|
}
|
|
}
|
|
|
|
document.getElementById('langSelector')?.addEventListener('change', async (e) => {
|
|
langActual = e.target.value;
|
|
translateStatic();
|
|
|
|
const userId = sessionStorage.getItem("userId");
|
|
if (userId) {
|
|
await fetch(`/api/users/${userId}`, {
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ language: langActual })
|
|
});
|
|
}
|
|
});
|
|
|
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
const rutinaId = new URLSearchParams(window.location.search).get("routineId");
|
|
const formationIndexParam = new URLSearchParams(window.location.search).get("formationIndex");
|
|
const formationIndex = formationIndexParam !== null ? parseInt(formationIndexParam) : 0;
|
|
|
|
const userId = sessionStorage.getItem("userId");
|
|
if (userId) {
|
|
try {
|
|
const user = await fetch(`/api/users/${userId}`).then(res => res.json());
|
|
if (user?.language && traducciones[user.language]) {
|
|
langActual = user.language;
|
|
const selector = document.getElementById("langSelector");
|
|
if (selector) selector.value = langActual;
|
|
}
|
|
if (user?.name) {
|
|
document.getElementById("nombreUsuarioHeader").textContent = user.name;
|
|
}
|
|
} catch (err) {
|
|
console.error("❌ Error al obtener usuario:", err);
|
|
}
|
|
}
|
|
|
|
translateStatic();
|
|
|
|
if (!rutinaId) {
|
|
return alert("No se proporcionó ID de rutina.");
|
|
}
|
|
|
|
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());
|
|
const modalidad = rutina.modalidad;
|
|
|
|
tituloRutina.textContent = rutina.title || rutina.nombreCompetencia;
|
|
tipoRutina.textContent = rutina.tipoCompetencia;
|
|
modalidadRutina.textContent = modalidad;
|
|
|
|
if (rutina.musicUrl) {
|
|
audioPlayer.src = rutina.musicUrl;
|
|
playBtn.textContent = t("sim.play");
|
|
playBtn.onclick = () => {
|
|
if (audioPlayer.paused) {
|
|
audioPlayer.play();
|
|
playBtn.textContent = t("sim.pause");
|
|
} else {
|
|
audioPlayer.pause();
|
|
playBtn.textContent = t("sim.play");
|
|
}
|
|
};
|
|
} else {
|
|
playBtn.style.display = "none";
|
|
}
|
|
|
|
const piscinaWidth = rutina.piscina?.width || 850;
|
|
const piscinaHeight = rutina.piscina?.height || 400;
|
|
const escalaX = 25 / piscinaWidth;
|
|
const escalaY = 20 / piscinaHeight;
|
|
|
|
function convertirAMetros(pxX, pxY) {
|
|
return {
|
|
metrosX: (pxX * escalaX).toFixed(2),
|
|
metrosY: (pxY * escalaY).toFixed(2)
|
|
};
|
|
}
|
|
|
|
const stage = new Konva.Stage({
|
|
container: "piscina",
|
|
width: piscinaWidth,
|
|
height: piscinaHeight
|
|
});
|
|
const layer = new Konva.Layer();
|
|
stage.add(layer);
|
|
|
|
const divPiscina = document.getElementById("piscina");
|
|
divPiscina.style.width = `${piscinaWidth}px`;
|
|
divPiscina.style.height = `${piscinaHeight}px`;
|
|
divPiscina.style.background = "#d4f0ff";
|
|
document.getElementById("piscinaContainer").style.width = `${piscinaWidth + 20}px`;
|
|
|
|
function dibujarCuadricula(layer, ancho, alto, tamano = 45) {
|
|
for (let x = 0; x <= ancho; x += tamano) {
|
|
layer.add(new Konva.Line({
|
|
points: [x, 0, x, alto],
|
|
stroke: '#cceeff',
|
|
strokeWidth: 1
|
|
}));
|
|
}
|
|
for (let y = 0; y <= alto; y += tamano) {
|
|
layer.add(new Konva.Line({
|
|
points: [0, y, ancho, y],
|
|
stroke: '#cceeff',
|
|
strokeWidth: 1
|
|
}));
|
|
}
|
|
}
|
|
|
|
dibujarCuadricula(layer, piscinaWidth, piscinaHeight);
|
|
|
|
function dibujarAtleta(atleta) {
|
|
const color = atleta.rol === 'volador' ? 'purple' : atleta.rol === 'pilar' ? 'blue' : 'red';
|
|
const metros = convertirAMetros(atleta.x, atleta.y);
|
|
|
|
const circle = new Konva.Circle({
|
|
x: atleta.x,
|
|
y: atleta.y,
|
|
radius: 15,
|
|
fill: color,
|
|
stroke: 'white',
|
|
strokeWidth: 2
|
|
});
|
|
|
|
const text = new Konva.Text({
|
|
x: atleta.x - 10,
|
|
y: atleta.y - 7,
|
|
text: atleta.idPersonalizado,
|
|
fontSize: 14,
|
|
fontStyle: 'bold',
|
|
fill: 'white'
|
|
});
|
|
|
|
const figuraText = new Konva.Text({
|
|
x: atleta.x - 30,
|
|
y: atleta.y + 20,
|
|
text: atleta.figura || '',
|
|
fontSize: 11,
|
|
fill: '#333',
|
|
fontStyle: 'italic'
|
|
});
|
|
|
|
const coordText = new Konva.Text({
|
|
x: atleta.x - 38,
|
|
y: atleta.y + 34,
|
|
text: `${metros.metrosX}m, ${metros.metrosY}m`,
|
|
fontSize: 10,
|
|
fill: '#666'
|
|
});
|
|
|
|
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]
|
|
});
|
|
layer.add(dir);
|
|
}
|
|
|
|
layer.draw();
|
|
}
|
|
|
|
const formaciones = await fetch(`/api/rutinas/${rutinaId}/formations`).then(res => res.json());
|
|
|
|
if (!Array.isArray(formaciones) || formaciones.length === 0) {
|
|
alert("La rutina no tiene formaciones.");
|
|
return;
|
|
}
|
|
|
|
// Crear botones de la línea de tiempo
|
|
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);
|
|
});
|
|
|
|
function renderFormacion(index) {
|
|
const formacion = formaciones[index];
|
|
if (!formacion) return;
|
|
|
|
layer.destroyChildren();
|
|
dibujarCuadricula(layer, piscinaWidth, piscinaHeight);
|
|
formacion.atletas.forEach(dibujarAtleta);
|
|
}
|
|
|
|
renderFormacion(formationIndex);
|
|
|
|
timeline.addEventListener('click', (e) => {
|
|
if (!e.target.classList.contains('step')) return;
|
|
const index = parseInt(e.target.dataset.index);
|
|
renderFormacion(index);
|
|
});
|
|
});
|