From aff7ff5dd1762978ef30dde39038ef712ee22679 Mon Sep 17 00:00:00 2001 From: Olek <149747215+Primis1@users.noreply.github.com> Date: Sat, 3 Aug 2024 01:13:56 -0400 Subject: [PATCH] add zod shcemes, separate regex into single file, change form types --- src/shared/ui/form/form.tsx | 4 +- src/shared/ui/form/types.ts | 4 +- .../form-change-password-schema.ts | 58 ++++---- .../validation-schemas/form-login-schema.ts | 50 +++---- .../form-recovery-password-schema.ts | 34 ++--- .../validation-schemas/form-signup-schema.ts | 137 +++++++++--------- src/utils/regex-consts.ts | 8 + 7 files changed, 149 insertions(+), 146 deletions(-) create mode 100644 src/utils/regex-consts.ts diff --git a/src/shared/ui/form/form.tsx b/src/shared/ui/form/form.tsx index c83a8d99..3d7450ee 100644 --- a/src/shared/ui/form/form.tsx +++ b/src/shared/ui/form/form.tsx @@ -1,7 +1,7 @@ 'use client'; import React, { FC } from 'react'; -import { yupResolver } from '@hookform/resolvers/yup'; +import { zodResolver } from '@hookform/resolvers/zod'; import { FormProvider, useForm } from 'react-hook-form'; import styles from './form.module.scss'; import type { FormProps } from './types'; @@ -15,7 +15,7 @@ export const Form: FC = ({ }) => { const methods = useForm({ shouldUnregister: true, - resolver: schema ? yupResolver(schema) : undefined, + resolver: schema ? zodResolver(schema) : undefined, }); const { diff --git a/src/shared/ui/form/types.ts b/src/shared/ui/form/types.ts index 56c7f63f..86551eff 100644 --- a/src/shared/ui/form/types.ts +++ b/src/shared/ui/form/types.ts @@ -1,10 +1,10 @@ import { HTMLAttributes, ReactNode, FormEventHandler } from 'react'; -import * as yup from 'yup'; +import * as z from 'zod'; import { IUser } from '@/services/models/IUser'; export type FormProps = HTMLAttributes & { children: ReactNode; extraClass?: string; - schema?: yup.AnyObjectSchema; + schema?: z.ZodEffects | z.AnyZodObject; onSubmit: FormEventHandler & ((data: IUser) => void); }; diff --git a/src/shared/utils/validation-schemas/form-change-password-schema.ts b/src/shared/utils/validation-schemas/form-change-password-schema.ts index 072f74db..efdec475 100644 --- a/src/shared/utils/validation-schemas/form-change-password-schema.ts +++ b/src/shared/utils/validation-schemas/form-change-password-schema.ts @@ -1,36 +1,32 @@ -import * as yup from 'yup'; +import { z } from 'zod'; +import { passwordRegex } from '@/utils/regex-consts'; -const FormChangePasswordSchema = yup.object().shape({ - password: yup - .string() - .required('Поле обязательно для заполнения') - .min(8, 'Длина поля от 8 до 20 символов') - .max(20, 'Длина поля от 8 до 20 символов') - .matches( - /[a-zA-Zа-яА-Я0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]$/, - 'Проверьте правильность ввода' - ), +const FormChangePasswordSchema = z + .object({ + password: z + .string() + .min(1, { message: 'Поле обязательно для заполнения' }) + .min(8, { message: 'Длина поля от 8 до 20 символов' }) + .max(20, { message: 'Длина поля от 8 до 20 символов' }) + .regex(/[u00AE]/, 'Проверьте правильность ввода'), - newPassword: yup - .string() - .required('Поле обязательно для заполнения') - .min(8, 'Длина поля от 8 до 20 символов') - .max(20, 'Длина поля от 8 до 20 символов') - .matches( - /[a-zA-Zа-яА-Я0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]$/, - 'Проверьте правильность ввода' - ), + newPassword: z + .string() + .min(1, { message: 'Поле обязательно для заполнения' }) + .min(8, { message: 'Длина поля от 8 до 20 символов' }) + .max(20, { message: 'Длина поля от 8 до 20 символов' }) + .regex(passwordRegex, 'Проверьте правильность ввода'), - repeatNewPassword: yup - .string() - .required('Поле обязательно для заполнения') - .min(8, 'Проверьте правильность ввода') - .max(20, 'Проверьте правильность ввода') - .matches( - /[a-zA-Zа-яА-Я0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]$/, - 'Проверьте правильность ввода' - ) - .oneOf([yup.ref('newPassword')], 'Пароль не совпадает с введенным'), -}); + reapeatPassword: z + .string() + .min(1, { message: 'Поле обязательно для заполнения' }) + .min(8, { message: 'Длина поля от 8 до 20 символов' }) + .max(20, { message: 'Длина поля от 8 до 20 символов' }) + .regex(passwordRegex, 'Проверьте правильность ввода'), + }) + .refine((e) => e.newPassword === e.reapeatPassword, { + message: 'Пароль не совпадает с введенным', + path: ['reapeatPassword'], + }); export default FormChangePasswordSchema; diff --git a/src/shared/utils/validation-schemas/form-login-schema.ts b/src/shared/utils/validation-schemas/form-login-schema.ts index d3eabf4c..2cc295e6 100644 --- a/src/shared/utils/validation-schemas/form-login-schema.ts +++ b/src/shared/utils/validation-schemas/form-login-schema.ts @@ -1,32 +1,32 @@ -import * as yup from 'yup'; +import { z } from 'zod'; +import { + generalEmailRegex, + noSpecialCharEmailRegex, + limitedCharEmailRegex, + passwordRegex, +} from '@/utils/regex-consts'; -const schema = yup.object().shape({ - email: yup +const schema = z.object({ + email: z .string() - .required('Поле обязательно для заполнения') - .email('Проверьте правильность ввода') + .min(1, { message: 'Поле обязательно для заполнения' }) + .email({ message: 'Проверьте правильность ввода' }) + .min(6, { message: 'Длина поля от 6 до 256 символов' }) + .max(256, { message: 'Длина поля от 6 до 256 символов' }) .trim() - .min(6, 'Проверьте правильность ввода') - .max(256, 'Проверьте правильность ввода') - .matches( - /^[a-zA-Z0-9][a-zA-Z0-9_.-]*[a-zA-Z0-9]@[a-zA-Z0-9-]+(?:\.[a-zA-Zа-яА-Я0-9-]+)*\.[a-zA-Zа-яА-Я-]{2,}$/, - 'Проверьте правильность ввода' - ) - .matches( - /^[^/[!"#$%&'()*+,/:;<=>?[\\\]^`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]*$/, - 'Проверьте правильность ввода' - ) - .matches(/^[a-zA-Zа-яА-Я0-9-._@]*$/, 'Только буквы (A-z, А-я), цифры (0-9)') - .max(256, 'Проверьте правильность ввода'), - password: yup + .regex(generalEmailRegex, { message: 'Проверьте правильность ввода' }) + .regex(noSpecialCharEmailRegex, { message: 'Проверьте правильность ввода' }) + .regex(limitedCharEmailRegex, { + message: 'Только буквы (A-z, А-я), цифры (0-9)', + }), + + password: z .string() .trim() - .required('Поле обязательно для заполнения') - .min(8, 'Проверьте правильность ввода') - .max(20, 'Проверьте правильность ввода') - .matches( - /[a-zA-Zа-яА-Я0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]$/, - 'Проверьте правильность ввода' - ), + .min(1, { message: 'Поле обязательно для заполнения' }) + .min(8, { message: 'Длина поля от 8 до 20 символов' }) + .max(20, { message: 'Длина поля от 8 до 20 символов' }) + .regex(passwordRegex, { message: 'Проверьте правильность ввода' }), }); + export default schema; diff --git a/src/shared/utils/validation-schemas/form-recovery-password-schema.ts b/src/shared/utils/validation-schemas/form-recovery-password-schema.ts index be5f0e3a..49f30198 100644 --- a/src/shared/utils/validation-schemas/form-recovery-password-schema.ts +++ b/src/shared/utils/validation-schemas/form-recovery-password-schema.ts @@ -1,22 +1,22 @@ -import * as yup from 'yup'; +import { z } from 'zod'; +import { + generalEmailRegex, + noSpecialCharEmailRegex, + limitedCharEmailRegex, +} from '@/utils/regex-consts'; -const schema = yup.object().shape({ - email: yup +const schema = z.object({ + email: z .string() - .required('Поле обязательно для заполнения') - .email('Проверьте правильность ввода') + .min(1, { message: 'Поле обязательно для заполнения' }) + .email({ message: 'Проверьте правильность ввода' }) + .min(6, { message: 'Проверьте правильность ввода' }) + .max(256, { message: 'Проверьте правильность ввода' }) .trim() - .min(6, 'Проверьте правильность ввода') - .max(256, 'Проверьте правильность ввода') - .matches( - /^[a-zA-Z0-9][a-zA-Z0-9_.-]*[a-zA-Z0-9]@[a-zA-Z0-9-]+(?:\.[a-zA-Zа-яА-Я0-9-]+)*\.[a-zA-Zа-яА-Я-]{2,}$/, - 'Проверьте правильность ввода' - ) - .matches( - /^[^/[!"#$%&'()*+,/:;<=>?[\\\]^`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]*$/, - 'Проверьте правильность ввода' - ) - .matches(/^[a-zA-Zа-яА-Я0-9-._@]*$/, 'Только буквы (A-z, А-я), цифры (0-9)') - .max(256, 'Проверьте правильность ввода'), + .regex(generalEmailRegex, { message: 'Проверьте правильность ввода' }) + .regex(noSpecialCharEmailRegex, { message: 'Проверьте правильность ввода' }) + .regex(limitedCharEmailRegex, { + message: 'Только буквы (A-z, А-я), цифры (0-9)', + }), }); export default schema; diff --git a/src/shared/utils/validation-schemas/form-signup-schema.ts b/src/shared/utils/validation-schemas/form-signup-schema.ts index 0755c98d..02e6ea91 100644 --- a/src/shared/utils/validation-schemas/form-signup-schema.ts +++ b/src/shared/utils/validation-schemas/form-signup-schema.ts @@ -1,70 +1,69 @@ -import * as yup from 'yup'; +import { + generalEmailRegex, + noSpecialCharEmailRegex, + limitedCharEmailRegex, + passwordRegex, +} from '@/utils/regex-consts'; +import { z } from 'zod'; -const schema = yup.object().shape({ - email: yup - .string() - .required('Поле обязательно для заполнения') - .min(6, 'Длина поля от 6 до 256 символов') - .max(256, 'Длина поля от 6 до 256 символов') - .trim() - .matches( - /^[^/[!"#$%&'()*+,/:;<=>?[\\\]^`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]*$/, - 'Только дефис, точка, нижнее подчеркивание' - ) - .matches(/^[a-zA-Zа-яА-Я0-9-._@]*$/, 'Только буквы (A-z, А-я), цифры (0-9)') - .email('Введите корректный E-mail') - .matches( - /^[a-zA-Z0-9][a-zA-Z0-9_.-]*[a-zA-Z0-9]@[a-zA-Z0-9_-]+(?:\.[a-zA-Zа-яА-Я0-9_-]+)*\.[a-zA-Zа-яА-Я_-]{2,}$/, - 'Введите корректный E-mail' - ), - username: yup - .string() - .required('Поле обязательно для заполнения') - .trim() - .min(2, 'Длина поля от 2 до 30 символов') - .max(30, 'Длина поля от 2 до 30 символов') - .matches(/^[a-zA-Zа-яА-Я0-9-._@]*$/, 'Только буквы (A-z, А-я), цифры (0-9)') - .matches( - /^[^/[!"#$%&'()*+,/:;<=>?[\\\]^`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]*$/, - 'Только дефис, точка, нижнее подчеркивание' - ) - .test('no-consecutive-dashes', 'Введите корректную фамилию', (value) => { - if (typeof value !== 'string') { - return true; - } - return !/--/.test(value.replace(/\s/g, '')); - }) - .test( - 'no-spaces-between-dashes', - 'Введите никнейм без пробелов', - (value) => { - if (typeof value !== 'string') { - return true; - } - if (/\s/.test(value)) { - value.replace(/\s/g, ''); - } - return !/\s/.test(value); - } - ), - password: yup - .string() - .trim() - .required('Поле обязательно для заполнения') - .min(8, 'Длина поля от 8 до 20 символов') - .max(20, 'Длина поля от 8 до 20 символов') - .matches( - /[a-zA-Zа-яА-Я0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]$/, - 'Только буквы (A-z, А-я), цифры (0-9), спецсимволы' - ), - /* eslint-disable */ - re_password: yup - /* eslint-enable */ - .string() - .trim() - .required('Поле обязательно для заполнения') - .min(8, 'Длина поля от 8 до 20 символов') - .max(20, 'Длина поля от 8 до 20 символов') - .oneOf([yup.ref('password')], 'Пароли не совпадают'), -}); -export default schema; +const noConsecutiveDashes = (value: string) => + !/--/.test(value.replace(/\s/g, '')); +const noSpacesBetweenDashes = (value: string) => + !/\s/.test(value.replace(/\s/g, '')); + +const FormSchema = z + .object({ + email: z + .string() + .min(1, { message: 'Поле обязательно для заполнения' }) + .email({ message: 'Проверьте правильность ввода' }) + .min(6, { message: 'Длина поля от 6 до 256 символов' }) + .max(256, { message: 'Длина поля от 6 до 256 символов' }) + .trim() + .regex(generalEmailRegex, { message: 'Проверьте правильность ввода' }) + .regex(noSpecialCharEmailRegex, { + message: 'Проверьте правильность ввода', + }) + .regex(limitedCharEmailRegex, { + message: 'Только буквы (A-z, А-я), цифры (0-9)', + }), + + username: z + .string() + .min(1, { message: 'Поле обязательно для заполнения' }) + .min(2, { message: 'Длина поля от 2 до 30 символов' }) + .max(30, { message: 'Длина поля от 2 до 30 символов' }) + .trim() + .regex(limitedCharEmailRegex, { + message: 'Только буквы (A-z, А-я), цифры (0-9)', + }) + .regex(noSpecialCharEmailRegex, { + message: 'Только дефис, точка, нижнее подчеркивание', + }) + .refine(noConsecutiveDashes, { message: 'Введите корректную фамилию' }) + .refine(noSpacesBetweenDashes, { + message: 'Введите никнейм без пробелов', + }), + password: z + .string() + .min(1, { message: 'Поле обязательно для заполнения' }) + .min(8, { message: 'Длина поля от 8 до 20 символов' }) + .max(20, { message: 'Длина поля от 8 до 20 символов' }) + .regex(passwordRegex, { + message: 'Только буквы (A-z, А-я), цифры (0-9), спецсимволы', + }), + + // eslint-disable-next-line camelcase + re_password: z + .string() + .min(1, { message: 'Поле обязательно для заполнения' }) + + .min(8, { message: 'Длина поля от 8 до 20 символов' }) + .max(20, { message: 'Длина поля от 8 до 20 символов' }), + }) + .refine((val) => val.password === val.re_password, { + message: 'Пароли не совпадают', + path: ['re_password'], + }); + +export default FormSchema; diff --git a/src/utils/regex-consts.ts b/src/utils/regex-consts.ts new file mode 100644 index 00000000..5c5c7a5a --- /dev/null +++ b/src/utils/regex-consts.ts @@ -0,0 +1,8 @@ +export const generalEmailRegex = + /^[a-zA-Z0-9][a-zA-Z0-9_.-]*[a-zA-Z0-9]@[a-zA-Z0-9-]+(?:\.[a-zA-Zа-яА-Я0-9-]+)*\.[a-zA-Zа-яА-Я-]{2,}$/; +export const noSpecialCharEmailRegex = + /^[^/[!"#$%&'()*+,/:;<=>?[\\\]^`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]*$/; +export const limitedCharEmailRegex = /^[a-zA-Zа-яА-Я0-9-._@]*$/; + +export const passwordRegex = + /[a-zA-Zа-яА-Я0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~\u2116\u0024\u20AC\u00A3\u00A5\u20BD\u00A9\u2122\u00AE]$/;