first commit
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState, Suspense, useRef } from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useLanguage } from '@/context/LanguageContext';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import Card from '@/components/ui/Card';
|
||||
import Button from '@/components/ui/Button';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
function MagicLinkContent() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const { locale: language } = useLanguage();
|
||||
const { loginWithMagicLink } = useAuth();
|
||||
const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading');
|
||||
const [error, setError] = useState('');
|
||||
const verificationAttempted = useRef(false);
|
||||
|
||||
const token = searchParams.get('token');
|
||||
|
||||
useEffect(() => {
|
||||
// Prevent duplicate verification attempts (React StrictMode double-invokes effects)
|
||||
if (verificationAttempted.current) return;
|
||||
|
||||
if (token) {
|
||||
verificationAttempted.current = true;
|
||||
verifyToken();
|
||||
} else {
|
||||
setStatus('error');
|
||||
setError(language === 'es' ? 'Token no encontrado' : 'Token not found');
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
const verifyToken = async () => {
|
||||
try {
|
||||
await loginWithMagicLink(token!);
|
||||
setStatus('success');
|
||||
toast.success(language === 'es' ? '¡Bienvenido!' : 'Welcome!');
|
||||
setTimeout(() => {
|
||||
router.push('/dashboard');
|
||||
}, 1500);
|
||||
} catch (err: any) {
|
||||
setStatus('error');
|
||||
setError(err.message || (language === 'es' ? 'Enlace inválido o expirado' : 'Invalid or expired link'));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="section-padding min-h-[70vh] flex items-center">
|
||||
<div className="container-page">
|
||||
<div className="max-w-md mx-auto">
|
||||
<Card className="p-8 text-center">
|
||||
{status === 'loading' && (
|
||||
<>
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-secondary-blue mx-auto mb-4"></div>
|
||||
<h2 className="text-xl font-semibold mb-2">
|
||||
{language === 'es' ? 'Verificando...' : 'Verifying...'}
|
||||
</h2>
|
||||
<p className="text-gray-600">
|
||||
{language === 'es' ? 'Por favor espera' : 'Please wait'}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{status === 'success' && (
|
||||
<>
|
||||
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg className="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold mb-2">
|
||||
{language === 'es' ? '¡Inicio de sesión exitoso!' : 'Login successful!'}
|
||||
</h2>
|
||||
<p className="text-gray-600">
|
||||
{language === 'es' ? 'Redirigiendo...' : 'Redirecting...'}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{status === 'error' && (
|
||||
<>
|
||||
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg className="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold mb-2">
|
||||
{language === 'es' ? 'Error de Verificación' : 'Verification Error'}
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-6">{error}</p>
|
||||
<Button onClick={() => router.push('/login')}>
|
||||
{language === 'es' ? 'Ir a Iniciar Sesión' : 'Go to Login'}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LoadingFallback() {
|
||||
return (
|
||||
<div className="section-padding min-h-[70vh] flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-secondary-blue"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function MagicLinkPage() {
|
||||
return (
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<MagicLinkContent />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user