85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
|
|
import { useState } from 'react'
|
|||
|
|
import { Link, useNavigate } from 'react-router-dom'
|
|||
|
|
import { useForm } from 'react-hook-form'
|
|||
|
|
import { zodResolver } from '@hookform/resolvers/zod'
|
|||
|
|
import { z } from 'zod'
|
|||
|
|
import { useAuthStore } from '@/store/auth'
|
|||
|
|
import { Button, Input, Card, CardHeader, CardTitle, CardContent } from '@/components/ui'
|
|||
|
|
|
|||
|
|
const loginSchema = z.object({
|
|||
|
|
login: z.string().min(3, 'Логин должен быть не менее 3 символов'),
|
|||
|
|
password: z.string().min(6, 'Пароль должен быть не менее 6 символов'),
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
type LoginForm = z.infer<typeof loginSchema>
|
|||
|
|
|
|||
|
|
export function LoginPage() {
|
|||
|
|
const navigate = useNavigate()
|
|||
|
|
const { login, isLoading, error, clearError } = useAuthStore()
|
|||
|
|
const [submitError, setSubmitError] = useState<string | null>(null)
|
|||
|
|
|
|||
|
|
const {
|
|||
|
|
register,
|
|||
|
|
handleSubmit,
|
|||
|
|
formState: { errors },
|
|||
|
|
} = useForm<LoginForm>({
|
|||
|
|
resolver: zodResolver(loginSchema),
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const onSubmit = async (data: LoginForm) => {
|
|||
|
|
setSubmitError(null)
|
|||
|
|
clearError()
|
|||
|
|
try {
|
|||
|
|
await login(data)
|
|||
|
|
navigate('/marathons')
|
|||
|
|
} catch {
|
|||
|
|
setSubmitError(error || 'Ошибка входа')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="max-w-md mx-auto">
|
|||
|
|
<Card>
|
|||
|
|
<CardHeader>
|
|||
|
|
<CardTitle className="text-center">Вход</CardTitle>
|
|||
|
|
</CardHeader>
|
|||
|
|
<CardContent>
|
|||
|
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
|||
|
|
{(submitError || error) && (
|
|||
|
|
<div className="p-3 bg-red-500/20 border border-red-500 rounded-lg text-red-500 text-sm">
|
|||
|
|
{submitError || error}
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
<Input
|
|||
|
|
label="Логин"
|
|||
|
|
placeholder="Введите логин"
|
|||
|
|
error={errors.login?.message}
|
|||
|
|
{...register('login')}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<Input
|
|||
|
|
label="Пароль"
|
|||
|
|
type="password"
|
|||
|
|
placeholder="Введите пароль"
|
|||
|
|
error={errors.password?.message}
|
|||
|
|
{...register('password')}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<Button type="submit" className="w-full" isLoading={isLoading}>
|
|||
|
|
Войти
|
|||
|
|
</Button>
|
|||
|
|
|
|||
|
|
<p className="text-center text-gray-400 text-sm">
|
|||
|
|
Нет аккаунта?{' '}
|
|||
|
|
<Link to="/register" className="link">
|
|||
|
|
Зарегистрироваться
|
|||
|
|
</Link>
|
|||
|
|
</p>
|
|||
|
|
</form>
|
|||
|
|
</CardContent>
|
|||
|
|
</Card>
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|