Files
links/frontend/linktree-frontend/src/app/auth/login/page.tsx
Andrey K. Choi 2e535513b5 + Приведены все функции приложения в рабочий вид
+ Наведен порядок в файлах проекта
+ Наведен порядок в документации
+ Настроены скрипты установки, развертки и так далее, расширен MakeFile
2025-11-02 06:09:55 +09:00

131 lines
4.8 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.

// src/app/auth/login/page.tsx
'use client'
import { useState, useEffect } from 'react'
import { useForm } from 'react-hook-form'
import { useRouter } from 'next/navigation'
import Link from 'next/link'
type FormData = { username: string; password: string }
export default function LoginPage() {
const router = useRouter()
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>()
const [apiError, setApiError] = useState<string | null>(null)
useEffect(() => {
if (typeof window !== 'undefined' && localStorage.getItem('token')) {
router.push('/dashboard')
}
}, [router])
async function onSubmit(data: FormData) {
setApiError(null)
try {
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/auth/login/`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}
)
if (!res.ok) {
const json = await res.json()
setApiError(json.detail || 'Ошибка входа')
return
}
const { access } = await res.json()
localStorage.setItem('token', access)
router.push('/dashboard')
} catch {
setApiError('Сетевая ошибка')
}
}
return (
<div className="min-vh-100 d-flex align-items-center justify-content-center bg-light">
<div className="container">
<div className="row justify-content-center">
<div className="col-md-6 col-lg-5">
<div className="card shadow-lg border-0 rounded-4">
<div className="card-body p-5">
<div className="text-center mb-4">
<img
src="/assets/img/CAT.png"
alt="CatLink"
width="80"
height="80"
className="mb-3"
/>
<h2 className="fw-bold text-primary">Добро пожаловать!</h2>
<p className="text-muted">Войдите в свой аккаунт CatLink</p>
</div>
{apiError && (
<div className="alert alert-danger" role="alert">
{apiError}
</div>
)}
<form onSubmit={handleSubmit(onSubmit)}>
<div className="mb-3">
<label htmlFor="username" className="form-label">Имя пользователя</label>
<input
id="username"
type="text"
placeholder="Введите имя пользователя"
className={`form-control form-control-lg ${errors.username ? 'is-invalid' : ''}`}
{...register('username', { required: 'Введите имя пользователя' })}
/>
{errors.username && (
<div className="invalid-feedback">{errors.username.message}</div>
)}
</div>
<div className="mb-4">
<label htmlFor="password" className="form-label">Пароль</label>
<input
id="password"
type="password"
placeholder="Введите пароль"
className={`form-control form-control-lg ${errors.password ? 'is-invalid' : ''}`}
{...register('password', { required: 'Введите пароль' })}
/>
{errors.password && (
<div className="invalid-feedback">{errors.password.message}</div>
)}
</div>
<button
type="submit"
disabled={isSubmitting}
className="btn btn-primary btn-lg w-100 mb-3"
>
{isSubmitting ? (
<>
<span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
Входим...
</>
) : (
'Войти'
)}
</button>
</form>
<div className="text-center">
<p className="text-muted mb-0">
Нет аккаунта?{' '}
<Link href="/auth/register" className="text-primary text-decoration-none fw-bold">
Зарегистрироваться
</Link>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}