Cambios en login y registro

This commit is contained in:
JorgeLuisOZ 2025-06-04 00:50:39 -06:00
parent 96f7b057b0
commit 4dce58a00f
8 changed files with 389 additions and 181 deletions

96
public/css/index.css Normal file
View File

@ -0,0 +1,96 @@
/* Fondo izquierdo */
.bg-image {
background-image: url('../img/swimming.png'); /* ajusta la ruta */
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
/* Suavidad en la aparición sin salto */
.animate-fade-in {
opacity: 0;
animation: appear 0.6s ease-out forwards;
}
@keyframes appear {
to {
opacity: 1;
}
}
/* Títulos con azul más claro */
.text-primary-strong {
color: #49a7ff;
font-weight: 700;
}
/* Subtexto de sesión */
.subtext-muted {
font-size: 1rem;
color: #6c757d;
margin-top: 10px; /* más cerca del subtítulo */
margin-bottom: 20px; /* menos separación del formulario */
}
/* Formulario */
.form-control {
border-radius: 999px;
background-color: #e3f2fd;
border: none;
padding: 12px 20px;
font-size: 0.95rem;
}
.form-control:focus {
background-color: #d0e9fb;
box-shadow: none;
}
/* Botón */
.btn-primary {
background-color: #007BFF;
border: none;
border-radius: 999px;
padding: 10px;
font-weight: 600;
font-size: 1rem;
}
.btn-success {
border-radius: 999px;
padding: 10px;
font-weight: 600;
font-size: 1rem;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Estilos ya definidos */
#languageSelect {
border-radius: 999px;
background-color: #ffffff;
padding: 6px 12px;
font-size: 1rem; /* Aumentado */
min-width: 100px;
padding-right: 24px;
}
/* Fondo verde solo para inputs del formulario de registro */
.green-input {
background-color: #e0f6e9; /* verde claro */
border: none;
border-radius: 999px;
padding: 12px 20px;
font-size: 0.95rem;
}
.green-input:focus {
background-color: #c4ecd7;
box-shadow: none;
}

View File

@ -1,68 +0,0 @@
/* Fondo general */
body {
background: linear-gradient(to bottom right, #e0f7fa, #f1f8ff);
font-family: 'Segoe UI', sans-serif;
}
/* Tarjeta del login */
.card {
border-radius: 20px;
border: none;
background-color: #ffffffee;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}
/* Título principal */
.card h3 {
font-weight: bold;
color: #0077b6;
}
/* Input y botones */
.form-control {
border-radius: 12px;
border: 1px solid #b0bec5;
}
.form-control:focus {
border-color: #00bcd4;
box-shadow: 0 0 0 0.15rem rgba(0, 188, 212, 0.25);
}
.btn-primary {
background-color: #0077b6;
border: none;
border-radius: 12px;
transition: background-color 0.3s ease;
}
.btn-primary:hover {
background-color: #005f8c;
}
/* Idioma + Enlace de registro */
.text-center small {
font-size: 0.85rem;
color: #37474f;
}
a {
color: #0077b6;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Select de idioma */
#languageSelect {
border-radius: 999px;
background-color: #ffffff;
padding: 2px 10px;
font-size: 0.85rem;
min-width: 90px; /* 👈 da espacio para el texto */
padding-right: 24px; /* 👈 da espacio para la flechita */
background-clip: padding-box; /* asegura que no se solape */
}

BIN
public/img/swimming.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -2,46 +2,64 @@
<html lang="es"> <html lang="es">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Iniciar Sesión - SwimArt</title> <title>Bienvenido - SwimmingArt</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS --> <!-- Bootstrap + Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons --> <!-- Bootstrap Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
<link rel="stylesheet" href="css/login.css"> <link rel="stylesheet" href="css/index.css">
</head> </head>
<body class="bg-light"> <body>
<div class="container-fluid p-0">
<div class="row vh-100 w-100 m-0">
<div class="container d-flex justify-content-center align-items-center" style="min-height: 100vh;"> <!-- Lado izquierdo con imagen -->
<div class="card shadow p-4" style="width: 100%; max-width: 400px;"> <div class="col-md-6 p-0 bg-image"></div>
<h3 class="text-center mb-4">Iniciar Sesión</h3>
<form id="loginForm"> <!-- Lado derecho de inicio de sesion -->
<div id="rightPanel" class="col-md-6 p-0 d-flex flex-column justify-content-center align-items-center px-3 animate-fade-in">
<div class="text-center mb-4">
<h1 class="fw-bold mb-3 text-primary-strong display-3">Bienvenidos a SwimmingArt</h1>
<h3 class="fw mb-5 text-secondary-strong">La herramienta para tus rutinas de nado sincronizado</h3>
</div>
<!-- Formulario más ancho -->
<form id="loginForm" class="w-100" style="max-width: 480px;">
<p class="subtext-muted mb-3 text-center">Inicia sesión con tu cuenta para continuar</p>
<div class="mb-3"> <div class="mb-3">
<label for="email" class="form-label">Correo electrónico</label> <input type="email" class="form-control" placeholder="Correo electrónico" required>
<input type="email" class="form-control" id="email" name="email" placeholder="Ingresar correo" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="password" class="form-label">Contraseña</label> <input type="password" class="form-control" placeholder="Contraseña" required>
<input type="password" class="form-control" id="password" name="password" placeholder="Ingresar contraseña" required> </div>
<div class="d-flex justify-content-end mb-3">
<a href="#" class="small text-primary">¿Olvidaste tu contraseña?</a>
</div> </div>
<button type="submit" class="btn btn-primary w-100">Ingresar</button> <button type="submit" class="btn btn-primary w-100">Ingresar</button>
</form> </form>
<div class="text-center mt-3 d-flex justify-content-between align-items-center">
<small>¿No tienes cuenta? <a href="register.html">Regístrate aquí</a></small> <!-- Registro con fuente más grande -->
<div class="d-flex align-items-center ms-4"> <!-- ms-4 para separar del texto --> <p class="mt-4 fs-6">¿No tienes cuenta? <a href="register.html" class="text-primary">Regístrate</a></p>
<i class="bi bi-globe me-0"></i> <!-- me-0 para pegar el ícono al select -->
<select class="form-select form-select-sm border-0 shadow-none" id="languageSelect" style="width: auto;"> <!-- Selector de idioma con fuente más grande -->
<div class="mt-3 d-flex align-items-center fs-6">
<i class="bi bi-globe me-2"></i>
<select class="form-select form-select-sm border-0 shadow-none" id="languageSelect" style="width: auto; font-size: 1rem;">
<option value="es">Español</option> <option value="es">Español</option>
<option value="en">English</option> <option value="en">English</option>
<option value="fr">Français</option> <option value="fr">Français</option>
</select> </select>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- Bootstrap JS (opcional) --> <!-- JS Bootstrap -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="js/login.js"></script> <script src="js/index.js"></script>
</body> </body>
</html> </html>

248
public/js/index.js Normal file
View File

@ -0,0 +1,248 @@
const translations = {
es: {
title: "Bienvenidos a SwimmingArt",
subtitle: "La herramienta para tus rutinas de nado sincronizado",
login_prompt: "Inicia sesión con tu cuenta para continuar",
email: "Correo electrónico",
password: "Contraseña",
forgot_password: "¿Olvidaste tu contraseña?",
login_button: "Ingresar",
no_account: "¿No tienes cuenta?",
register: "Regístrate",
create_account: "Crear cuenta de SwimmingArt",
name: "Nombre completo",
username: "Nombre de usuario",
register_email: "Correo electrónico",
register_password: "Contraseña",
role: "Rol",
language: "Idioma",
register_button: "Registrarse",
has_account: "¿Ya tienes cuenta?",
login: "Inicia sesión"
},
en: {
title: "Welcome to SwimmingArt",
subtitle: "The tool for your synchronized swimming routines",
login_prompt: "Log in with your account to continue",
email: "Email",
password: "Password",
forgot_password: "Forgot your password?",
login_button: "Log in",
no_account: "Don't have an account?",
register: "Sign up",
create_account: "Create account in SwimmingArt",
name: "Full name",
username: "Username",
register_email: "Email",
register_password: "Password",
role: "Role",
language: "Language",
register_button: "Sign up",
has_account: "Already have an account?",
login: "Log in"
},
fr: {
title: "Bienvenue sur SwimmingArt",
subtitle: "Loutil pour vos routines de natation synchronisée",
login_prompt: "Connectez-vous avec votre compte pour continuer",
email: "Courriel",
password: "Mot de passe",
forgot_password: "Mot de passe oublié ?",
login_button: "Connexion",
no_account: "Vous navez pas de compte ?",
register: "Sinscrire",
create_account: "Créer un compte sur SwimmingArt",
name: "Nom complet",
username: "Nom d'utilisateur",
register_email: "Courriel",
register_password: "Mot de passe",
role: "Rôle",
language: "Langue",
register_button: "Sinscrire",
has_account: "Vous avez déjà un compte ?",
login: "Connexion"
}
};
function getLoginHTML(lang) {
const t = translations[lang];
return `
<div class="text-center mb-4">
<h1 class="fw-bold mb-3 text-primary-strong display-3">${t.title}</h1>
<h3 class="fw mb-5 text-secondary-strong">${t.subtitle}</h3>
</div>
<form id="loginForm" class="w-100" style="max-width: 480px;">
<p class="subtext-muted mb-3 text-center">${t.login_prompt}</p>
<div class="mb-3">
<input type="email" class="form-control" placeholder="${t.email}" required>
</div>
<div class="mb-3">
<input type="password" class="form-control" placeholder="${t.password}" required>
</div>
<div class="d-flex justify-content-end mb-3">
<a href="#" class="small text-primary">${t.forgot_password}</a>
</div>
<button type="submit" class="btn btn-primary w-100">${t.login_button}</button>
</form>
<p class="mt-4">${t.no_account} <a href="#" class="text-primary" onclick="showRegister()">${t.register}</a></p>
<div class="mt-3 d-flex align-items-center fs-6">
<i class="bi bi-globe me-2"></i>
<select class="form-select form-select-sm border-0 shadow-none" id="languageSelect" style="width: auto; font-size: 1rem;" onchange="changeLanguage(this.value)">
<option value="es">Español</option>
<option value="en">English</option>
<option value="fr">Français</option>
</select>
</div>
`;
}
function getRegisterHTML(lang) {
const t = translations[lang];
return `
<div class="text-center mb-4">
<h2 class="fw-bold mb-3 text-success">${t.create_account}</h2>
</div>
<form id="registerForm" class="w-100" style="max-width: 500px;">
<div class="mb-3">
<label class="form-label">${t.name}</label>
<input type="text" name="name" class="form-control green-input" placeholder="${t.name}" required>
</div>
<div class="mb-3">
<label class="form-label">${t.username}</label>
<input type="text" name="username" class="form-control green-input" placeholder="${t.username}" required>
</div>
<div class="mb-3">
<label class="form-label">${t.register_email}</label>
<input type="email" name="email" class="form-control green-input" placeholder="${t.register_email}" required>
</div>
<div class="mb-3">
<label class="form-label">${t.register_password}</label>
<input type="password" name="password" class="form-control green-input" placeholder="${t.register_password}" required>
</div>
<div class="mb-3">
<label class="form-label">${t.role}</label>
<select name="role" class="form-select" required>
<option value="coach">Coach</option>
<option value="athlete">Atleta</option>
</select>
</div>
<div class="mb-4">
<label class="form-label">${t.language}</label>
<select name="language" class="form-select" required>
<option value="es">Español</option>
<option value="en">Inglés</option>
<option value="fr">Francés</option>
</select>
</div>
<button type="submit" class="btn btn-success w-100">${t.register_button}</button>
</form>
<p class="mt-4 text-center">${t.has_account} <a href="#" onclick="showLogin()">${t.login}</a></p>
<div class="mt-3 d-flex align-items-center fs-6">
<i class="bi bi-globe me-2"></i>
<select class="form-select form-select-sm border-0 shadow-none" id="languageSelect" style="width: auto; font-size: 1rem;" onchange="changeLanguage(this.value)">
<option value="es">Español</option>
<option value="en">English</option>
<option value="fr">Français</option>
</select>
</div>
`;
}
let currentLang = 'es';
function showLogin() {
document.getElementById('rightPanel').innerHTML = getLoginHTML(currentLang);
document.getElementById('languageSelect').value = currentLang;
setupLoginForm();
}
function showRegister() {
document.getElementById('rightPanel').innerHTML = getRegisterHTML(currentLang);
document.getElementById('languageSelect').value = currentLang;
setupRegisterForm();
}
function changeLanguage(lang) {
currentLang = lang;
const isLogin = document.getElementById('loginForm') !== null;
if (isLogin) {
showLogin();
} else {
showRegister();
}
}
function setupLoginForm() {
const form = document.getElementById('loginForm');
if (!form) return;
form.addEventListener('submit', async (e) => {
e.preventDefault();
const email = form.querySelector('input[type="email"]').value;
const password = form.querySelector('input[type="password"]').value;
try {
const res = await fetch('/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (!res.ok) {
alert("Credenciales incorrectas.");
return;
}
const data = await res.json();
sessionStorage.setItem("userId", data.userId);
sessionStorage.setItem("role", data.role);
if (data.role === "coach") {
window.location.href = "equipoDisponibles.html";
} else if (data.role === "athlete") {
window.location.href = "atleta.html";
} else {
window.location.href = "ventanaPrincipal.html";
}
} catch (error) {
console.error("Error al iniciar sesión:", error);
alert("Error de red al intentar iniciar sesión.");
}
});
}
function setupRegisterForm() {
const form = document.getElementById('registerForm');
if (!form) return;
form.addEventListener('submit', async (e) => {
e.preventDefault();
const data = Object.fromEntries(new FormData(form));
try {
const response = await fetch('/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (response.ok) {
//alert('Registro exitoso. Ahora inicia sesión.');
showLogin();
} else {
const errorText = await response.text();
alert(`Error: ${errorText}`);
}
} catch (err) {
console.error('Error de red:', err);
alert('Ocurrió un error. Intenta más tarde.');
}
});
}
window.onload = () => {
showLogin();
};

View File

@ -1,30 +0,0 @@
document.getElementById('loginForm').addEventListener('submit', async function (e) {
e.preventDefault();
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
const res = await fetch('/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (!res.ok) {
alert("Credenciales incorrectas.");
return;
}
const data = await res.json();
sessionStorage.setItem("userId", data.userId);
sessionStorage.setItem("role", data.role);
if (data.role === "coach") {
window.location.href = "equipoDisponibles.html";
} else if (data.role === "athlete") {
window.location.href = "atleta.html";
} else {
window.location.href = "ventanaPrincipal.html";
}
});

View File

@ -1,58 +0,0 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Registro - SwimArt</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container d-flex justify-content-center align-items-center" style="min-height: 100vh;">
<div class="card shadow p-4" style="width: 100%; max-width: 500px;">
<h3 class="text-center mb-4">Crear cuenta</h3>
<form action="/auth/register" method="POST">
<div class="mb-3">
<label for="name" class="form-label">Nombre completo</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Ingresar nombre" required>
</div>
<div class="mb-3">
<label for="username" class="form-label">Nombre de usuario</label>
<input type="text" class="form-control" id="username" name="username" placeholder="Ingresar usuario" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Correo electrónico</label>
<input type="email" class="form-control" id="email" name="email" placeholder="Ingresar correo" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Contraseña</label>
<input type="password" class="form-control" id="password" name="password" placeholder="Ingresar contraseña" required>
</div>
<div class="mb-3">
<label for="role" class="form-label">Rol</label>
<select class="form-select" id="role" name="role" required>
<option value="coach">Coach</option>
<option value="athlete">Atleta</option>
</select>
</div>
<div class="mb-3">
<label for="language" class="form-label">Idioma</label>
<select class="form-select" id="language" name="language" required>
<option value="es">Español</option>
<option value="en">Inglés</option>
<option value="fr">Francés</option>
</select>
</div>
<button type="submit" class="btn btn-success w-100">Registrarse</button>
</form>
<div class="text-center mt-3">
<small>¿Ya tienes cuenta? <a href="index.html">Inicia sesión</a></small>
</div>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -36,7 +36,9 @@ router.post('/register', async (req, res) => {
const user = new User({ name, username, email, passwordHash, role, language }); const user = new User({ name, username, email, passwordHash, role, language });
await user.save(); await user.save();
res.redirect('/index.html'); //res.redirect('/index.html');
res.status(200).send('Usuario registrado correctamente');
} catch (error) { } catch (error) {
console.error('Error en registro:', error); console.error('Error en registro:', error);
res.status(500).send('Error interno del servidor'); res.status(500).send('Error interno del servidor');