@@ -6,7 +6,7 @@ import * as Yup from 'yup';
6
6
import { CircleStatus , Header , LoadingScreen , TextField } from '@/components' ;
7
7
import { Button } from '@/components/ui/button' ;
8
8
import { useToast } from '@/components/ui/use-toast' ;
9
- import { useSupplyCategories } from '@/hooks' ;
9
+ import { useShelter , useSupplies , useSupplyCategories } from '@/hooks' ;
10
10
import {
11
11
Select ,
12
12
SelectContent ,
@@ -19,12 +19,21 @@ import { getSupplyPriorityProps } from '@/lib/utils';
19
19
import { ShelterSupplyServices , SupplyServices } from '@/service' ;
20
20
import { ICreateShelterSupply } from '@/service/shelterSupply/types' ;
21
21
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' ;
22
26
23
27
const CreateSupply = ( ) => {
24
28
const navigate = useNavigate ( ) ;
25
29
const { shelterId = '-1' } = useParams ( ) ;
30
+ const { data : shelter } = useShelter ( shelterId ) ;
26
31
const { toast } = useToast ( ) ;
27
32
const { data : supplyCategories , loading } = useSupplyCategories ( ) ;
33
+ const { data : supplies } = useSupplies ( ) ;
34
+
35
+ const [ modalOpened , setModalOpened ] = useState < boolean > ( false ) ;
36
+ const [ supplyId , setSupplyId ] = useState < string > ( '' ) ;
28
37
29
38
const {
30
39
errors,
@@ -36,18 +45,27 @@ const CreateSupply = () => {
36
45
} = useFormik < ICreateSupply & Omit < ICreateShelterSupply , 'supplyId' > > ( {
37
46
initialValues : {
38
47
name : '' ,
39
- supplyCategoryId : supplyCategories ?. at ( 0 ) ?. id ?? '-1' ,
40
48
shelterId,
41
- priority : SupplyPriority . NotNeeded ,
49
+ priority : SupplyPriority . Needing ,
42
50
} ,
43
51
enableReinitialize : true ,
44
52
validateOnBlur : false ,
45
53
validateOnChange : false ,
46
54
validateOnMount : false ,
47
55
validationSchema : Yup . object ( ) . shape ( {
48
56
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 - z A - 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 - z A - 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 ( ) ,
51
69
priority : Yup . string ( ) . required ( 'Este campo deve ser preenchido' ) ,
52
70
supplyCategoryId : Yup . string ( ) . required ( 'Este campo deve ser preenchido' ) ,
53
71
} ) ,
@@ -78,122 +96,201 @@ const CreateSupply = () => {
78
96
} ,
79
97
} ) ;
80
98
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
+
81
133
if ( loading ) return < LoadingScreen /> ;
82
134
83
135
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
131
214
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 ) => (
169
220
< SelectItem
170
- key = { priority }
171
- value = { ` ${ priority } ` }
221
+ key = { category . id }
222
+ value = { category . id }
172
223
className = "text-muted-foreground"
173
224
>
174
- < div className = "flex gap-2 p-2 items-center" >
175
- < CircleStatus className = { className } />
176
- { label }
177
- </ div >
225
+ { category . name }
178
226
</ 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 >
183
279
</ 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 >
195
292
</ div >
196
- </ div >
293
+ </ Fragment >
197
294
) ;
198
295
} ;
199
296
0 commit comments