Files
aerbim-ht-monitor/frontend/app/(auth)/login/page.tsx
2026-02-02 11:00:40 +03:00

219 lines
8.6 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 React, { useState, useEffect } from 'react'
import Link from 'next/link'
import Image from 'next/image'
import { useRouter } from 'next/navigation'
import useUserStore from '@/app/store/userStore'
import Loader from '@/components/ui/Loader'
import { useForm } from '@/app/hooks/useForm'
import Button from '@/components/ui/Button'
import showToast from '@/components/ui/ShowToast'
import { signIn } from 'next-auth/react'
import TextInput from '@/components/ui/TextInput'
import RoleSelector from '@/components/selectors/RoleSelector'
const validationRules = {
login: { required: true },
password: { required: true },
}
const LoginPage = () => {
const router = useRouter()
const { isAuthenticated } = useUserStore()
const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
// проверяем логин
if (isAuthenticated) {
// распределяем
router.replace('/objects')
return
}
const timer = setTimeout(() => {
setIsLoading(false)
}, 300)
return () => clearTimeout(timer)
}, [isAuthenticated, router])
const { values, isVisible, handleChange, handleSubmit, togglePasswordVisibility } = useForm(
{
login: '',
password: '',
role: '',
},
validationRules,
async values => {
try {
const result = await signIn('credentials', {
login: values.login,
password: values.password,
redirect: false,
role: values.role,
})
if (result?.error) {
showToast({ type: 'error', message: result.error })
return
}
showToast({ type: 'success', message: 'Авторизация успешна!' })
router.push('/objects')
} catch {
showToast({ type: 'error', message: 'Ошибка при входе в аккаунт' })
}
}
)
if (isLoading) {
return <Loader />
}
const interSemiboldStyle = { fontFamily: 'Inter, sans-serif', fontWeight: 600 }
const interRegularStyle = { fontFamily: 'Inter, sans-serif', fontWeight: 400 }
return (
<div className="relative min-h-screen w-full flex flex-col items-center justify-center gap-8 py-8 overflow-hidden">
<style>{`
@keyframes float {
0%, 100% { transform: translateY(0px) rotate(0deg); }
50% { transform: translateY(-20px) rotate(180deg); }
}
@keyframes glow {
0%, 100% { opacity: 0.3; }
50% { opacity: 0.8; }
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes slideIn {
0% { transform: translateX(-100%); opacity: 0; }
100% { transform: translateX(0); opacity: 1; }
}
.float-animation {
animation: float 6s ease-in-out infinite;
}
.glow-animation {
animation: glow 3s ease-in-out infinite;
}
.rotate-animation {
animation: rotate 20s linear infinite;
}
.slide-in {
animation: slideIn 0.8s ease-out;
}
`}</style>
{/* Фоновый градиент - многоуровневый */}
<div className="absolute inset-0 bg-gradient-to-br from-[#050810] via-[#0f1729] to-[#1a1f28] z-0"></div>
{/* Второй слой градиента */}
<div className="absolute inset-0 bg-gradient-to-tr from-transparent via-[#1a3a52]/20 to-transparent z-0"></div>
{/* Основные светящиеся орбиты */}
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-gradient-to-br from-blue-600/20 to-cyan-500/10 rounded-full blur-3xl glow-animation" style={{ animationDelay: '0s' }}></div>
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-gradient-to-tl from-blue-500/20 to-cyan-500/10 rounded-full blur-3xl glow-animation" style={{ animationDelay: '1s' }}></div>
<div className="absolute top-1/2 right-1/3 w-80 h-80 bg-gradient-to-bl from-cyan-500/15 to-blue-400/10 rounded-full blur-3xl glow-animation" style={{ animationDelay: '2s' }}></div>
{/* Дополнительные акцентные элементы */}
<div className="absolute top-0 right-0 w-72 h-72 bg-blue-500/5 rounded-full blur-2xl float-animation"></div>
<div className="absolute bottom-0 left-0 w-72 h-72 bg-cyan-500/5 rounded-full blur-2xl float-animation" style={{ animationDelay: '3s' }}></div>
{/* Сетка с градиентом */}
<div className="absolute inset-0 opacity-5 z-0" style={{
backgroundImage: `
linear-gradient(0deg, transparent 24%, rgba(59, 147, 245, 0.08) 25%, rgba(59, 147, 245, 0.08) 26%, transparent 27%, transparent 74%, rgba(59, 147, 245, 0.08) 75%, rgba(59, 147, 245, 0.08) 76%, transparent 77%, transparent),
linear-gradient(90deg, transparent 24%, rgba(59, 147, 245, 0.08) 25%, rgba(59, 147, 245, 0.08) 26%, transparent 27%, transparent 74%, rgba(59, 147, 245, 0.08) 75%, rgba(59, 147, 245, 0.08) 76%, transparent 77%, transparent)
`,
backgroundSize: '60px 60px'
}}></div>
{/* Диагональные линии */}
<div className="absolute inset-0 opacity-3 z-0" style={{
backgroundImage: `
repeating-linear-gradient(45deg, transparent, transparent 35px, rgba(59, 147, 245, 0.1) 35px, rgba(59, 147, 245, 0.1) 70px)
`
}}></div>
{/* Центральный светящийся элемент */}
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-1 h-1 bg-cyan-400 rounded-full shadow-2xl" style={{
boxShadow: '0 0 60px 20px rgba(34, 211, 238, 0.15), 0 0 100px 40px rgba(59, 147, 245, 0.08)'
}}></div>
{/* Верхний логотип */}
<div className="relative z-10 mb-4 flex items-center justify-center gap-4 slide-in">
<Image src="/icons/logo.png" alt="AerBIM Logo" width={438} height={60} />
</div>
{/* Карточка формы с улучшенным стилем */}
<div className="relative z-10 mx-4 flex w-full max-w-md flex-col gap-6 rounded-[20px] bg-[#161824]/80 p-8 shadow-2xl border border-cyan-500/20 backdrop-blur-xl slide-in" style={{ animationDelay: '0.2s' }}>
{/* Верхний градиент на карточке */}
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-cyan-500/50 to-transparent rounded-t-[20px]"></div>
<form
className="flex flex-col gap-6"
onSubmit={handleSubmit}
>
<div className="flex flex-col gap-6">
<h1 style={interSemiboldStyle} className="text-3xl text-white">
Авторизация
</h1>
<div className="flex flex-col gap-4">
<TextInput
value={values.login}
name="login"
handleChange={handleChange}
placeholder="ivan_ivanov"
style="register"
label="Ваш логин"
/>
<TextInput
value={values.password}
name="password"
handleChange={handleChange}
placeholder="Не менее 8 символов"
style="register"
label="Ваш пароль"
isPassword={true}
isVisible={isVisible}
togglePasswordVisibility={togglePasswordVisibility}
/>
<RoleSelector
value={values.role}
name="role"
handleChange={handleChange}
label="Ваша роль"
placeholder="Выберите вашу роль"
/>
</div>
<button
type="submit"
className="mt-4 w-full py-3 px-4 bg-gradient-to-r from-[#3193f5] to-[#1e7ce8] hover:from-[#2563eb] hover:to-[#1a5fd6] text-white font-semibold rounded-xl transition-all duration-200 shadow-lg hover:shadow-2xl hover:shadow-blue-500/50"
style={interSemiboldStyle}
>
Войти
</button>
</div>
</form>
<div className="border-t border-cyan-500/20 pt-4">
<p style={interRegularStyle} className="text-center text-sm text-gray-400">
<span className="hover:text-cyan-400 transition-colors duration-200 hover:underline cursor-pointer">
<Link href="/password-recovery">Забыли логин/пароль?</Link>
</span>
</p>
</div>
</div>
</div>
)
}
export default LoginPage