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';
|
import '../../styles/components.css';
|
||||||
|
|
||||||
const Login = ({ onLogin }) => {
|
const Login = ({ onLogin }) => {
|
||||||
|
const [mode, setMode] = useState('login'); // 'login' | 'register' | 'forgot'
|
||||||
|
|
||||||
|
// Login state
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [error, setError] = 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();
|
e.preventDefault();
|
||||||
setError('');
|
setError('');
|
||||||
|
|
||||||
// Validación muy básica
|
|
||||||
if (!email || !password) {
|
if (!email || !password) {
|
||||||
setError('Por favor, ingresa tu correo y contraseña.');
|
setError('Por favor, ingresa tu correo y contraseña.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aquí iría la llamada real a API. Por ahora, simulamos éxito.
|
// Simulación de éxito
|
||||||
try {
|
try {
|
||||||
localStorage.setItem('auth', 'true');
|
localStorage.setItem('auth', 'true');
|
||||||
onLogin?.();
|
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 (
|
return (
|
||||||
<div className="auth-wrapper">
|
<div className="auth-wrapper">
|
||||||
<div className="card auth-card">
|
<div className="card auth-card">
|
||||||
<div className="form-header">
|
<div className="form-header">
|
||||||
<h3>
|
<h3>
|
||||||
<i className="fas fa-lock"></i>
|
<i className={mode === 'login' ? 'fas fa-lock' : mode === 'register' ? 'fas fa-user-plus' : 'fas fa-key'}></i>
|
||||||
Iniciar Sesión
|
{mode === 'login' && 'Iniciar Sesión'}
|
||||||
|
{mode === 'register' && 'Crear Cuenta'}
|
||||||
|
{mode === 'forgot' && 'Recuperar Contraseña'}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={handleSubmit} className="login-form" noValidate>
|
{mode === 'login' && renderLogin()}
|
||||||
<div className="form-group">
|
{mode === 'register' && renderRegister()}
|
||||||
<label htmlFor="email">Correo</label>
|
{mode === 'forgot' && renderForgot()}
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -716,3 +716,30 @@ textarea {
|
|||||||
max-width: 100%;
|
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