2025-12-03 21:56:50 +00:00
|
|
|
import { useForm } from 'react-hook-form';
|
|
|
|
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
|
|
import { z } from 'zod';
|
2025-12-16 19:40:16 +00:00
|
|
|
import { useNavigate } from 'react-router-dom';
|
2025-12-26 08:11:41 +00:00
|
|
|
import { useAuthStore } from '../store/authStore';
|
2025-12-16 19:40:16 +00:00
|
|
|
import { formatErrorMessage } from '@/utils/apiErrorHandler';
|
|
|
|
|
import type { LoginRequest } from '@/services/api/auth';
|
|
|
|
|
import type { ApiError } from '@/types/api';
|
2026-01-07 09:32:53 +00:00
|
|
|
import { Input, Button } from '@veza/design-system';
|
|
|
|
|
import { logger } from '@/utils/logger';
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
const loginSchema = z.object({
|
|
|
|
|
email: z.string().email('Email invalide'),
|
|
|
|
|
password: z.string().min(1, 'Le mot de passe est requis'),
|
|
|
|
|
remember_me: z.boolean().optional(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
type LoginFormData = z.infer<typeof loginSchema>;
|
|
|
|
|
|
2026-01-07 09:32:53 +00:00
|
|
|
/**
|
|
|
|
|
* LoginForm with Kōdō Design System
|
|
|
|
|
* MIGRATED: Now using Kōdō Input and Button components
|
|
|
|
|
*/
|
2025-12-03 21:56:50 +00:00
|
|
|
export const LoginForm = () => {
|
2025-12-16 19:40:16 +00:00
|
|
|
const navigate = useNavigate();
|
|
|
|
|
const { login: loginStore, isLoading, error } = useAuthStore();
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
const {
|
|
|
|
|
register,
|
|
|
|
|
handleSubmit,
|
|
|
|
|
formState: { errors },
|
|
|
|
|
} = useForm<LoginFormData>({
|
|
|
|
|
resolver: zodResolver(loginSchema),
|
|
|
|
|
defaultValues: {
|
|
|
|
|
email: '',
|
|
|
|
|
password: '',
|
|
|
|
|
remember_me: false,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-16 19:40:16 +00:00
|
|
|
const onSubmit = async (data: LoginFormData) => {
|
|
|
|
|
try {
|
|
|
|
|
const loginRequest: LoginRequest = {
|
|
|
|
|
email: data.email,
|
|
|
|
|
password: data.password,
|
|
|
|
|
remember_me: data.remember_me || false,
|
|
|
|
|
};
|
|
|
|
|
await loginStore(loginRequest);
|
|
|
|
|
// Redirection après succès
|
|
|
|
|
navigate('/dashboard');
|
|
|
|
|
} catch (err) {
|
|
|
|
|
// L'erreur est déjà gérée par le store
|
2026-01-07 09:32:53 +00:00
|
|
|
logger.error('Login error', {
|
|
|
|
|
error: err instanceof Error ? err.message : String(err),
|
|
|
|
|
stack: err instanceof Error ? err.stack : undefined,
|
|
|
|
|
});
|
2025-12-16 19:40:16 +00:00
|
|
|
}
|
2025-12-03 21:56:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
2025-12-13 02:34:34 +00:00
|
|
|
<form
|
|
|
|
|
onSubmit={handleSubmit(onSubmit)}
|
2026-01-07 09:32:53 +00:00
|
|
|
className="space-y-6"
|
2025-12-13 02:34:34 +00:00
|
|
|
>
|
2026-01-07 09:32:53 +00:00
|
|
|
<h2 className="text-2xl font-heading font-bold text-center text-white">
|
|
|
|
|
Connexion
|
|
|
|
|
</h2>
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
{error && (
|
2025-12-13 02:34:34 +00:00
|
|
|
<div
|
2026-01-07 09:32:53 +00:00
|
|
|
className="bg-kodo-red/10 border border-kodo-red/50 text-kodo-red px-4 py-3 rounded-lg"
|
2025-12-13 02:34:34 +00:00
|
|
|
role="alert"
|
|
|
|
|
>
|
2026-01-07 09:32:53 +00:00
|
|
|
<span className="block sm:inline text-sm">
|
2025-12-16 19:40:16 +00:00
|
|
|
{formatErrorMessage(error as ApiError)}
|
2025-12-13 02:34:34 +00:00
|
|
|
</span>
|
2025-12-03 21:56:50 +00:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div>
|
2026-01-07 09:32:53 +00:00
|
|
|
<Input
|
|
|
|
|
label="Email"
|
2025-12-03 21:56:50 +00:00
|
|
|
type="email"
|
2026-01-07 09:32:53 +00:00
|
|
|
autoComplete="email"
|
|
|
|
|
required
|
2025-12-03 21:56:50 +00:00
|
|
|
{...register('email')}
|
2026-01-07 09:32:53 +00:00
|
|
|
placeholder="votre@email.com"
|
|
|
|
|
aria-describedby={errors.email ? 'email-error' : undefined}
|
|
|
|
|
aria-invalid={errors.email ? 'true' : 'false'}
|
2025-12-03 21:56:50 +00:00
|
|
|
/>
|
2025-12-13 02:34:34 +00:00
|
|
|
{errors.email && (
|
2026-01-07 09:32:53 +00:00
|
|
|
<p id="email-error" className="text-kodo-red text-xs mt-1" role="alert">
|
|
|
|
|
{errors.email.message}
|
|
|
|
|
</p>
|
2025-12-13 02:34:34 +00:00
|
|
|
)}
|
2025-12-03 21:56:50 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
2026-01-07 09:32:53 +00:00
|
|
|
<Input
|
|
|
|
|
label="Mot de passe"
|
2025-12-03 21:56:50 +00:00
|
|
|
type="password"
|
2026-01-07 09:32:53 +00:00
|
|
|
autoComplete="current-password"
|
|
|
|
|
required
|
2025-12-03 21:56:50 +00:00
|
|
|
{...register('password')}
|
2026-01-07 09:32:53 +00:00
|
|
|
placeholder="••••••••"
|
|
|
|
|
aria-describedby={errors.password ? 'password-error' : undefined}
|
|
|
|
|
aria-invalid={errors.password ? 'true' : 'false'}
|
2025-12-03 21:56:50 +00:00
|
|
|
/>
|
2025-12-13 02:34:34 +00:00
|
|
|
{errors.password && (
|
2026-01-07 09:32:53 +00:00
|
|
|
<p id="password-error" className="text-kodo-red text-xs mt-1" role="alert">
|
|
|
|
|
{errors.password.message}
|
|
|
|
|
</p>
|
2025-12-13 02:34:34 +00:00
|
|
|
)}
|
2025-12-03 21:56:50 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
{...register('remember_me')}
|
|
|
|
|
id="remember_me"
|
2026-01-07 09:32:53 +00:00
|
|
|
className="h-4 w-4 text-kodo-cyan focus:ring-kodo-cyan border-kodo-steel rounded bg-kodo-graphite"
|
2025-12-03 21:56:50 +00:00
|
|
|
/>
|
2025-12-13 02:34:34 +00:00
|
|
|
<label
|
|
|
|
|
htmlFor="remember_me"
|
2026-01-07 09:32:53 +00:00
|
|
|
className="ml-2 block text-sm text-kodo-secondary"
|
2025-12-13 02:34:34 +00:00
|
|
|
>
|
2025-12-03 21:56:50 +00:00
|
|
|
Se souvenir de moi
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-01-07 09:32:53 +00:00
|
|
|
<Button
|
2025-12-03 21:56:50 +00:00
|
|
|
type="submit"
|
2025-12-16 19:40:16 +00:00
|
|
|
disabled={isLoading}
|
2026-01-07 09:32:53 +00:00
|
|
|
variant="primary"
|
|
|
|
|
className="w-full"
|
2025-12-03 21:56:50 +00:00
|
|
|
>
|
2025-12-16 19:40:16 +00:00
|
|
|
{isLoading ? 'Connexion...' : 'Se connecter'}
|
2026-01-07 09:32:53 +00:00
|
|
|
</Button>
|
2025-12-03 21:56:50 +00:00
|
|
|
</form>
|
|
|
|
|
);
|
2025-12-13 02:34:34 +00:00
|
|
|
};
|