179 lines
6.5 KiB
JavaScript
179 lines
6.5 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"
|
|
loading={isLoading}
|
|
disabled={!!success || !!currentUser}
|
|
onClick={handleSubmit}
|
|
className="w-full mt-2"
|
|
>
|
|
Se connecter
|
|
</Button>
|
|
</div>
|
|
|
|
<div className={`mt-6 text-center transition-opacity duration-200 ${currentUser ? 'opacity-50 pointer-events-none' : ''}`}>
|
|
<a
|
|
href="#"
|
|
onClick={(e) => {
|
|
e.preventDefault();
|
|
if (!currentUser) onNavigate('register');
|
|
}}
|
|
className="group flex items-center justify-center gap-2"
|
|
>
|
|
<span className="text-sm text-neutral-600 dark:text-neutral-400">Pas de compte ? </span>
|
|
<span className="text-sm text-neutral-900 group-hover:text-neutral-600 font-medium transition-colors duration-200 dark:text-white dark:group-hover:text-neutral-300">S'inscrire</span>
|
|
</a>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|