veza/apps/web/src/features/auth/pages/VerifyEmailPage.tsx

245 lines
7.3 KiB
TypeScript
Raw Normal View History

import { useState, useEffect, useRef } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { AuthLayout } from '../components/AuthLayout';
import { AuthButton } from '../components/AuthButton';
import { authApi } from '@/services/api/auth';
import type { ApiError } from '@/schemas/apiSchemas';
export function VerifyEmailPage() {
const [searchParams] = useSearchParams();
const navigate = useNavigate();
2025-12-13 02:34:34 +00:00
const [status, setStatus] = useState<'verifying' | 'success' | 'error'>(
'verifying',
);
const [message, setMessage] = useState(
'Vérification de votre email en cours...',
);
const [loading, setLoading] = useState(false);
const [resendLoading, setResendLoading] = useState(false);
const [resendCooldown, setResendCooldown] = useState(0);
const cooldownIntervalRef = useRef<NodeJS.Timeout | null>(null);
const [token, setToken] = useState<string | null>(null);
// Extraire le token depuis l'URL
useEffect(() => {
const tokenParam = searchParams.get('token');
if (tokenParam) {
setToken(tokenParam);
// Vérifier l'email automatiquement si le token est présent
handleVerifyEmail(tokenParam);
} else {
setStatus('error');
setMessage('Lien de vérification invalide ou manquant');
}
}, [searchParams]);
// Cleanup cooldown interval on unmount
useEffect(() => {
return () => {
if (cooldownIntervalRef.current) {
clearInterval(cooldownIntervalRef.current);
}
};
}, []);
// Gérer le cooldown pour le renvoi d'email
useEffect(() => {
if (resendCooldown > 0) {
cooldownIntervalRef.current = setInterval(() => {
setResendCooldown((prev) => {
if (prev <= 1) {
if (cooldownIntervalRef.current) {
clearInterval(cooldownIntervalRef.current);
}
return 0;
}
return prev - 1;
});
}, 1000);
} else {
if (cooldownIntervalRef.current) {
clearInterval(cooldownIntervalRef.current);
cooldownIntervalRef.current = null;
}
}
return () => {
if (cooldownIntervalRef.current) {
clearInterval(cooldownIntervalRef.current);
}
};
}, [resendCooldown]);
// Rediriger vers login après succès
useEffect(() => {
if (status === 'success') {
const timer = setTimeout(() => {
navigate('/login', { replace: true });
}, 3000);
return () => clearTimeout(timer);
}
return undefined;
}, [status, navigate]);
const handleVerifyEmail = async (emailToken: string) => {
try {
setLoading(true);
setStatus('verifying');
setMessage('Vérification de votre email en cours...');
2025-12-13 02:34:34 +00:00
await verifyEmail(emailToken);
2025-12-13 02:34:34 +00:00
setStatus('success');
setMessage('Votre email a été vérifié avec succès !');
} catch (error) {
setStatus('error');
const apiError = error as ApiError;
setMessage(apiError.message || 'La vérification a échoué');
} finally {
setLoading(false);
}
};
const handleResendVerificationEmail = async () => {
if (resendCooldown > 0 || resendLoading) {
return;
}
try {
setResendLoading(true);
2025-12-13 02:34:34 +00:00
// Récupérer l'email depuis localStorage (stocké lors de l'inscription)
const email = localStorage.getItem('pendingVerificationEmail');
if (!email) {
2025-12-13 02:34:34 +00:00
setMessage(
'Email non trouvé. Veuillez vous réinscrire ou contacter le support.',
);
return;
}
await authApi.resendVerification({ email });
2025-12-13 02:34:34 +00:00
// Définir cooldown de 60 secondes
setResendCooldown(60);
2025-12-13 02:34:34 +00:00
// Afficher message de confirmation
2025-12-13 02:34:34 +00:00
setMessage(
'Email de vérification envoyé ! Veuillez vérifier votre boîte mail.',
);
} catch (error) {
const apiError = error as ApiError;
2025-12-13 02:34:34 +00:00
setMessage(apiError.message || "Échec de l'envoi de l'email");
} finally {
setResendLoading(false);
}
};
// Afficher le statut de vérification
if (status === 'verifying') {
return (
<AuthLayout
title="Vérification de l'email"
subtitle="Vérification en cours..."
2025-12-13 02:34:34 +00:00
footerLinks={[{ label: 'Retour à la connexion', to: '/login' }]}
>
2025-12-13 02:34:34 +00:00
<div
className="text-center space-y-4"
role="status"
aria-live="polite"
aria-busy="true"
>
<div className="flex justify-center" aria-hidden="true">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-kodo-steel"></div>
</div>
<p className="text-kodo-content-dim">{message}</p>
2025-12-13 02:34:34 +00:00
<span className="sr-only">
Vérification de votre email en cours, veuillez patienter
</span>
</div>
</AuthLayout>
);
}
// Afficher le message de succès
if (status === 'success') {
return (
<AuthLayout
title="Email vérifié"
subtitle="Votre email a été vérifié avec succès"
2025-12-13 02:34:34 +00:00
footerLinks={[{ label: 'Retour à la connexion', to: '/login' }]}
>
<div className="text-center space-y-4" role="status" aria-live="polite">
2025-12-13 02:34:34 +00:00
<div
className="bg-kodo-lime/10 border border-kodo-lime text-kodo-lime px-4 py-4 rounded"
2025-12-13 02:34:34 +00:00
role="alert"
>
<p className="font-medium">Succès !</p>
<p className="text-sm mt-1">{message}</p>
<p className="text-xs mt-2 text-kodo-content-dim">
Vous allez être redirigé vers la page de connexion...
</p>
</div>
</div>
</AuthLayout>
);
}
// Afficher le message d'erreur avec options de retry/resend
return (
<AuthLayout
title="Vérification de l'email"
subtitle="Une erreur s'est produite"
2025-12-13 02:34:34 +00:00
footerLinks={[{ label: 'Retour à la connexion', to: '/login' }]}
>
<div className="text-center space-y-4">
2025-12-13 02:34:34 +00:00
<div
className="bg-kodo-red/10 border border-kodo-red text-kodo-red px-4 py-4 rounded"
role="alert"
aria-live="assertive"
>
<p className="font-medium">Erreur</p>
<p className="text-sm mt-1">{message}</p>
</div>
2025-12-13 02:34:34 +00:00
<div className="space-y-2">
{token && (
<AuthButton
onClick={() => handleVerifyEmail(token)}
loading={loading}
type="button"
>
Réessayer
</AuthButton>
)}
2025-12-13 02:34:34 +00:00
<AuthButton
onClick={handleResendVerificationEmail}
loading={resendLoading}
disabled={resendCooldown > 0}
type="button"
variant="secondary"
2025-12-13 02:34:34 +00:00
aria-label={
resendCooldown > 0
? `Renvoyer l'email de vérification dans ${resendCooldown} secondes`
: "Renvoyer l'email de vérification"
}
>
{resendCooldown > 0 ? (
<>
2025-12-13 02:34:34 +00:00
<span className="sr-only">
Renvoyer dans {resendCooldown} secondes
</span>
<span aria-hidden="true">Renvoyer dans {resendCooldown}s</span>
</>
) : (
2025-12-13 02:34:34 +00:00
"Renvoyer l'email de vérification"
)}
</AuthButton>
</div>
</div>
</AuthLayout>
);
}
export default VerifyEmailPage;