diff --git a/public/coach.html b/public/coach.html index 964357b..e01b746 100644 --- a/public/coach.html +++ b/public/coach.html @@ -3,96 +3,95 @@ - Crear Rutina – SwimmingArt + Crear Rutina – SwimmingArt - +
-
-

Inicializar Nueva Rutina

+
+

Inicializar Nueva Rutina

- +
-
- - -
-
- - +
+ +
- +
- +
- +
- +
- +
- + + + diff --git a/public/css/navbar.css b/public/css/navbar.css new file mode 100644 index 0000000..846a47e --- /dev/null +++ b/public/css/navbar.css @@ -0,0 +1,107 @@ +body { + background-color: #f8f9fa; +} + +.navbar { + background-color: #0d6efd; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.navbar a { + color: white !important; + transition: 0.2s ease-in-out; +} + +.navbar a:hover { + text-decoration: underline; + color: #e6e6e6 !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; +} + +/* ======== USER DROPDOWN ========= */ +#userDropdown { + background-color: #0d6efd; + color: white; + border: none; + border-radius: 8px 8px 0 0; + font-weight: 500; + transition: background 0.2s; +} + +#userDropdown:hover { + background-color: #0b5ed7; +} + +/* Menú del dropdown azul continuo */ +.dropdown-menu { + margin-top: 0; + padding: 0.75rem; + border: none; + border-radius: 0 0 8px 8px; + background-color: #0d6efd; + box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1); + min-width: 200px; + color: white; +} + +/* Eliminar nombre superior */ +.dropdown-menu strong { + display: none; +} + +/* Selector de idioma estilizado */ +.dropdown-menu select, +#langSelector { + width: 100%; + padding: 0.4rem 2rem 0.4rem 0.75rem; + font-size: 0.875rem; + border-radius: 6px; + background-color: white; + color: #212529; + border: none; + appearance: none; + margin-bottom: 0.75rem; + background-image: url("data:image/svg+xml,%3Csvg fill='gray' height='16' viewBox='0 0 20 20' width='16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7 7l5 5 5-5z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 0.6rem center; + background-size: 1rem; + box-shadow: inset 0 1px 2px rgba(0,0,0,0.1); + transition: all 0.2s ease-in-out; +} + +#langSelector:hover { + background-color: #f1f1f1; +} + +#langSelector:focus { + outline: none; + box-shadow: 0 0 0 0.15rem rgba(13,110,253,.25); +} + +/* Botón de cerrar sesión rojo sólido */ +.dropdown-menu .btn-danger { + width: 100%; + color: white; + background-color: #dc3545; + border: none; + font-weight: 500; + border-radius: 6px; + transition: background 0.2s; +} + +.dropdown-menu .btn-danger:hover { + background-color: #b02a37; +} diff --git a/public/js/coach.js b/public/js/coach.js index d95de5d..ffd98de 100644 --- a/public/js/coach.js +++ b/public/js/coach.js @@ -1,15 +1,47 @@ +const duracionesPorModalidadYTipo = { + solo: { + técnica: { texto: "2:00", valor: 120 }, + libre: { texto: "2:15", valor: 135 } + }, + duo: { + técnica: { texto: "2:20", valor: 140 }, + libre: { texto: "2:45", valor: 165 } + }, + equipo: { + técnica: { texto: "2:50", valor: 170 }, + libre: { texto: "3:30", valor: 210 } + } +}; + +function actualizarDuracionTexto() { + const tipo = document.getElementById("tipoCompetencia").value; + const modalidad = document.getElementById("modalidad").value; + const input = document.getElementById("duration"); + + if (duracionesPorModalidadYTipo[modalidad] && duracionesPorModalidadYTipo[modalidad][tipo]) { + input.value = duracionesPorModalidadYTipo[modalidad][tipo].texto; + input.dataset.segundos = duracionesPorModalidadYTipo[modalidad][tipo].valor; + } else { + input.value = traducciones[langActual].form.durationPlaceholder; + input.dataset.segundos = ""; + } +} + +document.getElementById("tipoCompetencia").addEventListener("change", actualizarDuracionTexto); +document.getElementById("modalidad").addEventListener("change", actualizarDuracionTexto); + +// Envío del formulario document.getElementById('rutinaForm').addEventListener('submit', async function (e) { e.preventDefault(); const title = document.getElementById('title').value.trim(); - const duration = parseInt(document.getElementById('duration').value.trim()); - const language = document.getElementById('language').value; + const duration = parseInt(document.getElementById('duration').dataset.segundos); const nombreCompetencia = document.getElementById('nombreCompetencia').value.trim(); const tipoCompetencia = document.getElementById('tipoCompetencia').value; const modalidad = document.getElementById('modalidad').value; const musicFile = document.getElementById('music').files[0]; - if (!title || isNaN(duration) || !language || !nombreCompetencia || !tipoCompetencia || !modalidad) { + if (!title || isNaN(duration) || !nombreCompetencia || !tipoCompetencia || !modalidad) { alert("Completa todos los campos."); return; } @@ -43,7 +75,6 @@ document.getElementById('rutinaForm').addEventListener('submit', async function const routine = { title, duration, - language, nombreCompetencia, tipoCompetencia, modalidad, @@ -74,3 +105,32 @@ document.getElementById('rutinaForm').addEventListener('submit', async function alert("Error al guardar la rutina."); } }); + +function logout() { + sessionStorage.clear(); + alert("Sesión cerrada"); + window.location.href = "../index.html"; +} + +// Mostrar nombre del usuario logueado en el header +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; + } + + try { + const res = await fetch(`/api/users/${userId}`); + const user = await res.json(); + + 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); + } +}); diff --git a/public/js/traduccionCoach.js b/public/js/traduccionCoach.js new file mode 100644 index 0000000..d02c89e --- /dev/null +++ b/public/js/traduccionCoach.js @@ -0,0 +1,107 @@ +const traducciones = { + es: { + title: "Crear Rutina – SwimmingArt", + nav: { + init: "Inicializar Rutina", + equip: "Equipos Disponibles", + pool: "Piscina" + }, + form: { + title: "Inicializar Nueva Rutina", + routineTitle: "Título de la rutina", + duration: "Duración", + durationPlaceholder: "Selecciona tipo de competencia y modalidad", + music: "Subir música (mp3)", + competition: "Nombre de la competencia", + type: "Tipo de competencia", + free: "Libre", + technical: "Técnica", + mode: "Modalidad", + solo: "Solo", + duo: "Dúo", + team: "Equipo", + save: "Guardar Rutina" + } + }, + en: { + title: "Create Routine – SwimmingArt", + nav: { + init: "Initialize Routine", + equip: "Available Equipment", + pool: "Pool" + }, + form: { + title: "Initialize New Routine", + routineTitle: "Title of the routine", + duration: "Duration", + durationPlaceholder: "Select type and modality", + music: "Upload music (mp3)", + competition: "Name of the competition", + type: "Type of competition", + free: "Free", + technical: "Technical", + mode: "Mode", + solo: "Only", + duo: "Duo", + team: "Team", + save: "Save Routine" + } + }, + fr: { + title: "Créer une Routine – SwimmingArt", + nav: { + init: "Initialiser la Routine", + equip: "Équipements Disponibles", + pool: "Piscine" + }, + form: { + title: "Initialiser une Nouvelle Routine", + routineTitle: "Titre de la routine", + duration: "Durée", + durationPlaceholder: "Sélectionnez le type et la modalité", + music: "Télécharger musique (mp3)", + competition: "Nom de la compétition", + type: "Type de compétition", + free: "Libre", + technical: "Technique", + mode: "Modalité", + solo: "Solo", + duo: "Duo", + team: "Équipe", + save: "Enregistrer la Routine" + } + } +}; + +let langActual = "es"; + +function updateLanguage(lang) { + langActual = lang; + const t = traducciones[lang]; + document.querySelector("title").innerText = t.title; + + document.querySelectorAll("[data-i18n]").forEach(el => { + const keys = el.getAttribute("data-i18n").split("."); + let value = t; + for (let k of keys) value = value[k]; + if (el.tagName === "OPTION") { + el.text = value; + } else if (el.tagName === "INPUT" && el.placeholder !== undefined) { + el.placeholder = value; + } else { + el.textContent = value; + } + }); + + // actualizar el texto si la duración está vacía + const input = document.getElementById("duration"); + if (input && !input.dataset.segundos) { + input.value = traducciones[langActual].form.durationPlaceholder; + } +} + +document.getElementById("langSelector").addEventListener("change", function () { + updateLanguage(this.value); +}); + +window.onload = () => updateLanguage("es"); diff --git a/public/register.html b/public/register.html index 427dd7e..ee50f55 100644 --- a/public/register.html +++ b/public/register.html @@ -36,14 +36,6 @@ -
- - -
diff --git a/routes/auth.js b/routes/auth.js index e1509a3..33239fd 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -18,7 +18,6 @@ const userSchema = new mongoose.Schema({ email: { type: String, unique: true, required: true }, passwordHash: String, role: { type: String, enum: ['coach', 'athlete'], required: true }, - language: { type: String, enum: ['es', 'en', 'fr'], default: 'es' }, createdAt: { type: Date, default: Date.now } }); @@ -26,14 +25,14 @@ const User = mongoose.model('User', userSchema); // === Ruta: Registro de usuario === router.post('/register', async (req, res) => { - const { name, username, email, password, role, language } = req.body; + const { name, username, email, password, role } = req.body; try { const existing = await User.findOne({ email }); if (existing) return res.status(400).send('Correo ya registrado'); const passwordHash = await bcrypt.hash(password, 10); - const user = new User({ name, username, email, passwordHash, role, language }); + const user = new User({ name, username, email, passwordHash, role }); await user.save(); res.redirect('/index.html'); diff --git a/routes/routines copy.js b/routes/routines copy.js index 6a83518..9fff432 100644 --- a/routes/routines copy.js +++ b/routes/routines copy.js @@ -5,7 +5,6 @@ const router = express.Router(); const routineSchema = new mongoose.Schema({ title: String, createdBy: { type: String, default: "coach-id-ejemplo" }, - language: { type: String, enum: ['es', 'en', 'fr'], default: 'es' }, duration: Number, musicUrl: { type: String, default: "" }, nombreCompetencia: String, diff --git a/routes/routines.js b/routes/routines.js index d831f76..ae4fbf8 100644 --- a/routes/routines.js +++ b/routes/routines.js @@ -30,7 +30,6 @@ const upload = multer({ const routineSchema = new mongoose.Schema({ title: String, createdBy: { type: String, default: "coach-id-ejemplo" }, - language: { type: String, enum: ['es', 'en', 'fr'], default: 'es' }, duration: Number, musicUrl: { type: String, default: "" }, nombreCompetencia: String, diff --git a/routes/users.js b/routes/users.js index 15b3c90..327392b 100644 --- a/routes/users.js +++ b/routes/users.js @@ -1,11 +1,12 @@ const express = require('express'); const router = express.Router(); -const { MongoClient } = require('mongodb'); +const { MongoClient, ObjectId } = require('mongodb'); require('dotenv').config(); const uri = process.env.MONGO_URI; const client = new MongoClient(uri); +// Obtener todos los atletas router.get('/athletes', async (req, res) => { try { await client.connect(); @@ -22,4 +23,23 @@ router.get('/athletes', async (req, res) => { } }); +// Obtener usuario por ID +router.get('/:id', async (req, res) => { + try { + await client.connect(); + const db = client.db('swimartdb'); + const user = await db.collection('users') + .findOne({ _id: new ObjectId(req.params.id) }, { projection: { name: 1, email: 1, role: 1 } }); + + if (!user) { + return res.status(404).json({ error: 'Usuario no encontrado' }); + } + + res.json(user); + } catch (error) { + console.error('Error al obtener usuario:', error); + res.status(500).json({ error: 'Error al obtener usuario', details: error }); + } +}); + module.exports = router; diff --git a/uploads/music/1748455764105-282013196.mp3 b/uploads/music/1748455764105-282013196.mp3 new file mode 100644 index 0000000..a7f82e7 Binary files /dev/null and b/uploads/music/1748455764105-282013196.mp3 differ