2a1bdf1ba9
Nueva marca configurable desde Configuracion -> Empresa (solo superadmin): nombre y logo, guardados en system_settings (mismo patron que SMTP, sin tabla nueva). Nuevo endpoint publico GET /api/company (+ /logo) para que login/registro/recuperacion de clave puedan mostrar la marca antes de iniciar sesion. Sin configurar, se ve "LinkedCorp" y el logo estatico de siempre. Probado en dev (build + Playwright: panel de Configuracion, sidebar expandido/compacto, login publico), sin desplegar a produccion por pedido explicito del usuario -- este commit sirve de punto de control en git antes de decidir el despliegue. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
62 lines
2.4 KiB
React
62 lines
2.4 KiB
React
import { useState } from 'react';
|
|
import { Link, 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 { useBranding } from '../../context/branding-context.jsx';
|
|
import { Field } from '../../components/Field.jsx';
|
|
import { Button } from '../../components/Button.jsx';
|
|
import { PasswordInput } from '../../components/PasswordInput.jsx';
|
|
import logoDark from '../../assets/logoLinkedCorpTxt.png';
|
|
import logoLight from '../../assets/logoLinkedCorpTxtBlack.png';
|
|
|
|
export default function LoginPage() {
|
|
const { login } = useAuth();
|
|
const { t, theme } = useUi();
|
|
const branding = useBranding();
|
|
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 (err) {
|
|
setError(err.message || t('login_error'));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="login-shell">
|
|
<form className="login-card" onSubmit={handleSubmit}>
|
|
<div className="login-logo">
|
|
<img src={branding.logoUrl || (theme === 'light' ? logoLight : logoDark)} alt={branding.name || t('appName')} className="login-logo-img" />
|
|
</div>
|
|
<div className="login-title">{t('login_title')}</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')}>
|
|
<PasswordInput 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>
|
|
<div className="login-links">
|
|
<Link to="/olvide-clave">{t('login_forgot_password')}</Link>
|
|
<Link to="/registro">{t('login_create_account')}</Link>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|