From 9a20c9f098afef20f724a618a08ff39e246bb108 Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Mon, 3 Feb 2025 00:06:16 +0300 Subject: [PATCH] feat: rewrite the form to react-hook-form and add validation --- locales/en.json | 9 ++ locales/ru.json | 9 ++ src/components/MasterDrawer/MasterDrawer.tsx | 158 +++++++++++-------- src/helpers/showToast.ts | 21 +++ src/models/arm/form.ts | 4 + 5 files changed, 137 insertions(+), 64 deletions(-) create mode 100644 src/helpers/showToast.ts create mode 100644 src/models/arm/form.ts diff --git a/locales/en.json b/locales/en.json index 7726b14..fd07db5 100644 --- a/locales/en.json +++ b/locales/en.json @@ -86,6 +86,15 @@ "dry-wash.arm.master.drawer.inputPhone.placeholder": "Enter Phone Number", "dry-wash.arm.master.drawer.button.save": "Save", "dry-wash.arm.master.drawer.button.cancel": "Cancel", + "dry-wash.arm.master.drawer.toast.create-master": "Master created", + "dry-wash.arm.master.drawer.toast.error.empty-fields": "Fields cannot be empty", + "dry-wash.arm.master.drawer.toast.error.base": "Error", + "dry-wash.arm.master.drawer.toast.error.create-master": "Error creating master", + "dry-wash.arm.master.drawer.toast.error.create-master-details": "Failed to add master. Please try again", + "dry-wash.arm.master.drawer.form.name.required": "Master name is required", + "dry-wash.arm.master.drawer.form.phone.required": "Phone number is required", + "dry-wash.arm.master.drawer.form.phone.pattern": "Invalid phone number", + "dry-wash.arm.master.drawer.form.name.minLength": "Name must contain at least 2 characters", "dry-wash.arm.master.sideBar.orders": "Orders", "dry-wash.arm.master.sideBar.master": "Masters", "dry-wash.arm.master.sideBar.title": "Dry Master", diff --git a/locales/ru.json b/locales/ru.json index 94d00fd..6b03952 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -36,6 +36,15 @@ "dry-wash.arm.master.drawer.inputPhone.placeholder": "Введите номер телефона", "dry-wash.arm.master.drawer.button.save": "Сохранить", "dry-wash.arm.master.drawer.button.cancel": "Отменить", + "dry-wash.arm.master.drawer.toast.create-master": "Мастер создан", + "dry-wash.arm.master.drawer.toast.error.empty-fields": "Поля не могут быть пустыми", + "dry-wash.arm.master.drawer.toast.error.base": "Ошибка", + "dry-wash.arm.master.drawer.toast.error.create-master": "Ошибка при создании мастера", + "dry-wash.arm.master.drawer.toast.error.create-master-details": "Не удалось добавить мастера. Попробуйте еще раз", + "dry-wash.arm.master.drawer.form.name.required": "Имя мастера обязательно", + "dry-wash.arm.master.drawer.form.phone.required": "Телефон обязателен", + "dry-wash.arm.master.drawer.form.phone.pattern": "Некорректный номер телефона", + "dry-wash.arm.master.drawer.form.name.minLength": "Имя должно содержать минимум 2 символа", "dry-wash.arm.master.sideBar.orders": "Заказы", "dry-wash.arm.master.sideBar.master": "Мастера", "dry-wash.arm.master.sideBar.title": "Сухой мастер", diff --git a/src/components/MasterDrawer/MasterDrawer.tsx b/src/components/MasterDrawer/MasterDrawer.tsx index fe790bb..482073d 100644 --- a/src/components/MasterDrawer/MasterDrawer.tsx +++ b/src/components/MasterDrawer/MasterDrawer.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; +import { useForm, SubmitHandler } from 'react-hook-form'; import { Button, FormControl, @@ -14,107 +15,136 @@ import { useToast, InputGroup, InputLeftElement, + FormErrorMessage, } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; import { PhoneIcon } from '@chakra-ui/icons'; import { api } from '../../__data__/service/api'; +import showToast from '../../helpers/showToast'; +import { DrawerInputs } from '../../models/arm/form'; -const MasterDrawer = ({ isOpen, onClose }) => { - const [addMaster, { error, isSuccess }] = api.useAddMasterMutation(); - const toast = useToast(); +interface MasterDrawerProps { + isOpen: boolean; + onClose: () => void; +} - const [newMaster, setNewMaster] = useState({ name: '', phone: '' }); +const MasterDrawer = ({ isOpen, onClose }: MasterDrawerProps) => { + const { + register, + handleSubmit, + reset, + formState: { errors }, + } = useForm(); - const handleSave = async () => { + const { t } = useTranslation('~', { + keyPrefix: 'dry-wash.arm.master.drawer', + }); + + const onSubmit: SubmitHandler = async (data) => { const trimMaster = { - phone: newMaster.phone.trim(), - name: newMaster.name.trim(), + name: data.name.trim(), + phone: data.phone.trim(), }; - if (trimMaster.name === '' || trimMaster.phone === '') { + const isEmptyFields = trimMaster.name === '' || trimMaster.phone === ''; + + if (isEmptyFields) { + showToast({ + toast, + title: t('toast.error.base'), + description: t('toast.error.empty-fields'), + status: 'error', + }); return; } - addMaster(trimMaster); + await addMaster(trimMaster); }; + const [addMaster, { error, isSuccess }] = api.useAddMasterMutation(); + const toast = useToast(); + useEffect(() => { if (isSuccess) { - toast({ - title: 'Мастер создан.', - description: `Мастер "${newMaster.name}" успешно добавлен.`, + showToast({ + toast, + title: t('toast.create-master'), status: 'success', - duration: 5000, - isClosable: true, - position: 'top-right', }); + reset(); onClose(); } }, [isSuccess]); useEffect(() => { if (error) { - toast({ - title: 'Ошибка при создании мастера.', - description: 'Не удалось добавить мастера. Попробуйте еще раз.', + showToast({ + toast, + title: t('toast.error.create-master'), + description: t('toast.error.create-master-details'), status: 'error', - duration: 5000, - isClosable: true, - position: 'top-right', }); console.error(error); } }, [error]); - const { t } = useTranslation('~', { - keyPrefix: 'dry-wash.arm.master.drawer', - }); - return ( - - {t('title')} - - - {t('inputName.label')} - - setNewMaster({ ...newMaster, name: e.target.value }) - } - placeholder={t('inputName.placeholder')} - /> - - - {t('inputPhone.label')} - - - - - +
+ + {t('title')} + + + {t('inputName.label')} - setNewMaster({ ...newMaster, phone: e.target.value }) - } - placeholder={t('inputPhone.placeholder')} + {...register('name', { + required: t('form.name.required'), + minLength: { + value: 2, + message: t('form.name.minLength'), + }, + })} + placeholder={t('inputName.placeholder')} /> - - - - - - - + + {errors.name && errors.name.message} + + + + {t('inputPhone.label')} + + + + + value.replace(/[^\d+]/g, ''), + })} + placeholder={t('inputPhone.placeholder')} + /> + + + {errors.phone && errors.phone.message} + + + + + + + +
); diff --git a/src/helpers/showToast.ts b/src/helpers/showToast.ts new file mode 100644 index 0000000..eed5aca --- /dev/null +++ b/src/helpers/showToast.ts @@ -0,0 +1,21 @@ +import { UseToastOptions } from '@chakra-ui/react'; + +interface ShowToast { + toast: (options: UseToastOptions) => void; + title: string; + description?: string; + status: 'info' | 'warning' | 'success' | 'error'; +} + +const showToast = ({ toast, title, description, status }: ShowToast) => { + toast({ + title, + description, + status, + duration: 5000, + isClosable: true, + position: 'top-right', + }); +}; + +export default showToast; diff --git a/src/models/arm/form.ts b/src/models/arm/form.ts new file mode 100644 index 0000000..caadfcf --- /dev/null +++ b/src/models/arm/form.ts @@ -0,0 +1,4 @@ +export type DrawerInputs = { + phone: string; + name: string; +}; -- 2.45.2