Skip to content

Commit

Permalink
SOS-RS#287 - [FIX] Itens Cadastrados sem Categoria estão indo para Me…
Browse files Browse the repository at this point in the history
…dicamentos (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).
  • Loading branch information
TucanoWeb authored and marcodmc committed Jun 6, 2024
1 parent 378ff0d commit 140f1c5
Show file tree
Hide file tree
Showing 6 changed files with 446 additions and 114 deletions.
319 changes: 208 additions & 111 deletions src/pages/CreateSupply/CreateSupply.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<boolean>(false);
const [supplyId, setSupplyId] = useState<string>('');

const {
errors,
Expand All @@ -36,18 +45,27 @@ const CreateSupply = () => {
} = useFormik<ICreateSupply & Omit<ICreateShelterSupply, 'supplyId'>>({
initialValues: {
name: '',
supplyCategoryId: supplyCategories?.at(0)?.id ?? '-1',
shelterId,
priority: SupplyPriority.NotNeeded,
priority: SupplyPriority.Needing,
},
enableReinitialize: true,
validateOnBlur: false,
validateOnChange: false,
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'),
}),
Expand Down Expand Up @@ -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 (
<Fragment key={key}>
<div
className="flex cursor-pointer border m-1 p-2 rounded hover:scale-105"
onClick={() => {
setSupplyId(element.id);
setModalOpened(true);
}}
>
{element.name}
</div>
</Fragment>
);
};

if (loading) return <LoadingScreen />;

