feat: add password change and reset via email code
This commit is contained in:
@@ -44,6 +44,9 @@ export function AuthPage() {
|
||||
const [oauthError, setOauthError] = useState<string | null>(null)
|
||||
const [tab, setTab] = useState(0)
|
||||
const [isRegister, setIsRegister] = useState(false)
|
||||
const [showForgot, setShowForgot] = useState(false)
|
||||
const [forgotStep, setForgotStep] = useState(0)
|
||||
const [forgotEmail, setForgotEmail] = useState('')
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const navigate = useNavigate()
|
||||
const user = useUnit($user)
|
||||
@@ -99,7 +102,7 @@ export function AuthPage() {
|
||||
mutationFn: async () => {
|
||||
await apiClient.post('auth/request-code', { email })
|
||||
},
|
||||
onSuccess: () => setMessage('Код отправлен. Проверьте почту (в dev может быть в логах сервера).'),
|
||||
onSuccess: () => setMessage('Код отправлен. Проверьте почту.'),
|
||||
})
|
||||
|
||||
const verifyCode = useMutation({
|
||||
@@ -110,11 +113,38 @@ export function AuthPage() {
|
||||
},
|
||||
})
|
||||
|
||||
const forgotCode = useMutation({
|
||||
mutationFn: async () => {
|
||||
await apiClient.post('auth/forgot-password', { email: forgotEmail })
|
||||
},
|
||||
onSuccess: () => {
|
||||
setForgotStep(1)
|
||||
setMessage('Код отправлен на почту')
|
||||
},
|
||||
})
|
||||
|
||||
const resetPassword = useMutation({
|
||||
mutationFn: async () => {
|
||||
await apiClient.post('auth/reset-password', {
|
||||
email: forgotEmail,
|
||||
code,
|
||||
newPassword: password,
|
||||
})
|
||||
},
|
||||
onSuccess: () => {
|
||||
setShowForgot(false)
|
||||
setForgotStep(0)
|
||||
setMessage('Пароль изменён. Войдите с новым паролем.')
|
||||
},
|
||||
})
|
||||
|
||||
const errMsg =
|
||||
getApiErrorMessage(loginMutation.error) ||
|
||||
getApiErrorMessage(registerMutation.error) ||
|
||||
getApiErrorMessage(requestCode.error) ||
|
||||
getApiErrorMessage(verifyCode.error)
|
||||
getApiErrorMessage(verifyCode.error) ||
|
||||
getApiErrorMessage(forgotCode.error) ||
|
||||
getApiErrorMessage(resetPassword.error)
|
||||
|
||||
const passwordError = isRegister && passwordConfirm && password !== passwordConfirm ? 'Пароли не совпадают' : null
|
||||
|
||||
@@ -304,6 +334,99 @@ export function AuthPage() {
|
||||
Войти
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{!isRegister && !showForgot && (
|
||||
<Button
|
||||
variant="text"
|
||||
size="small"
|
||||
sx={{ textTransform: 'none', alignSelf: 'center', color: 'text.secondary' }}
|
||||
onClick={() => {
|
||||
setShowForgot(true)
|
||||
setForgotStep(0)
|
||||
setForgotEmail(email)
|
||||
setMessage(null)
|
||||
}}
|
||||
>
|
||||
Забыли пароль?
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{showForgot && (
|
||||
<>
|
||||
<TextField
|
||||
label="Email"
|
||||
value={forgotEmail}
|
||||
onChange={(e) => setForgotEmail(e.target.value)}
|
||||
fullWidth
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<Mail size={18} />
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
{forgotStep === 1 && (
|
||||
<>
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
|
||||
<TextField
|
||||
label="Код (6 цифр)"
|
||||
inputMode="numeric"
|
||||
value={code}
|
||||
onChange={(e) => {
|
||||
register('code').onChange(e)
|
||||
}}
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={() => forgotCode.mutate()}
|
||||
disabled={!forgotEmail || forgotCode.isPending}
|
||||
sx={{ whiteSpace: 'nowrap' }}
|
||||
>
|
||||
Отправить ещё раз
|
||||
</Button>
|
||||
</Stack>
|
||||
<TextField label="Новый пароль" type="password" {...register('password')} fullWidth />
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={
|
||||
!code || code.length !== 6 || !password || password.length < 8 || resetPassword.isPending
|
||||
}
|
||||
onClick={() => resetPassword.mutate()}
|
||||
>
|
||||
Сменить пароль
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{forgotStep === 0 && (
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={!forgotEmail || forgotCode.isPending}
|
||||
onClick={() => forgotCode.mutate()}
|
||||
>
|
||||
Отправить код
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="text"
|
||||
size="small"
|
||||
sx={{ textTransform: 'none', alignSelf: 'center' }}
|
||||
onClick={() => {
|
||||
setShowForgot(false)
|
||||
setForgotStep(0)
|
||||
setMessage(null)
|
||||
}}
|
||||
>
|
||||
Назад к входу
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user