ADD new frontend

This commit is contained in:
Urtzi Alfaro
2025-08-28 10:41:04 +02:00
parent 9c247a5f99
commit 0fd273cfce
492 changed files with 114979 additions and 1632 deletions

View File

@@ -0,0 +1,201 @@
import React, { useState, useEffect } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import { useAuthActions, useAuthError, useAuthLoading, useIsAuthenticated } from '../../stores';
import { Button, Input, Card } from '../../components/ui';
import { PublicLayout } from '../../components/layout';
const LoginPage: React.FC = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [rememberMe, setRememberMe] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const { login } = useAuthActions();
const error = useAuthError();
const loading = useAuthLoading();
const isAuthenticated = useIsAuthenticated();
const from = (location.state as any)?.from?.pathname || '/app';
useEffect(() => {
if (isAuthenticated && !loading) {
// Add a small delay to ensure the auth state has fully settled
setTimeout(() => {
navigate(from, { replace: true });
}, 100);
}
}, [isAuthenticated, loading, navigate, from]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!email || !password) return;
try {
await login(email, password);
} catch (err) {
// Error is handled by the store
}
};
return (
<PublicLayout
variant="centered"
maxWidth="md"
headerProps={{
showThemeToggle: true,
showAuthButtons: false,
variant: "minimal"
}}
>
<div className="w-full max-w-md mx-auto space-y-8">
<div>
<div className="flex justify-center">
<div className="w-12 h-12 bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] rounded-lg flex items-center justify-center text-white font-bold text-lg">
PI
</div>
</div>
<h2 className="mt-6 text-center text-3xl font-extrabold text-[var(--text-primary)]">
Inicia sesión en tu cuenta
</h2>
<p className="mt-2 text-center text-sm text-[var(--text-secondary)]">
O{' '}
<Link
to="/register"
className="font-medium text-[var(--color-primary)] hover:text-[var(--color-primary-light)]"
>
regístrate para comenzar tu prueba gratuita
</Link>
</p>
</div>
<Card className="p-8">
<form className="space-y-6" onSubmit={handleSubmit}>
{error && (
<div className="bg-[var(--color-error)]/10 border border-[var(--color-error)]/20 rounded-md p-4">
<div className="flex">
<div className="flex-shrink-0">
<svg className="h-5 w-5 text-[var(--color-error)]" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-[var(--color-error)]">
Error de autenticación
</h3>
<div className="mt-2 text-sm text-[var(--color-error)]">
{error}
</div>
</div>
</div>
</div>
)}
<div>
<label htmlFor="email" className="sr-only">
Correo electrónico
</label>
<Input
id="email"
name="email"
type="email"
autoComplete="email"
required
placeholder="Correo electrónico"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<label htmlFor="password" className="sr-only">
Contraseña
</label>
<Input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
placeholder="Contraseña"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center">
<input
id="remember-me"
name="remember-me"
type="checkbox"
className="h-4 w-4 text-[var(--color-primary)] focus:ring-[var(--color-primary)] border-[var(--border-primary)] rounded"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
/>
<label htmlFor="remember-me" className="ml-2 block text-sm text-[var(--text-primary)]">
Recordarme
</label>
</div>
<div className="text-sm">
<a href="#" className="font-medium text-[var(--color-primary)] hover:text-[var(--color-primary-light)]">
¿Olvidaste tu contraseña?
</a>
</div>
</div>
<div>
<Button
type="submit"
className="w-full flex justify-center"
disabled={loading}
>
{loading ? 'Iniciando sesión...' : 'Iniciar sesión'}
</Button>
</div>
<div className="mt-6">
<div className="relative">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-[var(--border-primary)]" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-[var(--bg-primary)] text-[var(--text-tertiary)]">Demo</span>
</div>
</div>
<div className="mt-6">
<Button
type="button"
variant="outline"
className="w-full"
onClick={() => {
// TODO: Handle demo login
console.log('Demo login');
}}
>
Usar cuenta de demo
</Button>
</div>
</div>
</form>
<div className="mt-6 text-center text-xs text-[var(--text-tertiary)]">
Al iniciar sesión, aceptas nuestros{' '}
<a href="#" className="text-[var(--color-primary)] hover:text-[var(--color-primary-light)]">
Términos de Servicio
</a>
{' '}y{' '}
<a href="#" className="text-[var(--color-primary)] hover:text-[var(--color-primary-light)]">
Política de Privacidad
</a>
</div>
</Card>
</div>
</PublicLayout>
);
};
export default LoginPage;