import { useEffect, useState } from 'react' import { alpha, useTheme } from '@mui/material/styles' import Alert from '@mui/material/Alert' import Box from '@mui/material/Box' import Button from '@mui/material/Button' import InputAdornment from '@mui/material/InputAdornment' import Paper from '@mui/material/Paper' import Stack from '@mui/material/Stack' import TextField from '@mui/material/TextField' import Typography from '@mui/material/Typography' import { useMutation } from '@tanstack/react-query' import { useUnit } from 'effector-react' import { Lock, Mail } from 'lucide-react' import { useForm } from 'react-hook-form' import { useNavigate, useSearchParams } from 'react-router-dom' import { OAuthButtons } from '@/features/auth-oauth' import { apiClient } from '@/shared/api/client' import { $user, tokenSet } from '@/shared/model/auth' import { BearLogo } from '@/shared/ui/BearLogo' type AuthResponse = { token: string user: { id: string email: string displayName?: string | null avatar?: string | null avatarStyle?: string | null } } function getApiErrorMessage(err: unknown): string | null { if (!err || typeof err !== 'object') return null const anyErr = err as Record const response = anyErr.response as Record | undefined const data = response?.data as Record | undefined const msg = data?.error return typeof msg === 'string' ? msg : null } export function AuthPage() { const theme = useTheme() const [message, setMessage] = useState(null) const [oauthError, setOauthError] = useState(null) const [tab, setTab] = useState(0) const [isRegister, setIsRegister] = useState(false) const [searchParams, setSearchParams] = useSearchParams() const navigate = useNavigate() const user = useUnit($user) const { register, watch } = useForm<{ email: string password: string passwordConfirm: string displayName: string code: string }>({ defaultValues: { email: '', password: '', passwordConfirm: '', displayName: '', code: '' }, mode: 'onChange', }) const email = watch('email') const password = watch('password') const passwordConfirm = watch('passwordConfirm') const code = watch('code') useEffect(() => { if (user) navigate('/', { replace: true }) }, [navigate, user]) useEffect(() => { const err = searchParams.get('oauthError') if (!err) return setOauthError(err) setSearchParams({}, { replace: true }) }, [searchParams, setSearchParams]) const loginMutation = useMutation({ mutationFn: async () => { const { data } = await apiClient.post('auth/login', { email, password }) tokenSet(data.token) navigate('/', { replace: true }) }, }) const registerMutation = useMutation({ mutationFn: async () => { const { data } = await apiClient.post('auth/register', { email, password, displayName: watch('displayName') || undefined, }) tokenSet(data.token) navigate('/', { replace: true }) }, }) const requestCode = useMutation({ mutationFn: async () => { await apiClient.post('auth/request-code', { email }) }, onSuccess: () => setMessage('Код отправлен. Проверьте почту (в dev может быть в логах сервера).'), }) const verifyCode = useMutation({ mutationFn: async () => { const { data } = await apiClient.post('auth/verify-code', { email, code }) tokenSet(data.token) navigate('/', { replace: true }) }, }) const errMsg = getApiErrorMessage(loginMutation.error) || getApiErrorMessage(registerMutation.error) || getApiErrorMessage(requestCode.error) || getApiErrorMessage(verifyCode.error) const passwordError = isRegister && passwordConfirm && password !== passwordConfirm ? 'Пароли не совпадают' : null return ( Добро пожаловать в Любимый Креатив Войдите или зарегистрируйтесь, чтобы продолжить {[ { label: 'Пароль', idx: 0 }, { label: 'Код', idx: 1 }, { label: 'Другой способ', idx: 2 }, ].map(({ label, idx }) => ( ))} {(errMsg || oauthError) && ( { setOauthError(null) }} > {errMsg || oauthError} )} {message && ( setMessage(null)}> {message} )} {tab === 0 && ( ), }, }} /> {isRegister && ( )} ), }, }} /> {isRegister && ( )} {isRegister ? ( ) : ( )} )} {tab === 1 && ( ), }, }} /> )} {tab === 2 && ( )} ) }