return (
<div className="flex flex-col h-screen items-center">
<Header
title="Cadastrar novo item"
className="bg-white [&_*]:text-zinc-800 border-b-[1px] border-b-border"
startAdornment={
<Button
variant="ghost"
className="[&_svg]:stroke-blue-500"
onClick={() => navigate(-1)}
>
<ChevronLeft size={20} />
</Button>
}
/>
<div className="p-4 flex flex-col max-w-5xl w-full gap-3 items-start h-full">
<form className="contents" onSubmit={handleSubmit}>
<h6 className="text-2xl font-semibold">Cadastrar novo item</h6>
<p className="text-muted-foreground">
Informe o nome do item que você deseja cadastrar, a categoria e a
prioridade
</p>
<div className="flex flex-col gap-6 w-full mt-6">
<TextField
label="Nome do item"
{...getFieldProps('name')}
error={!!errors.name}
helperText={errors.name}
/>
<div className="flex flex-col w-full">
<label className="text-muted-foreground">Categoria</label>
<Select
onValueChange={(v) => {
setFieldValue('supplyCategoryId', v);
}}
defaultValue={`${values.supplyCategoryId}`}
>
<SelectTrigger className="w-full">
<SelectValue
className="text-muted-foreground"
placeholder="Selecione"
/>
</SelectTrigger>
<SelectContent>
{supplyCategories.map((category) => (
<SelectItem
key={category.id}
value={category.id}
<Fragment>
{modalOpened && (
<ModalCreateSupply
supplies={shelter.shelterSupplies as any}
supplyId={supplyId}
shelterId={shelterId}
open={modalOpened}
title="Escolha a prioridade do item"
options={[
{
label: 'Precisa urgente',
value: `${SupplyPriority.Urgent}`,
},
{
label: 'Precisa',
value: `${SupplyPriority.Needing}`,
},
{
label: 'Disponível para doação',
value: `${SupplyPriority.Remaining}`,
},
{
label: 'Não preciso',
value: `${SupplyPriority.NotNeeded}`,
},
]}
onClose={() => setModalOpened(false)}
/>
)}
<div className="flex flex-col h-screen items-center">
<Header
title="Cadastrar novo item"
className="bg-white [&_*]:text-zinc-800 border-b-[1px] border-b-border"
startAdornment={
<Button
variant="ghost"
className="[&_svg]:stroke-blue-500"
onClick={() => navigate(-1)}
>
<ChevronLeft size={20} />
</Button>
}
/>
<div className="p-4 flex flex-col max-w-5xl w-full gap-3 items-start h-full">
<form className="contents" onSubmit={handleSubmit}>
<h6 className="text-2xl font-semibold">Cadastrar novo item</h6>
<p className="text-muted-foreground">
Informe o nome do item que você deseja cadastrar, a categoria e a
prioridade
</p>
<div className="flex flex-col gap-6 w-full mt-6">
<TextField
label="Nome do item"
{...getFieldProps('name')}
error={!!errors.name}
helperText={errors.name}
/>
<div className="flex flex-wrap">
{filteredSupplies.length > 0 && (
<div className="border p-2 rounded bg-amber-200 text-gray-700">
Foram encontrados registros com as informações fornecidas.
Para evitar registros duplicados, pedimos a gentileza de
verificar se alguns dos registros abaixo lhe atende:
</div>
)}
{filteredSupplies.map(renderSupplies)}
</div>
<div className="flex flex-col w-full">
<label className="text-muted-foreground">Categoria</label>
<Select
onValueChange={(v) => setFieldValue('supplyCategoryId', v)}
>
<SelectTrigger
className={`w-full ${
errors.supplyCategoryId && 'border-red-600'
}`}
>
<SelectValue
className="text-muted-foreground"
>
{category.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<TextField
label="Quantidade"
{...getFieldProps('quantity')}
error={!!errors.quantity}
helperText={errors.quantity}
/>
<div className="flex flex-col w-full">
<label className="text-muted-foreground">Prioridade</label>
<Select
onValueChange={(v) => {
setFieldValue('priority', v);
}}
defaultValue={`${values.priority}`}
>
<SelectTrigger className="w-full">
<SelectValue
className="text-muted-foreground"
placeholder="Selecione"
/>
</SelectTrigger>
<SelectContent>
{[
SupplyPriority.Urgent,
SupplyPriority.Needing,
SupplyPriority.Remaining,
SupplyPriority.NotNeeded,
].map((priority) => {
const { className, label } =
getSupplyPriorityProps(priority);
return (
placeholder="Selecione"
/>
</SelectTrigger>
<SelectContent>
{supplyCategories.map((category) => (
<SelectItem
key={priority}
value={`${priority}`}
key={category.id}
value={category.id}
className="text-muted-foreground"
>
<div className="flex gap-2 p-2 items-center">
<CircleStatus className={className} />
{label}
</div>
{category.name}
</SelectItem>
);
})}
</SelectContent>
</Select>
))}
</SelectContent>
</Select>
<div className="text-red-600 text-sm mt-0">
{errors.supplyCategoryId}
</div>
</div>
<TextField
label="Quantidade"
{...getFieldProps('quantity')}
error={!!errors.quantity}
helperText={errors.quantity}
/>
<div className="flex flex-col w-full">
<label className="text-muted-foreground">Prioridade</label>
<Select
onValueChange={(v) => {
setFieldValue('priority', v);
}}
defaultValue={`${values.priority}`}
>
<SelectTrigger className="w-full">
<SelectValue
className="text-muted-foreground"
placeholder="Selecione"
/>
</SelectTrigger>
<SelectContent>
{[
SupplyPriority.Needing,
SupplyPriority.Urgent,
SupplyPriority.Remaining,
].map((priority) => {
const { className, label } =
getSupplyPriorityProps(priority);
return (
<SelectItem
key={priority}
value={`${priority}`}
className="text-muted-foreground"
>
<div className="flex gap-2 p-2 items-center">
<CircleStatus className={className} />
{label}
</div>
</SelectItem>
);
})}
</SelectContent>
</Select>
</div>
<div className="text-red-600 text-sm mt-0">{errors.priority}</div>
</div>
</div>
<div className="flex flex-1 flex-col justify-end md:justify-start w-full py-6">
<Button
loading={isSubmitting}
type="submit"
className="flex gap-2 text-white font-medium text-lg bg-blue-500 hover:bg-blue-600 w-full"
>
Salvar
</Button>
</div>
</form>
<div className="flex flex-1 flex-col justify-end md:justify-start w-full py-6">
<Button
loading={isSubmitting}
type="submit"
disabled={filteredSupplies.length > 0}
className="flex gap-2 text-white font-medium text-lg bg-blue-500 hover:bg-blue-600 w-full"
>
Salvar
</Button>
</div>
</form>
</div>
</div>
</div>
</Fragment>
);
};

Expand Down
3 changes: 3 additions & 0 deletions src/pages/CreateSupply/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { ModalCreateSupply } from './modal';

export { ModalCreateSupply };
Loading

0 comments on commit 140f1c5

Please sign in to comment.