P2-015 Frontend: Auth pages¶
Метаданные¶
| Поле | Значение |
|---|---|
| Фаза | Phase 2: Core |
| Статус | done |
| Приоритет | critical |
| Связь с roadmap | Roadmap - Frontend |
Контекст¶
Бизнес-контекст¶
Страницы аутентификации — точка входа для пользователей. Нужны формы входа, регистрации и восстановления пароля. Также интеграция с Telegram Login Widget.
Технический контекст¶
- Next.js 14 App Router
- Route group
(auth)— минимальный layout - shadcn/ui компоненты
- React Hook Form + Zod для валидации
- Zustand для auth state
Реализованный код: - login/page.tsx — страница входа - register/page.tsx — страница регистрации - forgot-password/page.tsx — восстановление пароля - reset-password/page.tsx — сброс пароля - verify-email-sent/page.tsx — подтверждение отправки - components/features/auth/ — form компоненты
Связанные документы: - Frontend Architecture - Components - User Service API
Цель¶
Реализовать полнофункциональные страницы входа, регистрации и восстановления пароля с интеграцией с backend API.
Definition of Ready (DoR)¶
- [x] Контекст понятен и описан
- [x] Цель сформулирована
- [x] Acceptance Criteria определены
- [x] Технические детали проработаны
- [x] Зависимости определены (backend API)
- [x] Нет блокеров
Acceptance Criteria¶
Страница входа (/login)¶
- [x] Форма с полями: email, password
- [x] Валидация: email формат, пароль обязателен
- [x] Кнопка «Войти»
- [x] Ссылка «Забыли пароль?»
- [x] Ссылка «Зарегистрироваться»
- [x] Telegram Login Widget
- [x] При успешном входе — redirect на /dashboard
- [x] Отображение ошибок API
- [x] Loading состояние кнопки
Страница регистрации (/register)¶
- [x] Форма: email, password, confirmPassword, firstName, lastName
- [x] Валидация: email, пароль 8+ символов с буквами и цифрами
- [x] Пароли должны совпадать
- [x] Кнопка «Зарегистрироваться»
- [x] Ссылка «Уже есть аккаунт?»
- [x] После регистрации — redirect на /verify-email-sent
- [x] Telegram Login Widget как альтернатива
Страница восстановления пароля (/forgot-password)¶
- [x] Форма с email
- [x] Кнопка «Отправить инструкции»
- [x] После отправки — сообщение об успехе
- [x] Ссылка назад к входу
Страница сброса пароля (/reset-password)¶
- [x] Форма: newPassword, confirmPassword
- [x] Валидация пароля
- [x] Token из URL query params
- [x] После сброса — redirect на /login с сообщением
Telegram Login Widget¶
- [x] Интеграция на страницах login и register
- [x] При успешном входе через Telegram — создание/вход
- [x] Обработка callback от Telegram
Auth State Management¶
- [x] Zustand store для auth state (user, accessToken)
- [x] Persist в localStorage
- [x] Auto-refresh токена
- [x] Logout очищает state
Definition of Done (DoD)¶
- [x] Все Acceptance Criteria выполнены
- [x] Используются только shadcn/ui компоненты
- [x] React Hook Form + Zod для форм
- [x] Responsive design (mobile-first)
- [x] Unit тесты для форм (18 unit + 4 integration тестов)
- [x] E2E тесты критических путей (18 Playwright тестов)
- [x] Code review пройден
- [ ] CI/CD pipeline проходит
Технические детали¶
Затрагиваемые компоненты¶
- [ ] Backend: —
- [x] Frontend:
app/(auth)/*,lib/api/auth.ts,lib/store/auth-store.ts - [ ] Database: —
- [ ] Infrastructure: —
Структура файлов¶
frontend/
├── app/
│ └── (auth)/
│ ├── layout.tsx — минимальный layout
│ ├── login/page.tsx — вход
│ ├── register/page.tsx — регистрация
│ ├── forgot-password/page.tsx
│ ├── reset-password/page.tsx
│ └── verify-email/page.tsx
├── components/
│ └── features/
│ └── auth/
│ ├── login-form.tsx
│ ├── register-form.tsx
│ ├── forgot-password-form.tsx
│ ├── reset-password-form.tsx
│ └── telegram-login.tsx
└── lib/
├── api/auth.ts
└── store/auth-store.ts
Login Form¶
'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { useAuthStore } from '@/lib/store/auth-store';
import { authApi } from '@/lib/api/auth';
const loginSchema = z.object({
email: z.string().email('Некорректный email'),
password: z.string().min(1, 'Пароль обязателен'),
});
export function LoginForm() {
const { setAuth } = useAuthStore();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const form = useForm<z.infer<typeof loginSchema>>({
resolver: zodResolver(loginSchema),
defaultValues: { email: '', password: '' },
});
async function onSubmit(data: z.infer<typeof loginSchema>) {
setIsLoading(true);
setError(null);
try {
const response = await authApi.login(data);
setAuth(response.user, response.accessToken);
router.push('/dashboard');
} catch (err) {
setError(err.response?.data?.message || 'Ошибка входа');
} finally {
setIsLoading(false);
}
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
{error && <div className="text-destructive text-sm">{error}</div>}
{/* fields */}
<Button type="submit" className="w-full" disabled={isLoading}>
{isLoading ? 'Вход...' : 'Войти'}
</Button>
</form>
</Form>
);
}
Telegram Login Widget¶
'use client';
import { useEffect, useRef } from 'react';
interface TelegramLoginProps {
botName: string;
onAuth: (user: TelegramUser) => void;
}
export function TelegramLogin({ botName, onAuth }: TelegramLoginProps) {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// Добавляем скрипт Telegram
const script = document.createElement('script');
script.src = 'https://telegram.org/js/telegram-widget.js?22';
script.setAttribute('data-telegram-login', botName);
script.setAttribute('data-size', 'large');
script.setAttribute('data-request-access', 'write');
script.setAttribute('data-onauth', 'onTelegramAuth(user)');
script.async = true;
// Callback
(window as any).onTelegramAuth = (user: TelegramUser) => {
onAuth(user);
};
containerRef.current?.appendChild(script);
}, [botName, onAuth]);
return <div ref={containerRef} />;
}
Зависимости¶
Блокирует¶
- P2-016 Dashboard (требует авторизации)
Зависит от¶
Out of Scope¶
- Social login (Google, GitHub)
- 2FA
- Magic link login
- Запоминание устройства
Заметки¶
- ✅ Все страницы реализованы и функциональны
- Telegram Login Widget требует HTTPS в production (в dev работает)
- accessToken хранится в localStorage через Zustand persist
- refreshToken передаётся через httpOnly cookie (защита от XSS)
- Auto-refresh токена реализован в API client interceptor
- Тесты: 29 unit/integration (Vitest) + 18 E2E (Playwright)