add reset password
This commit is contained in:
@@ -2,7 +2,6 @@ import { useTranslations } from 'next-intl'
|
|||||||
import LogoIcon from '@/app/ui/logo-icon';
|
import LogoIcon from '@/app/ui/logo-icon';
|
||||||
import styles from '@/app/styles/module/login.module.scss';
|
import styles from '@/app/styles/module/login.module.scss';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import LanguageSwitcher from '@/app/components/LanguageSwitcher';
|
|
||||||
import ForgotPasswordForm from '@/app/ui/forms/forgot-password-form';
|
import ForgotPasswordForm from '@/app/ui/forms/forgot-password-form';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
@@ -10,9 +9,6 @@ export default function Page() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles['login-container-wrapper']}`}>
|
<div className={`${styles['login-container-wrapper']}`}>
|
||||||
<div className={`${styles['login-container-language']}`}>
|
|
||||||
<LanguageSwitcher />
|
|
||||||
</div>
|
|
||||||
<div className={`${styles['login-container']}`}>
|
<div className={`${styles['login-container']}`}>
|
||||||
<div className={`${styles['logo']}`}>
|
<div className={`${styles['logo']}`}>
|
||||||
<LogoIcon />
|
<LogoIcon />
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import LogoIcon from '@/app/ui/logo-icon';
|
|||||||
import styles from '@/app/styles/module/login.module.scss'
|
import styles from '@/app/styles/module/login.module.scss'
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import RegisterForm from '@/app/ui/forms/register-form';
|
import RegisterForm from '@/app/ui/forms/register-form';
|
||||||
import LanguageSwitcher from '@/app/components/LanguageSwitcher';
|
|
||||||
import {useTranslations} from 'next-intl';
|
import {useTranslations} from 'next-intl';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
@@ -10,9 +9,6 @@ export default function Page() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles['login-container-wrapper']}`}>
|
<div className={`${styles['login-container-wrapper']}`}>
|
||||||
<div className={`${styles['login-container-language']}`}>
|
|
||||||
<LanguageSwitcher />
|
|
||||||
</div>
|
|
||||||
<div className={`${styles['login-container']}`}>
|
<div className={`${styles['login-container']}`}>
|
||||||
<div className={`${styles['logo']}`}>
|
<div className={`${styles['logo']}`}>
|
||||||
<LogoIcon />
|
<LogoIcon />
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { useTranslations } from 'next-intl'
|
||||||
|
import LogoIcon from '@/app/ui/logo-icon';
|
||||||
|
import styles from '@/app/styles/module/login.module.scss';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import ResetPasswordForm from '@/app/ui/forms/reset-password-form';
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const t = useTranslations('Login-register-form');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles['login-container-wrapper']}`}>
|
||||||
|
<div className={`${styles['login-container']}`}>
|
||||||
|
<div className={`${styles['logo']}`}>
|
||||||
|
<LogoIcon />
|
||||||
|
<h1>NO COPY</h1>
|
||||||
|
<p>
|
||||||
|
{t('recover-password')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ResetPasswordForm />
|
||||||
|
|
||||||
|
<div className={`${styles['register-link']}`}>
|
||||||
|
<p>{t('already-have-an-account')}? <Link href="login">{t('login')}</Link></p>
|
||||||
|
</div>
|
||||||
|
<div className={`${styles['register-link']}`}>
|
||||||
|
<p>{t('no-account')}?
|
||||||
|
<Link href="register"> {t('register')}</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -341,4 +341,152 @@ export async function confirmEmail(
|
|||||||
console.log('confirmEmail');
|
console.log('confirmEmail');
|
||||||
const emailConfirm = formData.get('emailConfirm') as string || '';
|
const emailConfirm = formData.get('emailConfirm') as string || '';
|
||||||
console.log(emailConfirm);
|
console.log(emailConfirm);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetPassword(
|
||||||
|
state: any | undefined,
|
||||||
|
formData: any,
|
||||||
|
) {
|
||||||
|
const email = formData.get('email');
|
||||||
|
console.log('resetPassword');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/v1/data`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 20010,
|
||||||
|
message_body: {
|
||||||
|
email: email,
|
||||||
|
action: "resetPassword"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
|
||||||
|
const parsed = await response.json();
|
||||||
|
console.log(parsed);
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw parsed.message_body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
redirect(`/reset-password?email=${email}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FormStateConfirmPassword = {
|
||||||
|
previousState: {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
confirm_password: string;
|
||||||
|
verifyToken: string;
|
||||||
|
},
|
||||||
|
error: Record<string, string> | null
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function confirmPassword(
|
||||||
|
state: FormStateConfirmPassword | undefined,
|
||||||
|
formData: any,
|
||||||
|
): Promise<FormStateConfirmPassword | undefined> {
|
||||||
|
const email = formData.get('email');
|
||||||
|
const password = formData.get('password');
|
||||||
|
const confirm_password = formData.get('confirm_password');
|
||||||
|
const verifyToken = formData.get('verify-token');
|
||||||
|
console.log('confirmPassword');
|
||||||
|
|
||||||
|
const SignupFormSchema = await getSignupFormSchema('resetPassword');
|
||||||
|
|
||||||
|
const dataToValidate = {
|
||||||
|
password,
|
||||||
|
confirm_password
|
||||||
|
};
|
||||||
|
|
||||||
|
const validatedFields = SignupFormSchema.safeParse(dataToValidate);
|
||||||
|
|
||||||
|
console.log(validatedFields);
|
||||||
|
|
||||||
|
if (!validatedFields.success) {
|
||||||
|
const errors: Record<string, string> = {}
|
||||||
|
JSON.parse(validatedFields.error.message).forEach((obj: {
|
||||||
|
message: string,
|
||||||
|
path: string
|
||||||
|
}) => {
|
||||||
|
if (errors[obj.path[0]]) {
|
||||||
|
errors[obj.path[0]] = `${errors[obj.path[0]]}&${obj.message}`;
|
||||||
|
} else {
|
||||||
|
errors[obj.path[0]] = obj.message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
previousState: {
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirm_password,
|
||||||
|
verifyToken
|
||||||
|
},
|
||||||
|
error: errors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/v1/data`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 20010,
|
||||||
|
message_body: {
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
verifyToken: verifyToken,
|
||||||
|
action: "confirmVerification"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
|
||||||
|
const parsed = await response.json();
|
||||||
|
console.log(parsed);
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
await createSession(parsed.message_body.authToken, email as string);
|
||||||
|
} else {
|
||||||
|
throw parsed.message_body;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw (`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const typedError = error as any;
|
||||||
|
const errors: Record<string, string> = {};
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return {
|
||||||
|
previousState: {
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirm_password,
|
||||||
|
verifyToken
|
||||||
|
},
|
||||||
|
error: errors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
redirect('/pages/dashboard');
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
import { createValidationSchema } from '@/app/lib/validation-config-parser';
|
import { createValidationSchema, createValidationSchemaForPssword } from '@/app/lib/validation-config-parser';
|
||||||
import validationConfig from '@/app/lib/validation-config.json';
|
import validationConfig from '@/app/lib/validation-config.json';
|
||||||
|
|
||||||
export type FormState =
|
export type FormState =
|
||||||
@@ -38,7 +38,7 @@ const defaultConfig = validationConfig as ValidationConfig;
|
|||||||
let cachedSignupSchema: any = null;
|
let cachedSignupSchema: any = null;
|
||||||
let schemaPromise: Promise<any> | null = null;
|
let schemaPromise: Promise<any> | null = null;
|
||||||
|
|
||||||
async function loadConfigFromBackend(accountType: 'b2b' | 'b2c'): Promise<any> {
|
async function loadConfigFromBackend(accountType?: 'b2b' | 'b2c'): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/validation-config');
|
const response = await fetch('/api/validation-config');
|
||||||
if (!response.ok) throw new Error('Failed to load config');
|
if (!response.ok) throw new Error('Failed to load config');
|
||||||
@@ -50,15 +50,19 @@ async function loadConfigFromBackend(accountType: 'b2b' | 'b2c'): Promise<any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSignupFormSchema(accountType: 'b2b' | 'b2c') {
|
export async function getSignupFormSchema(schemaType?: 'b2b' | 'b2c' | 'resetPassword') {
|
||||||
if (cachedSignupSchema) {
|
if (cachedSignupSchema) {
|
||||||
return cachedSignupSchema;
|
return cachedSignupSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!schemaPromise) {
|
if (!schemaPromise) {
|
||||||
schemaPromise = (async () => {
|
schemaPromise = (async () => {
|
||||||
const config = await loadConfigFromBackend(accountType);
|
const config = await loadConfigFromBackend();
|
||||||
cachedSignupSchema = createValidationSchema(config, accountType);
|
if (schemaType === 'resetPassword') {
|
||||||
|
cachedSignupSchema = createValidationSchemaForPssword(config);
|
||||||
|
} else {
|
||||||
|
cachedSignupSchema = createValidationSchema(config, schemaType);
|
||||||
|
}
|
||||||
|
|
||||||
return cachedSignupSchema;
|
return cachedSignupSchema;
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -163,79 +163,63 @@ export function createValidationSchema(config: ValidationConfig, accountType?: '
|
|||||||
schema.password = passwordSchema;
|
schema.password = passwordSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
// companyName (опциональное поле), надо будет удалить потом
|
|
||||||
/* if (config.companyName) {
|
|
||||||
let companySchema = z.string();
|
|
||||||
|
|
||||||
// Если поле опциональное, добавляем .optional().nullable()
|
|
||||||
if (config.companyName.optional?.value) {
|
|
||||||
companySchema = companySchema
|
|
||||||
.optional()
|
|
||||||
.nullable()
|
|
||||||
.transform((val) => (val === null || val === undefined ? '' : val))
|
|
||||||
.refine(
|
|
||||||
(value) => {
|
|
||||||
// Для пустого значения пропускаем проверки
|
|
||||||
if (!value || value.trim().length === 0) return true;
|
|
||||||
|
|
||||||
// Проверка минимальной длины
|
|
||||||
const minLength = config.companyName?.minSize?.value || 2;
|
|
||||||
return value.trim().length >= minLength;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: config.companyName.minSize?.message || 'register-error-company-name-min'
|
|
||||||
}
|
|
||||||
) as unknown as z.ZodString;
|
|
||||||
} else {
|
|
||||||
// Если поле обязательное
|
|
||||||
if (config.companyName.minSize) {
|
|
||||||
companySchema = companySchema.min(config.companyName.minSize.value, {
|
|
||||||
error: config.companyName.minSize.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Максимальная длина (применяется всегда)
|
|
||||||
if (config.companyName.maxSize) {
|
|
||||||
companySchema = companySchema.refine(
|
|
||||||
(value) => {
|
|
||||||
if (config.companyName?.optional?.value && (!value || value.trim().length === 0)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const maxLength = config.companyName?.maxSize?.value || 200;
|
|
||||||
return (value || '').trim().length <= maxLength;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: config.companyName.maxSize.message
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Допустимые символы
|
|
||||||
if (config.companyName.allowedSymbols?.value) {
|
|
||||||
companySchema = companySchema.refine(
|
|
||||||
(value) => {
|
|
||||||
if (config.companyName?.optional?.value && (!value || value.trim().length === 0)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const regex = new RegExp(config.companyName.allowedSymbols!.value);
|
|
||||||
return regex.test(value || '');
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: config.companyName.allowedSymbols.message
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
companySchema = companySchema.transform((val) => val?.trim() || '') as unknown as z.ZodString;
|
|
||||||
|
|
||||||
schema.companyName = companySchema;
|
|
||||||
} */
|
|
||||||
|
|
||||||
// Дополнительные поля для формы регистрации
|
// Дополнительные поля для формы регистрации
|
||||||
schema.confirm_password = z.string();
|
schema.confirm_password = z.string();
|
||||||
schema.agree = z.string().min(2, { error: 'register-error-agree' });
|
schema.agree = z.string().min(2, { error: 'register-error-agree' });
|
||||||
|
|
||||||
|
return z
|
||||||
|
.object(schema)
|
||||||
|
.refine((data) => data.password === data.confirm_password, {
|
||||||
|
message: 'repeat-password',
|
||||||
|
path: ['confirm_password'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createValidationSchemaForPssword(config: ValidationConfig) {
|
||||||
|
const schema: Record<string, any> = {};
|
||||||
|
|
||||||
|
// password
|
||||||
|
if (config.password) {
|
||||||
|
let passwordSchema = z
|
||||||
|
.string()
|
||||||
|
.min(config.password.minSize?.value || 8, {
|
||||||
|
error: config.password.minSize?.message || 'register-error-password-symbols'
|
||||||
|
})
|
||||||
|
.max(config.password.maxSize?.value || 124, {
|
||||||
|
error: config.password.maxSize?.message || 'register-error-max-password'
|
||||||
|
})
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
// Проверка допустимых символов
|
||||||
|
if (config.password.allowedSymbols?.value) {
|
||||||
|
passwordSchema = passwordSchema.regex(
|
||||||
|
new RegExp(config.password.allowedSymbols.value),
|
||||||
|
{ message: config.password.allowedSymbols.message }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка наличия цифры
|
||||||
|
if (config.password.requiredDigit?.value) {
|
||||||
|
passwordSchema = passwordSchema.regex(
|
||||||
|
/[0-9]/,
|
||||||
|
{ message: config.password.requiredDigit.message }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка наличия буквы
|
||||||
|
if (config.password.requiredLetter?.value) {
|
||||||
|
passwordSchema = passwordSchema.regex(
|
||||||
|
/[a-zA-Zа-яёА-ЯЁ]/,
|
||||||
|
{ message: config.password.requiredLetter.message }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
schema.password = passwordSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Дополнительные поля для формы регистрации
|
||||||
|
schema.confirm_password = z.string();
|
||||||
|
|
||||||
return z
|
return z
|
||||||
.object(schema)
|
.object(schema)
|
||||||
.refine((data) => data.password === data.confirm_password, {
|
.refine((data) => data.password === data.confirm_password, {
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ import styles from '@/app/styles/module/login.module.scss'
|
|||||||
import { useActionState } from 'react';
|
import { useActionState } from 'react';
|
||||||
|
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
|
import {resetPassword} from '@/app/actions/auth';
|
||||||
|
|
||||||
export default function ForgotPasswordForm() {
|
export default function ForgotPasswordForm() {
|
||||||
const [state, formAction, isPending] = useActionState(
|
const [state, formAction, isPending] = useActionState(
|
||||||
() => {
|
resetPassword,
|
||||||
console.log("test");
|
|
||||||
},
|
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
const t = useTranslations('Login-register-form');
|
const t = useTranslations('Login-register-form');
|
||||||
@@ -27,9 +26,9 @@ export default function ForgotPasswordForm() {
|
|||||||
className={`${styles['form-input']}`}
|
className={`${styles['form-input']}`}
|
||||||
placeholder={t('enter-email')}
|
placeholder={t('enter-email')}
|
||||||
/>
|
/>
|
||||||
{/* {state?.error?.email && (
|
{/* {state === 'error' && (
|
||||||
<p className={`${styles['form-error']}`}>
|
<p className={`${styles['form-error']}`}>
|
||||||
{t(state?.error?.email)}
|
Восстановление пароля не работает
|
||||||
</p>
|
</p>
|
||||||
)} */}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
@@ -38,15 +37,8 @@ export default function ForgotPasswordForm() {
|
|||||||
{t(state?.error.server)}
|
{t(state?.error.server)}
|
||||||
</p>
|
</p>
|
||||||
)} */}
|
)} */}
|
||||||
<div className={`${styles['form-options']}`}>
|
|
||||||
<div className={`${styles['form-checkbox']}`}>
|
|
||||||
<small>
|
|
||||||
функционал не реализован
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="submit" className={`${styles['btn']}`}>
|
<button type="submit" className={`${styles['btn']}`}>
|
||||||
{t('sign-in')}
|
Восстановить пароль
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,208 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import styles from '@/app/styles/module/login.module.scss'
|
||||||
|
import { useActionState, useEffect, useRef, useState, ChangeEvent } from 'react';
|
||||||
|
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { confirmPassword, FormStateConfirmPassword } from '@/app/actions/auth';
|
||||||
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
import { IconEye } from '@/app/ui/icons/icons';
|
||||||
|
import { SignupFormSchema } from '@/app/actions/definitions';
|
||||||
|
import * as z from 'zod';
|
||||||
|
|
||||||
|
export default function ResetPasswordForm() {
|
||||||
|
const [state, formAction, isPending] = useActionState(
|
||||||
|
confirmPassword,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
const passwordRef = useRef<HTMLInputElement>(null);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
|
const [formState, setFormState] = useState<FormStateConfirmPassword | undefined>(undefined);
|
||||||
|
const t = useTranslations('Login-register-form');
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const email = searchParams.get('email');
|
||||||
|
console.log(email);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state) {
|
||||||
|
setFormState(state);
|
||||||
|
}
|
||||||
|
}, [state])
|
||||||
|
|
||||||
|
function showPassowrd(target: 'password' | 'confirm-password') {
|
||||||
|
if (target === 'password') {
|
||||||
|
setShowPassword(!showPassword);
|
||||||
|
} else {
|
||||||
|
setShowConfirmPassword(!showConfirmPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateField(
|
||||||
|
fieldName: string,
|
||||||
|
value: string,
|
||||||
|
) {
|
||||||
|
switch (fieldName) {
|
||||||
|
case 'confirm_password':
|
||||||
|
const baseSchema = SignupFormSchema.shape.confirm_password;
|
||||||
|
const baseResult = baseSchema.safeParse(value);
|
||||||
|
|
||||||
|
if (!baseResult.success) {
|
||||||
|
return baseResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPassword = passwordRef.current?.value || '';
|
||||||
|
if (value !== currentPassword) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: new z.ZodError([
|
||||||
|
{
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: 'repeat-password',
|
||||||
|
path: ['confirm_password']
|
||||||
|
}
|
||||||
|
])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true, data: value };
|
||||||
|
case 'password':
|
||||||
|
const schema = SignupFormSchema.shape[fieldName];
|
||||||
|
return schema.safeParse(value);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown field: ${fieldName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeHandler(e: ChangeEvent<HTMLInputElement>): void {
|
||||||
|
const fieldName = e.target.name;
|
||||||
|
if (fieldName !== 'phone') {
|
||||||
|
e.target.value = e.target.value.replace(/([^a-zA-Zа-яёА-ЯЁ0-9@.!#$%&"'*+/=?^_{|}~\-\s])/g, '');
|
||||||
|
}
|
||||||
|
const validatedField = validateField(fieldName, e.target.value);
|
||||||
|
|
||||||
|
if (validatedField.success) {
|
||||||
|
setFormState(prevState => {
|
||||||
|
if (!prevState?.error || !prevState?.error[fieldName]) {
|
||||||
|
return prevState;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { [fieldName]: _, ...newError } = prevState.error;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prevState,
|
||||||
|
error: newError
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form action={formAction}>
|
||||||
|
<div className={`${styles['form-group']}`}>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
className={`${styles['form-input']}`}
|
||||||
|
placeholder={t('enter-email')}
|
||||||
|
defaultValue={email ? email : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={`${styles['form-group']}`}>
|
||||||
|
<label className={`${styles['form-label']} ${styles['required']}`}>
|
||||||
|
Код восстановления
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="readOnly"
|
||||||
|
id="verify-token"
|
||||||
|
name="verify-token"
|
||||||
|
className={`${styles['form-input']}`}
|
||||||
|
placeholder={'Код восстановления'}
|
||||||
|
defaultValue={formState?.previousState?.verifyToken ? formState?.previousState?.verifyToken : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={`${styles['form-group']}`}>
|
||||||
|
<label className={`${styles['form-label']} ${styles['required']}`}>
|
||||||
|
{t('password')}
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
className={`${styles['password-wrapper']}`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
ref={passwordRef}
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
className={`${styles['form-input']} ${styles['password']}`}
|
||||||
|
placeholder={t('password-placeholder')}
|
||||||
|
defaultValue={formState?.previousState?.password ? formState?.previousState?.password : ''}
|
||||||
|
onChange={onChangeHandler}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
showPassowrd('password')
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
className={`show-password-button ${showPassword ? 'show' : ''}`}
|
||||||
|
>
|
||||||
|
<IconEye />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{formState?.error?.password && (
|
||||||
|
<p className="text-sm text-red-500">
|
||||||
|
{
|
||||||
|
formState?.error?.password.split('&').map((e, index) => {
|
||||||
|
return (
|
||||||
|
<span key={index}>
|
||||||
|
{t(e)}
|
||||||
|
<br />
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={`${styles['form-group']}`}>
|
||||||
|
<label className={`${styles['form-label']} ${styles['required']}`}>
|
||||||
|
{t('confirm-password')}
|
||||||
|
</label>
|
||||||
|
<div className={`${styles['password-wrapper']}`}>
|
||||||
|
<input
|
||||||
|
type={showConfirmPassword ? "text" : "password"}
|
||||||
|
id="confirm_password"
|
||||||
|
name="confirm_password"
|
||||||
|
className={`${styles['form-input']} ${styles['password']}`}
|
||||||
|
placeholder={t('repeat-password')}
|
||||||
|
defaultValue={formState?.previousState?.confirm_password ? formState?.previousState?.confirm_password : ''}
|
||||||
|
onChange={onChangeHandler}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
showPassowrd('confirm-password');
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
className={`show-password-button ${showConfirmPassword ? 'show' : ''}`}
|
||||||
|
>
|
||||||
|
<IconEye />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{formState?.error?.confirm_password && (
|
||||||
|
<p className="text-sm text-red-500">
|
||||||
|
{t(formState?.error?.confirm_password)}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{/* {state?.error.server && (
|
||||||
|
<p className={`${styles['form-error']}`}>
|
||||||
|
{t(state?.error.server)}
|
||||||
|
</p>
|
||||||
|
)} */}
|
||||||
|
<button type="submit" className={`${styles['btn']}`}>
|
||||||
|
Изменить пароль
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user