uchill/front_material/app/(auth)/verify-email/page.tsx

178 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useState, useEffect, Suspense } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { verifyEmail } from '@/api/auth';
const loadMaterialComponents = async () => {
await Promise.all([
import('@material/web/button/filled-button.js'),
import('@material/web/button/text-button.js'),
]);
};
function VerifyEmailContent() {
const router = useRouter();
const searchParams = useSearchParams();
const token = searchParams.get('token');
const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading');
const [message, setMessage] = useState('');
const [componentsLoaded, setComponentsLoaded] = useState(false);
useEffect(() => {
loadMaterialComponents().then(() => setComponentsLoaded(true));
}, []);
useEffect(() => {
if (!componentsLoaded || !token) {
if (!token) {
setStatus('error');
setMessage('Отсутствует ссылка для подтверждения. Проверьте письмо или запросите новое.');
}
return;
}
let cancelled = false;
verifyEmail(token)
.then((res) => {
if (cancelled) return;
if (res.success) {
setStatus('success');
setMessage('Email успешно подтверждён. Теперь вы можете войти в аккаунт.');
} else {
setStatus('error');
setMessage(res.message || 'Не удалось подтвердить email.');
}
})
.catch((err: any) => {
if (cancelled) return;
setStatus('error');
const msg =
err.response?.data?.error?.message ||
err.response?.data?.detail ||
'Неверная или устаревшая ссылка. Запросите новое письмо с подтверждением.';
setMessage(msg);
});
return () => {
cancelled = true;
};
}, [token, componentsLoaded]);
if (!componentsLoaded) {
return (
<div style={{ width: '100%', maxWidth: '400px' }}>
<h1 style={{ fontSize: '28px', fontWeight: '600', margin: '0 0 8px 0', color: '#1a1a1a' }}>
Uchill
</h1>
<p style={{ fontSize: '14px', color: '#666', marginBottom: '28px' }}>
Подтверждение email...
</p>
<div style={{ display: 'flex', justifyContent: 'center', padding: '32px' }}>
<div
style={{
width: '40px',
height: '40px',
border: '3px solid #e0e0e0',
borderTopColor: 'var(--md-sys-color-primary, #6750a4)',
borderRadius: '50%',
animation: 'spin 0.8s linear infinite',
}}
/>
</div>
</div>
);
}
return (
<div style={{ width: '100%', maxWidth: '400px' }}>
<p style={{ fontSize: '14px', color: '#666', marginBottom: '28px' }}>
Подтверждение email
</p>
{status === 'loading' && (
<div style={{ display: 'flex', justifyContent: 'center', padding: '32px' }}>
<div
style={{
width: '40px',
height: '40px',
border: '3px solid #e0e0e0',
borderTopColor: 'var(--md-sys-color-primary, #6750a4)',
borderRadius: '50%',
animation: 'spin 0.8s linear infinite',
}}
/>
</div>
)}
{status === 'success' && (
<>
<div
style={{
padding: '16px',
marginBottom: '24px',
background: '#e8f5e9',
color: '#2e7d32',
borderRadius: '12px',
fontSize: '14px',
lineHeight: '1.5',
}}
>
{message}
</div>
<md-filled-button
onClick={() => router.push('/login')}
style={{ width: '100%', height: '48px' }}
>
Войти в аккаунт
</md-filled-button>
</>
)}
{status === 'error' && (
<>
<div
style={{
padding: '16px',
marginBottom: '24px',
background: '#ffebee',
color: '#c62828',
borderRadius: '12px',
fontSize: '14px',
lineHeight: '1.5',
}}
>
{message}
</div>
<md-filled-button
onClick={() => router.push('/login')}
style={{ width: '100%', height: '48px', marginBottom: '12px' }}
>
На страницу входа
</md-filled-button>
<div style={{ textAlign: 'center' }}>
<md-text-button onClick={() => router.push('/register')} style={{ fontSize: '14px' }}>
Зарегистрироваться
</md-text-button>
</div>
</>
)}
</div>
);
}
export default function VerifyEmailPage() {
return (
<Suspense
fallback={
<div style={{ width: '100%', maxWidth: '400px' }}>
<p style={{ fontSize: '14px', color: '#666' }}>Загрузка...</p>
</div>
}
>
<VerifyEmailContent />
</Suspense>
);
}