fix: translate auth pages to Russian, fix backend error parsing, calendar timezone
- Translate forgot-password and reset-password pages to Russian
- Translate verify-email page to Russian
- Fix error message parsing: backend returns {error:{message}} not {message}
- Apply timezone fix in calendar (FullCalendar timeZone prop + fTime helper)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f6caa7df6b
commit
75d6072309
|
|
@ -23,8 +23,8 @@ import { requestPasswordReset } from 'src/auth/context/jwt';
|
|||
const ForgotPasswordSchema = zod.object({
|
||||
email: zod
|
||||
.string()
|
||||
.min(1, { message: 'Email is required!' })
|
||||
.email({ message: 'Email must be a valid email address!' }),
|
||||
.min(1, { message: 'Введите email!' })
|
||||
.email({ message: 'Введите корректный email адрес!' }),
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
|
@ -46,11 +46,11 @@ export function JwtForgotPasswordView() {
|
|||
const onSubmit = handleSubmit(async (data) => {
|
||||
try {
|
||||
await requestPasswordReset({ email: data.email });
|
||||
setSuccessMsg('Password reset instructions have been sent to your email.');
|
||||
setSuccessMsg('Инструкции по сбросу пароля отправлены на ваш email.');
|
||||
setErrorMsg('');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
const msg = error?.response?.data?.message || error?.response?.data?.detail || 'Error sending request. Please check your email.';
|
||||
const msg = error?.response?.data?.error?.message || error?.response?.data?.message || error?.response?.data?.detail || 'Ошибка отправки запроса. Проверьте правильность email.';
|
||||
setErrorMsg(msg);
|
||||
}
|
||||
});
|
||||
|
|
@ -58,10 +58,10 @@ export function JwtForgotPasswordView() {
|
|||
return (
|
||||
<>
|
||||
<Stack spacing={1.5} sx={{ mb: 5 }}>
|
||||
<Typography variant="h5">Forgot your password?</Typography>
|
||||
<Typography variant="h5">Забыли пароль?</Typography>
|
||||
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
Enter your email address and we will send you a link to reset your password.
|
||||
Введите ваш email и мы отправим вам ссылку для сброса пароля.
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ export function JwtForgotPasswordView() {
|
|||
{!successMsg && (
|
||||
<Form methods={methods} onSubmit={onSubmit}>
|
||||
<Stack spacing={3}>
|
||||
<Field.Text name="email" label="Email address" InputLabelProps={{ shrink: true }} />
|
||||
<Field.Text name="email" label="Email" InputLabelProps={{ shrink: true }} />
|
||||
|
||||
<LoadingButton
|
||||
fullWidth
|
||||
|
|
@ -89,9 +89,9 @@ export function JwtForgotPasswordView() {
|
|||
type="submit"
|
||||
variant="contained"
|
||||
loading={isSubmitting}
|
||||
loadingIndicator="Sending..."
|
||||
loadingIndicator="Отправка..."
|
||||
>
|
||||
Send reset link
|
||||
Отправить ссылку
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</Form>
|
||||
|
|
@ -104,7 +104,7 @@ export function JwtForgotPasswordView() {
|
|||
color="inherit"
|
||||
sx={{ display: 'block', mt: 3, textAlign: 'center' }}
|
||||
>
|
||||
Back to sign in
|
||||
Вернуться ко входу
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@ const ResetPasswordSchema = zod
|
|||
.object({
|
||||
newPassword: zod
|
||||
.string()
|
||||
.min(1, { message: 'Password is required!' })
|
||||
.min(6, { message: 'Password must be at least 6 characters!' }),
|
||||
newPasswordConfirm: zod.string().min(1, { message: 'Please confirm your password!' }),
|
||||
.min(1, { message: 'Введите пароль!' })
|
||||
.min(6, { message: 'Пароль должен содержать не менее 6 символов!' }),
|
||||
newPasswordConfirm: zod.string().min(1, { message: 'Подтвердите пароль!' }),
|
||||
})
|
||||
.refine((data) => data.newPassword === data.newPasswordConfirm, {
|
||||
message: 'Passwords do not match!',
|
||||
message: 'Пароли не совпадают!',
|
||||
path: ['newPasswordConfirm'],
|
||||
});
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ function ResetPasswordContent() {
|
|||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
if (!token) {
|
||||
setErrorMsg('Reset link is missing. Please request a new password reset.');
|
||||
setErrorMsg('Ссылка для сброса отсутствует. Запросите новый сброс пароля.');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
|
@ -72,11 +72,11 @@ function ResetPasswordContent() {
|
|||
newPassword: data.newPassword,
|
||||
newPasswordConfirm: data.newPasswordConfirm,
|
||||
});
|
||||
setSuccessMsg('Password changed successfully. You can now sign in with your new password.');
|
||||
setSuccessMsg('Пароль успешно изменён. Теперь вы можете войти с новым паролем.');
|
||||
setErrorMsg('');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
const msg = error?.response?.data?.message || error?.response?.data?.detail || 'Failed to reset password. The link may have expired — please request a new one.';
|
||||
const msg = error?.response?.data?.error?.message || error?.response?.data?.message || error?.response?.data?.detail || 'Не удалось сбросить пароль. Возможно, ссылка устарела — запросите новую.';
|
||||
setErrorMsg(msg);
|
||||
}
|
||||
});
|
||||
|
|
@ -85,13 +85,13 @@ function ResetPasswordContent() {
|
|||
return (
|
||||
<>
|
||||
<Stack spacing={1.5} sx={{ mb: 5 }}>
|
||||
<Typography variant="h5">Reset password</Typography>
|
||||
<Typography variant="h5">Сброс пароля</Typography>
|
||||
</Stack>
|
||||
<Alert severity="error" sx={{ mb: 3 }}>
|
||||
Reset link is missing. Please follow the link from your email or request a new one.
|
||||
Ссылка для сброса отсутствует. Перейдите по ссылке из письма или запросите новую.
|
||||
</Alert>
|
||||
<Link component={RouterLink} href={paths.auth.jwt.forgotPassword} variant="subtitle2">
|
||||
Request new reset link
|
||||
Запросить новую ссылку
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
|
|
@ -101,13 +101,13 @@ function ResetPasswordContent() {
|
|||
return (
|
||||
<>
|
||||
<Stack spacing={1.5} sx={{ mb: 5 }}>
|
||||
<Typography variant="h5">Reset password</Typography>
|
||||
<Typography variant="h5">Сброс пароля</Typography>
|
||||
</Stack>
|
||||
<Alert severity="success" sx={{ mb: 3 }}>
|
||||
{successMsg}
|
||||
</Alert>
|
||||
<Link component={RouterLink} href={paths.auth.jwt.signIn} variant="subtitle2">
|
||||
Sign in
|
||||
Войти
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
|
|
@ -116,9 +116,9 @@ function ResetPasswordContent() {
|
|||
return (
|
||||
<>
|
||||
<Stack spacing={1.5} sx={{ mb: 5 }}>
|
||||
<Typography variant="h5">Set new password</Typography>
|
||||
<Typography variant="h5">Новый пароль</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
Enter your new password below.
|
||||
Введите новый пароль ниже.
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
|
|
@ -132,8 +132,8 @@ function ResetPasswordContent() {
|
|||
<Stack spacing={3}>
|
||||
<Field.Text
|
||||
name="newPassword"
|
||||
label="New password"
|
||||
placeholder="6+ characters"
|
||||
label="Новый пароль"
|
||||
placeholder="6+ символов"
|
||||
type={newPassword.value ? 'text' : 'password'}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
InputProps={{
|
||||
|
|
@ -149,8 +149,8 @@ function ResetPasswordContent() {
|
|||
|
||||
<Field.Text
|
||||
name="newPasswordConfirm"
|
||||
label="Confirm new password"
|
||||
placeholder="6+ characters"
|
||||
label="Подтвердите пароль"
|
||||
placeholder="6+ символов"
|
||||
type={newPasswordConfirm.value ? 'text' : 'password'}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
InputProps={{
|
||||
|
|
@ -171,9 +171,9 @@ function ResetPasswordContent() {
|
|||
type="submit"
|
||||
variant="contained"
|
||||
loading={isSubmitting}
|
||||
loadingIndicator="Saving..."
|
||||
loadingIndicator="Сохранение..."
|
||||
>
|
||||
Save new password
|
||||
Сохранить пароль
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</Form>
|
||||
|
|
@ -185,7 +185,7 @@ function ResetPasswordContent() {
|
|||
color="inherit"
|
||||
sx={{ display: 'block', mt: 3, textAlign: 'center' }}
|
||||
>
|
||||
Back to sign in
|
||||
Вернуться ко входу
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
|
|
@ -195,7 +195,7 @@ function ResetPasswordContent() {
|
|||
|
||||
export function JwtResetPasswordView() {
|
||||
return (
|
||||
<Suspense fallback={<Typography variant="body2">Loading...</Typography>}>
|
||||
<Suspense fallback={<Typography variant="body2">Загрузка...</Typography>}>
|
||||
<ResetPasswordContent />
|
||||
</Suspense>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ export function JwtSignUpView() {
|
|||
router.replace(paths.dashboard.root);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
const msg = error?.response?.data?.message || error?.response?.data?.detail || (error instanceof Error ? error.message : 'Ошибка регистрации. Проверьте введённые данные.');
|
||||
const msg = error?.response?.data?.error?.message || error?.response?.data?.message || error?.response?.data?.detail || (error instanceof Error ? error.message : 'Ошибка регистрации. Проверьте введённые данные.');
|
||||
setErrorMsg(msg);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ function VerifyEmailContent() {
|
|||
useEffect(() => {
|
||||
if (!token) {
|
||||
setStatus('error');
|
||||
setMessage('Verification link is missing. Please check your email or request a new one.');
|
||||
setMessage('Ссылка для подтверждения отсутствует. Проверьте письмо или запросите новое.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -37,16 +37,16 @@ function VerifyEmailContent() {
|
|||
if (cancelled) return;
|
||||
if (res?.success) {
|
||||
setStatus('success');
|
||||
setMessage('Email successfully verified. You can now sign in.');
|
||||
setMessage('Email успешно подтверждён. Теперь вы можете войти.');
|
||||
} else {
|
||||
setStatus('error');
|
||||
setMessage(res?.message || 'Failed to verify email.');
|
||||
setMessage(res?.error?.message || res?.message || 'Не удалось подтвердить email.');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (cancelled) return;
|
||||
setStatus('error');
|
||||
const msg = err?.response?.data?.message || err?.response?.data?.detail || 'Invalid or expired link. Please request a new verification email.';
|
||||
const msg = err?.response?.data?.error?.message || err?.response?.data?.message || err?.response?.data?.detail || 'Ссылка недействительна или устарела. Запросите новое письмо с подтверждением.';
|
||||
setMessage(msg);
|
||||
});
|
||||
|
||||
|
|
@ -58,14 +58,14 @@ function VerifyEmailContent() {
|
|||
return (
|
||||
<>
|
||||
<Stack spacing={1.5} sx={{ mb: 5 }}>
|
||||
<Typography variant="h5">Email verification</Typography>
|
||||
<Typography variant="h5">Подтверждение email</Typography>
|
||||
</Stack>
|
||||
|
||||
{status === 'loading' && (
|
||||
<Stack alignItems="center" sx={{ py: 4 }}>
|
||||
<CircularProgress />
|
||||
<Typography variant="body2" sx={{ mt: 2, color: 'text.secondary' }}>
|
||||
Verifying your email...
|
||||
Подтверждение email...
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
|
|
@ -76,7 +76,7 @@ function VerifyEmailContent() {
|
|||
{message}
|
||||
</Alert>
|
||||
<Link component={RouterLink} href={paths.auth.jwt.signIn} variant="subtitle2">
|
||||
Sign in to your account
|
||||
Войти в аккаунт
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
|
|
@ -88,10 +88,10 @@ function VerifyEmailContent() {
|
|||
</Alert>
|
||||
<Stack spacing={1}>
|
||||
<Link component={RouterLink} href={paths.auth.jwt.signIn} variant="subtitle2">
|
||||
Back to sign in
|
||||
Вернуться ко входу
|
||||
</Link>
|
||||
<Link component={RouterLink} href={paths.auth.jwt.signUp} variant="body2" color="text.secondary">
|
||||
Create a new account
|
||||
Создать новый аккаунт
|
||||
</Link>
|
||||
</Stack>
|
||||
</>
|
||||
|
|
@ -104,7 +104,7 @@ function VerifyEmailContent() {
|
|||
|
||||
export function JwtVerifyEmailView() {
|
||||
return (
|
||||
<Suspense fallback={<Typography variant="body2">Loading...</Typography>}>
|
||||
<Suspense fallback={<Typography variant="body2">Загрузка...</Typography>}>
|
||||
<VerifyEmailContent />
|
||||
</Suspense>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue