uchill/front_minimal/src/sections/auth/jwt/jwt-reset-password-view.jsx

203 lines
6.8 KiB
JavaScript
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.

'use client';
import { z as zod } from 'zod';
import { useState, Suspense } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import Link from '@mui/material/Link';
import Alert from '@mui/material/Alert';
import Stack from '@mui/material/Stack';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import LoadingButton from '@mui/lab/LoadingButton';
import InputAdornment from '@mui/material/InputAdornment';
import { paths } from 'src/routes/paths';
import { useRouter, useSearchParams } from 'src/routes/hooks';
import { RouterLink } from 'src/routes/components';
import { useBoolean } from 'src/hooks/use-boolean';
import { Iconify } from 'src/components/iconify';
import { Form, Field } from 'src/components/hook-form';
import { confirmPasswordReset } from 'src/auth/context/jwt';
// ----------------------------------------------------------------------
const ResetPasswordSchema = zod
.object({
newPassword: zod
.string()
.min(1, { message: 'Введите пароль!' })
.min(8, { message: 'Пароль должен содержать не менее 8 символов!' }),
newPasswordConfirm: zod.string().min(1, { message: 'Подтвердите пароль!' }),
})
.refine((data) => data.newPassword === data.newPasswordConfirm, {
message: 'Пароли не совпадают!',
path: ['newPasswordConfirm'],
});
// ----------------------------------------------------------------------
function ResetPasswordContent() {
const router = useRouter();
const searchParams = useSearchParams();
const token = searchParams.get('token');
const [errorMsg, setErrorMsg] = useState('');
const [successMsg, setSuccessMsg] = useState('');
const newPassword = useBoolean();
const newPasswordConfirm = useBoolean();
const methods = useForm({
resolver: zodResolver(ResetPasswordSchema),
defaultValues: { newPassword: '', newPasswordConfirm: '' },
});
const {
handleSubmit,
formState: { isSubmitting },
} = methods;
const onSubmit = handleSubmit(async (data) => {
if (!token) {
setErrorMsg('Ссылка для сброса отсутствует. Запросите новый сброс пароля.');
return;
}
try {
await confirmPasswordReset({
token,
newPassword: data.newPassword,
newPasswordConfirm: data.newPasswordConfirm,
});
setSuccessMsg('Пароль успешно изменён. Теперь вы можете войти с новым паролем.');
setErrorMsg('');
} catch (error) {
console.error(error);
const msg = error?.response?.data?.error?.message || error?.response?.data?.message || error?.response?.data?.detail || 'Не удалось сбросить пароль. Возможно, ссылка устарела — запросите новую.';
setErrorMsg(msg);
}
});
if (!token) {
return (
<>
<Stack spacing={1.5} sx={{ mb: 5 }}>
<Typography variant="h5">Сброс пароля</Typography>
</Stack>
<Alert severity="error" sx={{ mb: 3 }}>
Ссылка для сброса отсутствует. Перейдите по ссылке из письма или запросите новую.
</Alert>
<Link component={RouterLink} href={paths.auth.jwt.forgotPassword} variant="subtitle2">
Запросить новую ссылку
</Link>
</>
);
}
if (successMsg) {
return (
<>
<Stack spacing={1.5} sx={{ mb: 5 }}>
<Typography variant="h5">Сброс пароля</Typography>
</Stack>
<Alert severity="success" sx={{ mb: 3 }}>
{successMsg}
</Alert>
<Link component={RouterLink} href={paths.auth.jwt.signIn} variant="subtitle2">
Войти
</Link>
</>
);
}
return (
<>
<Stack spacing={1.5} sx={{ mb: 5 }}>
<Typography variant="h5">Новый пароль</Typography>
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
Введите новый пароль ниже.
</Typography>
</Stack>
{!!errorMsg && (
<Alert severity="error" sx={{ mb: 3 }}>
{errorMsg}
</Alert>
)}
<Form methods={methods} onSubmit={onSubmit}>
<Stack spacing={3}>
<Field.Text
name="newPassword"
label="Новый пароль"
placeholder="8+ символов"
type={newPassword.value ? 'text' : 'password'}
InputLabelProps={{ shrink: true }}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={newPassword.onToggle} edge="end">
<Iconify icon={newPassword.value ? 'solar:eye-bold' : 'solar:eye-closed-bold'} />
</IconButton>
</InputAdornment>
),
}}
/>
<Field.Text
name="newPasswordConfirm"
label="Подтвердите пароль"
placeholder="8+ символов"
type={newPasswordConfirm.value ? 'text' : 'password'}
InputLabelProps={{ shrink: true }}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={newPasswordConfirm.onToggle} edge="end">
<Iconify icon={newPasswordConfirm.value ? 'solar:eye-bold' : 'solar:eye-closed-bold'} />
</IconButton>
</InputAdornment>
),
}}
/>
<LoadingButton
fullWidth
color="inherit"
size="large"
type="submit"
variant="contained"
loading={isSubmitting}
loadingIndicator="Сохранение..."
>
Сохранить пароль
</LoadingButton>
</Stack>
</Form>
<Link
component={RouterLink}
href={paths.auth.jwt.signIn}
variant="body2"
color="inherit"
sx={{ display: 'block', mt: 3, textAlign: 'center' }}
>
Вернуться ко входу
</Link>
</>
);
}
// ----------------------------------------------------------------------
export function JwtResetPasswordView() {
return (
<Suspense fallback={<Typography variant="body2">Загрузка...</Typography>}>
<ResetPasswordContent />
</Suspense>
);
}