Add multi-mode support for authentication: login, register, and forgot password

Refactored login component to support three modes: login, registration, and password recovery. Introduced new UI and validation for each mode. Updated styles to accommodate mode-specific layouts.
This commit is contained in:
2025-11-20 18:37:02 -04:00
parent 2164aeee33
commit 113245c76f
2 changed files with 217 additions and 34 deletions

View File

@@ -2,21 +2,31 @@ import React, { useState } from 'react';
import '../../styles/components.css';
const Login = ({ onLogin }) => {
const [mode, setMode] = useState('login'); // 'login' | 'register' | 'forgot'
// Login state
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleSubmit = (e) => {
// Register state
const [name, setName] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
// Forgot state
const [forgotEmail, setForgotEmail] = useState('');
const [forgotSent, setForgotSent] = useState(false);
const handleLoginSubmit = (e) => {
e.preventDefault();
setError('');
// Validación muy básica
if (!email || !password) {
setError('Por favor, ingresa tu correo y contraseña.');
return;
}
// Aquí iría la llamada real a API. Por ahora, simulamos éxito.
// Simulación de éxito
try {
localStorage.setItem('auth', 'true');
onLogin?.();
@@ -25,16 +35,47 @@ const Login = ({ onLogin }) => {
}
};
return (
<div className="auth-wrapper">
<div className="card auth-card">
<div className="form-header">
<h3>
<i className="fas fa-lock"></i>
Iniciar Sesión
</h3>
</div>
<form onSubmit={handleSubmit} className="login-form" noValidate>
const handleRegisterSubmit = (e) => {
e.preventDefault();
setError('');
if (!email || !password) {
setError('Por favor, completa correo y contraseña.');
return;
}
if (password.length < 6) {
setError('La contraseña debe tener al menos 6 caracteres.');
return;
}
if (password !== confirmPassword) {
setError('Las contraseñas no coinciden.');
return;
}
// Simulación de registro + login automático
try {
localStorage.setItem('auth', 'true');
onLogin?.();
} catch (err) {
setError('Ocurrió un error al registrarte.');
}
};
const handleForgotSubmit = (e) => {
e.preventDefault();
setError('');
if (!forgotEmail) {
setError('Por favor, ingresa tu correo.');
return;
}
// Simulación de envío de correo
setForgotSent(true);
};
const renderLogin = () => (
<form onSubmit={handleLoginSubmit} className="login-form" noValidate>
<div className="form-group">
<label htmlFor="email">Correo</label>
<input
@@ -43,7 +84,7 @@ const Login = ({ onLogin }) => {
placeholder="tu@correo.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
autoFocus
autoFocus={mode === 'login'}
aria-invalid={!!error && (!email || !password)}
/>
</div>
@@ -62,7 +103,122 @@ const Login = ({ onLogin }) => {
{error && <div className="alert alert-error" role="alert">{error}</div>}
<button type="submit" className="btn btn-primary btn-block">Entrar</button>
<div className="auth-links">
<button type="button" className="link" onClick={() => { setMode('forgot'); setError(''); }}>¿Olvidaste tu contraseña?</button>
<span className="divider"></span>
<button type="button" className="link" onClick={() => { setMode('register'); setError(''); }}>Crear cuenta</button>
</div>
</form>
);
const renderRegister = () => (
<form onSubmit={handleRegisterSubmit} className="login-form" noValidate>
<div className="form-group">
<label htmlFor="name">Nombre</label>
<input
id="name"
type="text"
placeholder="Tu nombre"
value={name}
onChange={(e) => setName(e.target.value)}
autoFocus={mode === 'register'}
/>
</div>
<div className="form-group">
<label htmlFor="reg-email">Correo</label>
<input
id="reg-email"
type="email"
placeholder="tu@correo.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
aria-invalid={!!error && !email}
/>
</div>
<div className="form-group">
<label htmlFor="reg-password">Contraseña</label>
<input
id="reg-password"
type="password"
placeholder="Mínimo 6 caracteres"
value={password}
onChange={(e) => setPassword(e.target.value)}
aria-invalid={!!error && password.length < 6}
/>
</div>
<div className="form-group">
<label htmlFor="reg-password2">Confirmar contraseña</label>
<input
id="reg-password2"
type="password"
placeholder="Repite tu contraseña"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
aria-invalid={!!error && password !== confirmPassword}
/>
</div>
{error && <div className="alert alert-error" role="alert">{error}</div>}
<button type="submit" className="btn btn-primary btn-block">Crear cuenta</button>
<div className="auth-links">
<button type="button" className="link" onClick={() => { setMode('login'); setError(''); }}>Volver al login</button>
</div>
</form>
);
const renderForgot = () => (
<form onSubmit={handleForgotSubmit} className="login-form" noValidate>
{!forgotSent ? (
<>
<div className="form-group">
<label htmlFor="forgot-email">Correo</label>
<input
id="forgot-email"
type="email"
placeholder="tu@correo.com"
value={forgotEmail}
onChange={(e) => setForgotEmail(e.target.value)}
autoFocus={mode === 'forgot'}
aria-invalid={!!error && !forgotEmail}
/>
</div>
{error && <div className="alert alert-error" role="alert">{error}</div>}
<button type="submit" className="btn btn-primary btn-block">Enviar instrucciones</button>
</>
) : (
<>
<div className="alert" role="status" style={{ background: 'rgba(16,185,129,0.08)', color: '#065f46', borderLeftColor: 'var(--success)' }}>
Si existe una cuenta con ese correo, recibirás un email con instrucciones para restablecer tu contraseña.
</div>
<div className="auth-links">
<button type="button" className="link" onClick={() => { setMode('login'); setForgotSent(false); setForgotEmail(''); }}>Volver al login</button>
</div>
</>
)}
{!forgotSent && (
<div className="auth-links">
<button type="button" className="link" onClick={() => { setMode('login'); setError(''); }}>Volver al login</button>
</div>
)}
</form>
);
return (
<div className="auth-wrapper">
<div className="card auth-card">
<div className="form-header">
<h3>
<i className={mode === 'login' ? 'fas fa-lock' : mode === 'register' ? 'fas fa-user-plus' : 'fas fa-key'}></i>
{mode === 'login' && 'Iniciar Sesión'}
{mode === 'register' && 'Crear Cuenta'}
{mode === 'forgot' && 'Recuperar Contraseña'}
</h3>
</div>
{mode === 'login' && renderLogin()}
{mode === 'register' && renderRegister()}
{mode === 'forgot' && renderForgot()}
</div>
</div>
);

View File

@@ -716,3 +716,30 @@ textarea {
max-width: 100%;
}
}
/* Auth helpers: links below forms */
.auth-links {
margin-top: 12px;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
flex-wrap: wrap;
}
.auth-links .divider {
color: var(--gray);
}
.link {
background: none;
border: none;
padding: 0;
color: var(--primary);
cursor: pointer;
font-weight: 600;
}
.link:hover {
text-decoration: underline;
}