From afde60652488b61fd43b96f4b48de23efffac805 Mon Sep 17 00:00:00 2001 From: Eric Ricielle Date: Sat, 25 May 2024 00:30:06 -0300 Subject: [PATCH] =?UTF-8?q?#287=20-=20[FIX]=20Itens=20Cadastrados=20sem=20?= =?UTF-8?q?Categoria=20est=C3=A3o=20indo=20para=20Medicamentos=20(#296)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #287 * Delete src/components/Icon directory * Update SupplyRowInfo.tsx * RollBack SupplyRowInfo.tsx * Update SupplyRow.tsx * Update EditShelterSupply.tsx * Update CreateSupply.tsx - De forma a evitar termos genéricos demais, é solicitado ao usuário que registre um recurso com no mínimo 3 caracteres. Validação via Yup. * Update CreateSupply.tsx - Bloqueia cadastro de items com números e caracteres especiais. Validação via Yup. * Update CreateSupply.tsx * Update CreateSupply.tsx - Limite de 30 itens retornados enquanto o usuário está digitando o termo desejado. * Update CreateSupply.tsx - Bloqueia caracteres especiais; - Requer no mínimo 3 letras (bloqueia apenas números). --- src/pages/CreateSupply/CreateSupply.tsx | 319 ++++++++++++------ src/pages/CreateSupply/components/index.ts | 3 + src/pages/CreateSupply/components/modal.tsx | 191 +++++++++++ src/pages/CreateSupply/components/types.ts | 35 ++ .../components/SupplyRow/SupplyRow.tsx | 10 +- src/service/supply/types.ts | 2 +- 6 files changed, 446 insertions(+), 114 deletions(-) create mode 100644 src/pages/CreateSupply/components/index.ts create mode 100644 src/pages/CreateSupply/components/modal.tsx create mode 100644 src/pages/CreateSupply/components/types.ts diff --git a/src/pages/CreateSupply/CreateSupply.tsx b/src/pages/CreateSupply/CreateSupply.tsx index 18fd1a3a..78f8025c 100644 --- a/src/pages/CreateSupply/CreateSupply.tsx +++ b/src/pages/CreateSupply/CreateSupply.tsx @@ -6,7 +6,7 @@ import * as Yup from 'yup'; import { CircleStatus, Header, LoadingScreen, TextField } from '@/components'; import { Button } from '@/components/ui/button'; import { useToast } from '@/components/ui/use-toast'; -import { useSupplyCategories } from '@/hooks'; +import { useShelter, useSupplies, useSupplyCategories } from '@/hooks'; import { Select, SelectContent, @@ -19,12 +19,21 @@ import { getSupplyPriorityProps } from '@/lib/utils'; import { ShelterSupplyServices, SupplyServices } from '@/service'; import { ICreateShelterSupply } from '@/service/shelterSupply/types'; import { clearCache } from '@/api/cache'; +import { Fragment } from 'react/jsx-runtime'; +import { IUseSuppliesData } from '@/hooks/useSupplies/types'; +import { useState } from 'react'; +import { ModalCreateSupply } from './components'; const CreateSupply = () => { const navigate = useNavigate(); const { shelterId = '-1' } = useParams(); + const { data: shelter } = useShelter(shelterId); const { toast } = useToast(); const { data: supplyCategories, loading } = useSupplyCategories(); + const { data: supplies } = useSupplies(); + + const [modalOpened, setModalOpened] = useState(false); + const [supplyId, setSupplyId] = useState(''); const { errors, @@ -36,9 +45,8 @@ const CreateSupply = () => { } = useFormik>({ initialValues: { name: '', - supplyCategoryId: supplyCategories?.at(0)?.id ?? '-1', shelterId, - priority: SupplyPriority.NotNeeded, + priority: SupplyPriority.Needing, }, enableReinitialize: true, validateOnBlur: false, @@ -46,8 +54,18 @@ const CreateSupply = () => { validateOnMount: false, validationSchema: Yup.object().shape({ shelterId: Yup.string().required('Este campo deve ser preenchido'), - name: Yup.string().required('Este campo deve ser preenchido'), - quantity: Yup.number().typeError('Insira um valor númerico').moreThan(0, 'O valor tem que ser maior do que 0').optional(), + name: Yup.string() + .matches(/^[a-zA-ZÀ-ÿ0-9\s]*$/, "O nome não deve conter caracteres especiais") + .test('min-letters', 'O nome deve conter pelo menos 3 letras', value => { + const letterCount = (value?.match(/[a-zA-ZÀ-ÿ]/g) || []).length; + return letterCount >= 3; + }) + .min(3, 'Insira no mínimo 3 caracteres') + .required('Este campo deve ser preenchido'), + quantity: Yup.number() + .typeError('Insira um valor númerico') + .moreThan(0, 'O valor tem que ser maior do que 0') + .optional(), priority: Yup.string().required('Este campo deve ser preenchido'), supplyCategoryId: Yup.string().required('Este campo deve ser preenchido'), }), @@ -78,122 +96,201 @@ const CreateSupply = () => { }, }); + const filteredSupplies = + values.name.length > 2 + ? supplies.filter((e) => { + const normalizedSearchTerm = values.name + .trim() + .toLowerCase() + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, ''); + return e.name + .toLowerCase() + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .includes(normalizedSearchTerm); + }) + : []; + + const renderSupplies = (element: IUseSuppliesData, key: number) => { + if (values.name.length < 3 || key > 30) return <>; + + return ( + +
{ + setSupplyId(element.id); + setModalOpened(true); + }} + > + {element.name} +
+
+ ); + }; + if (loading) return ; return ( -
-
navigate(-1)} - > - - - } - /> -
-
-
Cadastrar novo item
-

