Files
core/src/features/auth/pages/LoginPage.client.js
T

177 lines
6.3 KiB
JavaScript

'use client';
import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { Card, Input, Button } from '@zen/core/shared/components';
export default function LoginPage({ onSubmit, onNavigate, onSetSessionCookie, redirectAfterLogin = '/', currentUser = null }) {
const [error, setError] = useState('');
const [success, setSuccess] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [formData, setFormData] = useState({ email: '', password: '' });
const [honeypot, setHoneypot] = useState('');
const [formLoadedAt, setFormLoadedAt] = useState(0);
const router = useRouter();
useEffect(() => {
setFormLoadedAt(Date.now());
}, []);
useEffect(() => {
if (currentUser) {
router.replace(redirectAfterLogin);
}
}, [currentUser, redirectAfterLogin, router]);
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !isLoading && !success) {
handleSubmit();
}
};
const handleSubmit = async () => {
setError('');
setSuccess('');
setIsLoading(true);
const submitData = new FormData();
submitData.append('email', formData.email);
submitData.append('password', formData.password);
submitData.append('_hp', honeypot);
submitData.append('_t', String(formLoadedAt));
try {
const result = await onSubmit(submitData);
if (result.success) {
const successMsg = result.message || 'Connexion réussie ! Redirection...';
setSuccess(successMsg);
setIsLoading(false);
setTimeout(async () => {
if (result.sessionToken && onSetSessionCookie) {
await onSetSessionCookie(result.sessionToken);
}
router.push(redirectAfterLogin);
}, 1500);
} else {
setError(result.error || 'Échec de la connexion');
setIsLoading(false);
}
} catch (err) {
console.error('Login error:', err);
setError('Une erreur inattendue s\'est produite');
setIsLoading(false);
}
};
return (
<Card variant="default" padding="md" spacing="none" className="w-full max-w-md">
<div className="text-center mb-6">
<h1 className="text-2xl font-bold text-neutral-900 dark:text-white mb-2">
Connexion
</h1>
<p className="text-sm text-neutral-600 dark:text-neutral-400">
Veuillez vous connecter pour continuer.
</p>
</div>
{currentUser && (
<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg dark:bg-blue-500/10 dark:border-blue-500/20">
<div className="flex items-center space-x-2">
<div className="w-3.5 h-3.5 border-2 border-blue-400/50 border-t-blue-600 rounded-full animate-spin flex-shrink-0 dark:border-blue-500/30 dark:border-t-blue-500"></div>
<span className="text-xs text-blue-700 dark:text-blue-400">Redirection...</span>
</div>
</div>
)}
{success && !currentUser && (
<div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg dark:bg-green-500/10 dark:border-green-500/20">
<div className="flex items-center space-x-2">
<div className="w-1.5 h-1.5 bg-green-500 rounded-full flex-shrink-0"></div>
<span className="text-xs text-green-700 dark:text-green-400">{success}</span>
</div>
</div>
)}
{error && !currentUser && !success && (
<div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg dark:bg-red-500/10 dark:border-red-500/20">
<div className="flex items-center space-x-2">
<div className="w-1.5 h-1.5 bg-red-500 rounded-full flex-shrink-0"></div>
<span className="text-xs text-red-700 dark:text-red-400">{error}</span>
</div>
</div>
)}
<div className={`flex flex-col gap-4 transition-opacity duration-200 ${currentUser ? 'opacity-50 pointer-events-none' : ''}`}>
<div aria-hidden="true" style={{ position: 'absolute', left: '-9999px', top: '-9999px', width: 0, height: 0, overflow: 'hidden' }}>
<label htmlFor="_hp_login">Website</label>
<input id="_hp_login" name="_hp" type="text" tabIndex={-1} autoComplete="off" value={honeypot} onChange={(e) => setHoneypot(e.target.value)} />
</div>
<Input
id="email"
name="email"
type="email"
label="E-mail"
value={formData.email}
onChange={(value) => setFormData(prev => ({ ...prev, email: value }))}
onKeyDown={handleKeyPress}
placeholder="your@email.com"
disabled={!!success || !!currentUser}
required
/>
<div className="flex flex-col">
<div className="flex items-center justify-between mb-1.5">
<span className="text-xs font-medium text-neutral-700 dark:text-white">Mot de passe</span>
<a
href="#"
onClick={(e) => {
e.preventDefault();
if (!currentUser) onNavigate('forgot');
}}
className="text-xs text-neutral-600 hover:text-neutral-900 transition-colors duration-200 dark:text-neutral-300 dark:hover:text-white"
>
Mot de passe oublié ?
</a>
</div>
<Input
id="password"
name="password"
type="password"
value={formData.password}
onChange={(value) => setFormData(prev => ({ ...prev, password: value }))}
onKeyDown={handleKeyPress}
placeholder="••••••••"
disabled={!!success || !!currentUser}
/>
</div>
<Button
type="button"
variant="primary"
size="lg"
loading={isLoading}
disabled={!!success || !!currentUser}
onClick={handleSubmit}
className="w-full mt-2"
>
Se connecter
</Button>
</div>
<div className={`mt-6 flex justify-center transition-opacity duration-200 ${currentUser ? 'opacity-50 pointer-events-none' : ''}`}>
<Button
type="button"
variant="ghost"
disabled={!!currentUser}
onClick={() => { if (!currentUser) onNavigate('register'); }}
>
Pas de compte ? S'inscrire
</Button>
</div>
</Card>
);
}