175 lines
5.3 KiB
TypeScript
175 lines
5.3 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect, Suspense } from 'react';
|
||
import { useRouter, useSearchParams } from 'next/navigation';
|
||
import { verifyEmail } from '@/api/auth';
|
||
import { getErrorMessage } from '@/lib/error-utils';
|
||
|
||
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');
|
||
setMessage(getErrorMessage(err, 'Неверная или устаревшая ссылка. Запросите новое письмо с подтверждением.'));
|
||
});
|
||
|
||
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>
|
||
);
|
||
}
|