177 lines
4.8 KiB
TypeScript
177 lines
4.8 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect } from 'react';
|
||
import { useRouter } from 'next/navigation';
|
||
import Link from 'next/link';
|
||
import { login } from '@/api/auth';
|
||
import { useAuth } from '@/contexts/AuthContext';
|
||
import { getErrorMessage } from '@/lib/error-utils';
|
||
|
||
const loadMaterialComponents = async () => {
|
||
await Promise.all([
|
||
import('@material/web/textfield/filled-text-field.js'),
|
||
import('@material/web/button/filled-button.js'),
|
||
import('@material/web/button/text-button.js'),
|
||
]);
|
||
};
|
||
|
||
export default function LoginPage() {
|
||
const router = useRouter();
|
||
const { login: authLogin } = useAuth();
|
||
const [email, setEmail] = useState('');
|
||
const [password, setPassword] = useState('');
|
||
const [loading, setLoading] = useState(false);
|
||
const [error, setError] = useState('');
|
||
const [componentsLoaded, setComponentsLoaded] = useState(false);
|
||
|
||
useEffect(() => {
|
||
loadMaterialComponents()
|
||
.then(() => setComponentsLoaded(true))
|
||
.catch((err) => {
|
||
console.error('Error loading Material components:', err);
|
||
setComponentsLoaded(true);
|
||
});
|
||
}, []);
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
setLoading(true);
|
||
setError('');
|
||
|
||
try {
|
||
const response = await login({ email, password });
|
||
if (response.access) {
|
||
localStorage.setItem('access_token', response.access);
|
||
if (response.refresh) {
|
||
localStorage.setItem('refresh_token', response.refresh);
|
||
}
|
||
if (response.user) {
|
||
authLogin(response.access, response.user).catch(console.error);
|
||
} else {
|
||
authLogin(response.access).catch(console.error);
|
||
}
|
||
window.location.href = '/dashboard';
|
||
return;
|
||
} else {
|
||
setError('Ошибка: токен не получен');
|
||
setLoading(false);
|
||
return;
|
||
}
|
||
} catch (err: any) {
|
||
setError(getErrorMessage(err, 'Ошибка входа. Проверьте данные.'));
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
if (!componentsLoaded) {
|
||
return (
|
||
<div style={{ display: 'flex', justifyContent: 'center', padding: '48px' }}>
|
||
<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>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div style={{ width: '100%', maxWidth: '400px' }}>
|
||
|
||
<p style={{ fontSize: '14px', color: '#666', marginBottom: '28px' }}>
|
||
Добро пожаловать! Войдите в аккаунт.
|
||
</p>
|
||
|
||
<form onSubmit={handleSubmit}>
|
||
<div style={{ marginBottom: '20px' }}>
|
||
<md-filled-text-field
|
||
label="Email"
|
||
type="email"
|
||
value={email}
|
||
onInput={(e: any) => setEmail(e.target.value || '')}
|
||
required
|
||
style={{ width: '100%' }}
|
||
/>
|
||
</div>
|
||
|
||
<div style={{ marginBottom: '20px' }}>
|
||
<md-filled-text-field
|
||
label="Пароль"
|
||
type="password"
|
||
value={password}
|
||
onInput={(e: any) => setPassword(e.target.value || '')}
|
||
required
|
||
style={{ width: '100%' }}
|
||
/>
|
||
</div>
|
||
|
||
{error && (
|
||
<div
|
||
style={{
|
||
padding: '12px 16px',
|
||
marginBottom: '20px',
|
||
background: '#ffebee',
|
||
color: '#c62828',
|
||
borderRadius: '12px',
|
||
fontSize: '14px',
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
{error}
|
||
</div>
|
||
)}
|
||
|
||
<md-filled-button
|
||
type="submit"
|
||
disabled={loading}
|
||
style={{
|
||
width: '100%',
|
||
height: '48px',
|
||
marginBottom: '16px',
|
||
fontSize: '16px',
|
||
fontWeight: '500',
|
||
}}
|
||
>
|
||
{loading ? 'Вход...' : 'Войти'}
|
||
</md-filled-button>
|
||
</form>
|
||
|
||
<div
|
||
style={{
|
||
textAlign: 'center',
|
||
marginTop: '20px',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: '8px',
|
||
}}
|
||
>
|
||
<Link
|
||
href="/forgot-password"
|
||
style={{
|
||
fontSize: '14px',
|
||
color: 'var(--md-sys-color-primary, #6750a4)',
|
||
textDecoration: 'none',
|
||
}}
|
||
>
|
||
Забыли пароль?
|
||
</Link>
|
||
<Link
|
||
href="/register"
|
||
style={{
|
||
fontSize: '14px',
|
||
color: 'var(--md-sys-color-primary, #6750a4)',
|
||
textDecoration: 'none',
|
||
}}
|
||
>
|
||
Нет аккаунта? Зарегистрироваться
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|