chore: estructura inicial del MVP
Backend (Express + MariaDB) y frontend (React + Vite, identidad Fluent/Windows) con el nucleo funcional: seguridad, organizacion, proyectos, actividades, subtareas, dependencias, dashboard, calendario, gantt, comentarios, adjuntos, notificaciones in-app y catalogos. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { LogIn } from 'lucide-react';
|
||||
import { useAuth } from '../../context/auth-context.jsx';
|
||||
import { useUi } from '../../context/ui-context.jsx';
|
||||
import { Field } from '../../components/Field.jsx';
|
||||
import { Button } from '../../components/Button.jsx';
|
||||
|
||||
export default function LoginPage() {
|
||||
const { login } = useAuth();
|
||||
const { t } = useUi();
|
||||
const navigate = useNavigate();
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
setLoading(true);
|
||||
try {
|
||||
await login(email, password);
|
||||
navigate('/', { replace: true });
|
||||
} catch {
|
||||
setError(t('login_error'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="login-shell">
|
||||
<form className="login-card" onSubmit={handleSubmit}>
|
||||
<div className="login-logo">
|
||||
<div className="nav-logo">LC</div>
|
||||
<div className="login-title">{t('login_title')}</div>
|
||||
</div>
|
||||
{error && <div className="login-error">{error}</div>}
|
||||
<Field label={t('login_email')}>
|
||||
<input className="input" type="email" value={email} onChange={(e) => setEmail(e.target.value)} required autoFocus />
|
||||
</Field>
|
||||
<Field label={t('login_password')}>
|
||||
<input className="input" type="password" value={password} onChange={(e) => setPassword(e.target.value)} required />
|
||||
</Field>
|
||||
<Button variant="accent" icon={LogIn} type="submit" disabled={loading} style={{ width: '100%', justifyContent: 'center' }}>
|
||||
{t('login_submit')}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user