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:
@@ -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('');
|
||||
|
||||
// Register state
|
||||
const [name, setName] = useState('');
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
|
||||
// Forgot state
|
||||
const [forgotEmail, setForgotEmail] = useState('');
|
||||
const [forgotSent, setForgotSent] = useState(false);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
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,44 +35,190 @@ const Login = ({ onLogin }) => {
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="tu@correo.com"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
autoFocus={mode === 'login'}
|
||||
aria-invalid={!!error && (!email || !password)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="password">Contraseña</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="••••••••"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
aria-invalid={!!error && (!email || !password)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{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="fas fa-lock"></i>
|
||||
Iniciar Sesión
|
||||
<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>
|
||||
<form onSubmit={handleSubmit} className="login-form" noValidate>
|
||||
<div className="form-group">
|
||||
<label htmlFor="email">Correo</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="tu@correo.com"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
autoFocus
|
||||
aria-invalid={!!error && (!email || !password)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="password">Contraseña</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="••••••••"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
aria-invalid={!!error && (!email || !password)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && <div className="alert alert-error" role="alert">{error}</div>}
|
||||
|
||||
<button type="submit" className="btn btn-primary btn-block">Entrar</button>
|
||||
</form>
|
||||
{mode === 'login' && renderLogin()}
|
||||
{mode === 'register' && renderRegister()}
|
||||
{mode === 'forgot' && renderForgot()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user