- Informe o nome do item que você deseja cadastrar, a categoria e a - prioridade -

-
- -
- - setFieldValue('supplyCategoryId', v)} + > + + - {category.name} - - ))} - - -
- -
- - + ))} + + +
+ {errors.supplyCategoryId} +
+
+ +
+ + +
+
{errors.priority}
-
-
- -
- +
+ +
+ +
- + ); }; diff --git a/src/pages/CreateSupply/components/index.ts b/src/pages/CreateSupply/components/index.ts new file mode 100644 index 00000000..87db6c65 --- /dev/null +++ b/src/pages/CreateSupply/components/index.ts @@ -0,0 +1,3 @@ +import { ModalCreateSupply } from './modal'; + +export { ModalCreateSupply }; diff --git a/src/pages/CreateSupply/components/modal.tsx b/src/pages/CreateSupply/components/modal.tsx new file mode 100644 index 00000000..ce4a838e --- /dev/null +++ b/src/pages/CreateSupply/components/modal.tsx @@ -0,0 +1,191 @@ +import { useState, useEffect } from 'react'; + +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; +import { Label } from '@/components/ui/label'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { ShelterSupplyServices } from '@/service'; +import { Link } from 'react-router-dom'; +import { IMessage, IProps, ISupplies } from './types'; + +const ModalCreateSupply = (props: IProps) => { + const { + open, + onClose, + title, + description = '', + options, + supplies, + supplyId, + shelterId, + } = props; + const [data, setData] = useState({ + quantity: 0, + priority: '10', + }); + const [message, setMessage] = useState({ + error: false, + register: false, + successSub: false, + message: '', + }); + + useEffect(() => { + supplies.forEach((element: ISupplies) => { + if (element.supply.id === supplyId) { + setMessage((prev) => ({ + ...prev, + register: true, + message: 'Verificamos que você já tem esse item registrado.', + })); + setData({ + quantity: element.quantity, + priority: element.priority.toString(), + }); + } + }); + }, [supplies, supplyId]); + + const onSave = () => { + if (!message.register) { + ShelterSupplyServices.create({ + shelterId, + supplyId, + priority: parseInt(data.priority), + quantity: data.quantity, + }) + .then(() => { + setMessage((prev) => ({ + ...prev, + successSub: true, + message: 'Registro salvo com sucesso!', + })); + }) + .catch(() => + alert( + 'Ocorreu um erro. Por favor, tente novamente ou entre em contato com o suporte.' + ) + ); + } else { + ShelterSupplyServices.update(shelterId, supplyId, { + priority: parseInt(data.priority), + quantity: data.quantity, + }) + .then(() => { + setMessage((prev) => ({ + ...prev, + successSub: true, + message: 'Registro salvo com sucesso!', + })); + }) + .catch(() => + alert( + 'Ocorreu um erro. Por favor, tente novamente ou entre em contato com o suporte.' + ) + ); + } + }; + + return ( + + + {message.successSub ? ( + <> +
+ {message.message} +
+ + + + + + + ) : ( + <> + + + {title} + + {description && ( + {description} + )} + + {message.message && ( +
+ {message.message} +
+ )} +
+ +
+ + setData((prev) => ({ + ...prev, + quantity: parseInt(event.target.value), + })) + } + placeholder="Quantidade" + min={0} + /> +
+
+
+ + setData((prev: any) => ({ + ...prev, + priority: v, + })) + } + > + {options.map((option: any, idx: any) => ( +
+ + +
+ ))} +
+
+ + + + + )} +
+
+ ); +}; + +export { ModalCreateSupply }; diff --git a/src/pages/CreateSupply/components/types.ts b/src/pages/CreateSupply/components/types.ts new file mode 100644 index 00000000..27af8ed0 --- /dev/null +++ b/src/pages/CreateSupply/components/types.ts @@ -0,0 +1,35 @@ +import { IDialogSelectorItemProps } from '@/components/DialogSelector/types'; + +export interface IProps { + open: boolean; + onClose?: () => void; + title: string; + description?: string; + options: IDialogSelectorItemProps[]; + supplies: [ + { + supply: { + id: string; + }; + quantity: number; + priority: string; + } + ]; + supplyId: string; + shelterId: string; +} + +export interface ISupplies { + supply: { + id: string; + }; + quantity: number; + priority: string; +} + +export interface IMessage { + error: boolean; + message: string; + register: boolean; + successSub: boolean; +} diff --git a/src/pages/EditShelterSupply/components/SupplyRow/SupplyRow.tsx b/src/pages/EditShelterSupply/components/SupplyRow/SupplyRow.tsx index a4d59c7d..9aba6cd4 100644 --- a/src/pages/EditShelterSupply/components/SupplyRow/SupplyRow.tsx +++ b/src/pages/EditShelterSupply/components/SupplyRow/SupplyRow.tsx @@ -1,13 +1,19 @@ import { SupplyPriority } from '@/service/supply/types'; import { SupplyRowInfo } from '../SupplyRowInfo'; import { ISupplyRowProps } from './types'; +import { Archive } from 'lucide-react'; const SupplyRow = (props: ISupplyRowProps) => { const { name, items, onClick } = props; return ( -
-

{name}

+
+
+

+ + {name.toUpperCase()} +

+
{items.map((item, idy) => (