Skip to content

Commit 140f1c5

Browse files
TucanoWebmarcodmc
authored andcommitted
SOS-RS#287 - [FIX] Itens Cadastrados sem Categoria estão indo para Medicamentos (SOS-RS#296)
* SOS-RS#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).
1 parent 378ff0d commit 140f1c5

File tree

6 files changed

+446
-114
lines changed

6 files changed

+446
-114
lines changed

src/pages/CreateSupply/CreateSupply.tsx

Lines changed: 208 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as Yup from 'yup';
66
import { CircleStatus, Header, LoadingScreen, TextField } from '@/components';
77
import { Button } from '@/components/ui/button';
88
import { useToast } from '@/components/ui/use-toast';
9-
import { useSupplyCategories } from '@/hooks';
9+
import { useShelter, useSupplies, useSupplyCategories } from '@/hooks';
1010
import {
1111
Select,
1212
SelectContent,
@@ -19,12 +19,21 @@ import { getSupplyPriorityProps } from '@/lib/utils';
1919
import { ShelterSupplyServices, SupplyServices } from '@/service';
2020
import { ICreateShelterSupply } from '@/service/shelterSupply/types';
2121
import { clearCache } from '@/api/cache';
22+
import { Fragment } from 'react/jsx-runtime';
23+
import { IUseSuppliesData } from '@/hooks/useSupplies/types';
24+
import { useState } from 'react';
25+
import { ModalCreateSupply } from './components';
2226

2327
const CreateSupply = () => {
2428
const navigate = useNavigate();
2529
const { shelterId = '-1' } = useParams();
30+
const { data: shelter } = useShelter(shelterId);
2631
const { toast } = useToast();
2732
const { data: supplyCategories, loading } = useSupplyCategories();
33+
const { data: supplies } = useSupplies();
34+
35+
const [modalOpened, setModalOpened] = useState<boolean>(false);
36+
const [supplyId, setSupplyId] = useState<string>('');
2837

2938
const {
3039
errors,
@@ -36,18 +45,27 @@ const CreateSupply = () => {
3645
} = useFormik<ICreateSupply & Omit<ICreateShelterSupply, 'supplyId'>>({
3746
initialValues: {
3847
name: '',
39-
supplyCategoryId: supplyCategories?.at(0)?.id ?? '-1',
4048
shelterId,
41-
priority: SupplyPriority.NotNeeded,
49+
priority: SupplyPriority.Needing,
4250
},
4351
enableReinitialize: true,
4452
validateOnBlur: false,
4553
validateOnChange: false,
4654
validateOnMount: false,
4755
validationSchema: Yup.object().shape({
4856
shelterId: Yup.string().required('Este campo deve ser preenchido'),
49-
name: Yup.string().required('Este campo deve ser preenchido'),
50-
quantity: Yup.number().typeError('Insira um valor númerico').moreThan(0, 'O valor tem que ser maior do que 0').optional(),
57+
name: Yup.string()
58+
.matches(/^[a-zA-ZÀ-ÿ0-9\s]*$/, "O nome não deve conter caracteres especiais")
59+
.test('min-letters', 'O nome deve conter pelo menos 3 letras', value => {
60+
const letterCount = (value?.match(/[a-zA-ZÀ-ÿ]/g) || []).length;
61+
return letterCount >= 3;
62+
})
63+
.min(3, 'Insira no mínimo 3 caracteres')
64+
.required('Este campo deve ser preenchido'),
65+
quantity: Yup.number()
66+
.typeError('Insira um valor númerico')
67+
.moreThan(0, 'O valor tem que ser maior do que 0')
68+
.optional(),
5169
priority: Yup.string().required('Este campo deve ser preenchido'),
5270
supplyCategoryId: Yup.string().required('Este campo deve ser preenchido'),
5371
}),
@@ -78,122 +96,201 @@ const CreateSupply = () => {
7896
},
7997
});
8098

99+
const filteredSupplies =
100+
values.name.length > 2
101+
? supplies.filter((e) => {
102+
const normalizedSearchTerm = values.name
103+
.trim()
104+
.toLowerCase()
105+
.normalize('NFD')
106+
.replace(/[\u0300-\u036f]/g, '');
107+
return e.name
108+
.toLowerCase()
109+
.normalize('NFD')
110+
.replace(/[\u0300-\u036f]/g, '')
111+
.includes(normalizedSearchTerm);
112+
})
113+
: [];
114+
115+
const renderSupplies = (element: IUseSuppliesData, key: number) => {
116+
if (values.name.length < 3 || key > 30) return <></>;
117+
118+
return (
119+
<Fragment key={key}>
120+
<div
121+
className="flex cursor-pointer border m-1 p-2 rounded hover:scale-105"
122+
onClick={() => {
123+
setSupplyId(element.id);
124+
setModalOpened(true);
125+
}}
126+
>
127+
{element.name}
128+
</div>
129+
</Fragment>
130+
);
131+
};
132+
81133
if (loading) return <LoadingScreen />;
82134

83135
return (
84-
<div className="flex flex-col h-screen items-center">
85-
<Header
86-
title="Cadastrar novo item"
87-
className="bg-white [&_*]:text-zinc-800 border-b-[1px] border-b-border"
88-
startAdornment={
89-
<Button
90-
variant="ghost"
91-
className="[&_svg]:stroke-blue-500"
92-
onClick={() => navigate(-1)}
93-
>
94-
<ChevronLeft size={20} />
95-
</Button>
96-
}
97-
/>
98-
<div className="p-4 flex flex-col max-w-5xl w-full gap-3 items-start h-full">
99-
<form className="contents" onSubmit={handleSubmit}>
100-
<h6 className="text-2xl font-semibold">Cadastrar novo item</h6>
101-
<p className="text-muted-foreground">
102-
Informe o nome do item que você deseja cadastrar, a categoria e a
103-
prioridade
104-
</p>
105-
<div className="flex flex-col gap-6 w-full mt-6">
106-
<TextField
107-
label="Nome do item"
108-
{...getFieldProps('name')}
109-
error={!!errors.name}
110-
helperText={errors.name}
111-
/>
112-
<div className="flex flex-col w-full">
113-
<label className="text-muted-foreground">Categoria</label>
114-
<Select
115-
onValueChange={(v) => {
116-
setFieldValue('supplyCategoryId', v);
117-
}}
118-
defaultValue={`${values.supplyCategoryId}`}
119-
>
120-
<SelectTrigger className="w-full">
121-
<SelectValue
122-
className="text-muted-foreground"
123-
placeholder="Selecione"
124-
/>
125-
</SelectTrigger>
126-
<SelectContent>
127-
{supplyCategories.map((category) => (
128-
<SelectItem
129-
key={category.id}
130-
value={category.id}
136+
<Fragment>
137+
{modalOpened && (
138+
<ModalCreateSupply
139+
supplies={shelter.shelterSupplies as any}
140+
supplyId={supplyId}
141+
shelterId={shelterId}
142+
open={modalOpened}
143+
title="Escolha a prioridade do item"
144+
options={[
145+
{
146+
label: 'Precisa urgente',
147+
value: `${SupplyPriority.Urgent}`,
148+
},
149+
{
150+
label: 'Precisa',
151+
value: `${SupplyPriority.Needing}`,
152+
},
153+
{
154+
label: 'Disponível para doação',
155+
value: `${SupplyPriority.Remaining}`,
156+
},
157+
{
158+
label: 'Não preciso',
159+
value: `${SupplyPriority.NotNeeded}`,
160+
},
161+
]}
162+
onClose={() => setModalOpened(false)}
163+
/>
164+
)}
165+
<div className="flex flex-col h-screen items-center">
166+
<Header
167+
title="Cadastrar novo item"
168+
className="bg-white [&_*]:text-zinc-800 border-b-[1px] border-b-border"
169+
startAdornment={
170+
<Button
171+
variant="ghost"
172+
className="[&_svg]:stroke-blue-500"
173+
onClick={() => navigate(-1)}
174+
>
175+
<ChevronLeft size={20} />
176+
</Button>
177+
}
178+
/>
179+
<div className="p-4 flex flex-col max-w-5xl w-full gap-3 items-start h-full">
180+
<form className="contents" onSubmit={handleSubmit}>
181+
<h6 className="text-2xl font-semibold">Cadastrar novo item</h6>
182+
<p className="text-muted-foreground">
183+
Informe o nome do item que você deseja cadastrar, a categoria e a
184+
prioridade
185+
</p>
186+
<div className="flex flex-col gap-6 w-full mt-6">
187+
<TextField
188+
label="Nome do item"
189+
{...getFieldProps('name')}
190+
error={!!errors.name}
191+
helperText={errors.name}
192+
/>
193+
<div className="flex flex-wrap">
194+
{filteredSupplies.length > 0 && (
195+
<div className="border p-2 rounded bg-amber-200 text-gray-700">
196+
Foram encontrados registros com as informações fornecidas.
197+
Para evitar registros duplicados, pedimos a gentileza de
198+
verificar se alguns dos registros abaixo lhe atende:
199+
</div>
200+
)}
201+
{filteredSupplies.map(renderSupplies)}
202+
</div>
203+
<div className="flex flex-col w-full">
204+
<label className="text-muted-foreground">Categoria</label>
205+
<Select
206+
onValueChange={(v) => setFieldValue('supplyCategoryId', v)}
207+
>
208+
<SelectTrigger
209+
className={`w-full ${
210+
errors.supplyCategoryId && 'border-red-600'
211+
}`}
212+
>
213+
<SelectValue
131214
className="text-muted-foreground"
132-
>
133-
{category.name}
134-
</SelectItem>
135-
))}
136-
</SelectContent>
137-
</Select>
138-
</div>
139-
<TextField
140-
label="Quantidade"
141-
{...getFieldProps('quantity')}
142-
error={!!errors.quantity}
143-
helperText={errors.quantity}
144-
/>
145-
<div className="flex flex-col w-full">
146-
<label className="text-muted-foreground">Prioridade</label>
147-
<Select
148-
onValueChange={(v) => {
149-
setFieldValue('priority', v);
150-
}}
151-
defaultValue={`${values.priority}`}
152-
>
153-
<SelectTrigger className="w-full">
154-
<SelectValue
155-
className="text-muted-foreground"
156-
placeholder="Selecione"
157-
/>
158-
</SelectTrigger>
159-
<SelectContent>
160-
{[
161-
SupplyPriority.Urgent,
162-
SupplyPriority.Needing,
163-
SupplyPriority.Remaining,
164-
SupplyPriority.NotNeeded,
165-
].map((priority) => {
166-
const { className, label } =
167-
getSupplyPriorityProps(priority);
168-
return (
215+
placeholder="Selecione"
216+
/>
217+
</SelectTrigger>
218+
<SelectContent>
219+
{supplyCategories.map((category) => (
169220
<SelectItem
170-
key={priority}
171-
value={`${priority}`}
221+
key={category.id}
222+
value={category.id}
172223
className="text-muted-foreground"
173224
>
174-
<div className="flex gap-2 p-2 items-center">
175-
<CircleStatus className={className} />
176-
{label}
177-
</div>
225+
{category.name}
178226
</SelectItem>
179-
);
180-
})}
181-
</SelectContent>
182-
</Select>
227+
))}
228+
</SelectContent>
229+
</Select>
230+
<div className="text-red-600 text-sm mt-0">
231+
{errors.supplyCategoryId}
232+
</div>
233+
</div>
234+
<TextField
235+
label="Quantidade"
236+
{...getFieldProps('quantity')}
237+
error={!!errors.quantity}
238+
helperText={errors.quantity}
239+
/>
240+
<div className="flex flex-col w-full">
241+
<label className="text-muted-foreground">Prioridade</label>
242+
<Select
243+
onValueChange={(v) => {
244+
setFieldValue('priority', v);
245+
}}
246+
defaultValue={`${values.priority}`}
247+
>
248+
<SelectTrigger className="w-full">
249+
<SelectValue
250+
className="text-muted-foreground"
251+
placeholder="Selecione"
252+
/>
253+
</SelectTrigger>
254+
<SelectContent>
255+
{[
256+
SupplyPriority.Needing,
257+
SupplyPriority.Urgent,
258+
SupplyPriority.Remaining,
259+
].map((priority) => {
260+
const { className, label } =
261+
getSupplyPriorityProps(priority);
262+
return (
263+
<SelectItem
264+
key={priority}
265+
value={`${priority}`}
266+
className="text-muted-foreground"
267+
>
268+
<div className="flex gap-2 p-2 items-center">
269+
<CircleStatus className={className} />
270+
{label}
271+
</div>
272+
</SelectItem>
273+
);
274+
})}
275+
</SelectContent>
276+
</Select>
277+
</div>
278+
<div className="text-red-600 text-sm mt-0">{errors.priority}</div>
183279
</div>
184-
</div>
185-
<div className="flex flex-1 flex-col justify-end md:justify-start w-full py-6">
186-
<Button
187-
loading={isSubmitting}
188-
type="submit"
189-
className="flex gap-2 text-white font-medium text-lg bg-blue-500 hover:bg-blue-600 w-full"
190-
>
191-
Salvar
192-
</Button>
193-
</div>
194-
</form>
280+
<div className="flex flex-1 flex-col justify-end md:justify-start w-full py-6">
281+
<Button
282+
loading={isSubmitting}
283+
type="submit"
284+
disabled={filteredSupplies.length > 0}
285+
className="flex gap-2 text-white font-medium text-lg bg-blue-500 hover:bg-blue-600 w-full"
286+
>
287+
Salvar
288+
</Button>
289+
</div>
290+
</form>
291+
</div>
195292
</div>
196-
</div>
293+
</Fragment>
197294
);
198295
};
199296

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ModalCreateSupply } from './modal';
2+
3+
export { ModalCreateSupply };

0 commit comments

Comments
 (0)