1
+ import { Search } from 'lucide-react' ;
2
+ import { useMemo , useState } from 'react' ;
3
+
4
+ import { LoadingScreen } from '@/components' ;
5
+ import { SupplyPriority } from '@/service/supply/types' ;
6
+ import { Separator } from '@/components/ui/separator' ;
7
+ import { Input } from '@/components/ui/input' ;
8
+ import Select from 'react-select' ;
9
+ import { useSupplyCategories } from '@/hooks' ;
10
+ import { ISupplyCategory } from '@/hooks/useSupplyCategories/types' ;
11
+ import { useSupplies } from '@/hooks/useSupplies' ;
12
+ import { group } from '@/lib/utils' ;
13
+ import { Button } from '@/components/ui/button' ;
14
+ import { useFormik } from 'formik' ;
15
+ import { Dialog , DialogContent , DialogHeader , DialogTitle } from '@/components/ui/dialog' ;
16
+ import { IUseShelterSearchParams } from '@/hooks/useShelters/types' ;
17
+ import { IComplexSelectData , IComplexSelectGroupedData } from './types' ;
18
+
19
+ const Filter = ( props : any ) => {
20
+ const priorityOptions = [
21
+ {
22
+ label : 'Necessita urgente' ,
23
+ value : `${ SupplyPriority . Urgent } ` ,
24
+ } ,
25
+ {
26
+ label : 'Precisa' ,
27
+ value : `${ SupplyPriority . Needing } ` ,
28
+ } ,
29
+ {
30
+ label : 'Disponível para doação' ,
31
+ value : `${ SupplyPriority . Remaining } ` ,
32
+ } ,
33
+ ] ;
34
+ const [ supplyOptions , setSupplyOptions ] = useState < IComplexSelectGroupedData [ ] > ( [ ] ) ;
35
+ const { data : supplyCategories , loading } = useSupplyCategories ( ) ;
36
+ const result = useSupplies ( ) ;
37
+ const {
38
+ handleSubmit,
39
+ values,
40
+ setFieldValue,
41
+ } = useFormik < IUseShelterSearchParams > ( {
42
+ initialValues : {
43
+ search : props . filters . search ,
44
+ priority : props . filters . priority ,
45
+ supplyCategories : props . filters . supplyCategories ,
46
+ supplies : props . filters . supplies ,
47
+ filterAvailableShelter : props . filters . filterAvailableShelter ,
48
+ filterUnavailableShelter : props . filters . filterUnavailableShelter ,
49
+ waitingShelterAvailability : props . filters . waitingShelterAvailability
50
+ } ,
51
+ enableReinitialize : true ,
52
+ onSubmit : async ( values ) => {
53
+ const params = {
54
+ search : values . search ,
55
+ priority : values . priority ,
56
+ supplyCategories : values . supplyCategories ,
57
+ supplies : values . supplies ,
58
+ filterAvailableShelter : values . filterAvailableShelter ,
59
+ filterUnavailableShelter : values . filterUnavailableShelter ,
60
+ waitingShelterAvailability : values . waitingShelterAvailability ,
61
+ } ;
62
+
63
+ props . handleSearch ( params ) ;
64
+ }
65
+ } ) ;
66
+
67
+ const initSupplyOptions = useMemo ( ( ) => {
68
+ const grouped = group ( result . data ?? [ ] , 'supplyCategory.name' ) ;
69
+ /// set suply itens from backend data
70
+ setSupplyOptions ( Object . entries ( grouped ) . map ( ( [ key , values ] ) => ( {
71
+ label : key ,
72
+ options : values . map ( ( v ) => {
73
+ return {
74
+ label : v . name ,
75
+ value : v . id
76
+ }
77
+ } ) ,
78
+ } ) ) ) ;
79
+
80
+ /// init default options array with the already selected supplies
81
+ const defaultOptions = props ?. filters ?. supplies . length > 0 ?
82
+ Object . entries ( grouped ) . reduce ( ( filtered : IComplexSelectData [ ] , option ) => {
83
+ const [ , values ] = option ;
84
+ for ( const value of values ) {
85
+ for ( const suply of props . filters . supplies ) {
86
+ if ( value . id === suply ) {
87
+ filtered . push ( {
88
+ label : value . name ,
89
+ value : value . id
90
+ } )
91
+ }
92
+ }
93
+ }
94
+
95
+ return filtered ;
96
+ } , [ ] ) : [ ] ;
97
+
98
+ return defaultOptions ;
99
+ } , [ result . data , props ?. filters ?. supplies ] ) ;
100
+
101
+ const handleSupplyCategoriesSelected = ( supplyCategoriesSelected : readonly IComplexSelectData [ ] ) => {
102
+ console . log ( 'init' )
103
+ const grouped = group ( result . data ?? [ ] , 'supplyCategory.name' ) ;
104
+
105
+ const supplyOptionsFiltered = Object . entries ( grouped ) . reduce ( ( filtered : IComplexSelectGroupedData [ ] , option ) => {
106
+ const [ key , values ] = option ;
107
+
108
+ const found = supplyCategoriesSelected . some ( element => {
109
+ return element . label === key ;
110
+ } ) ;
111
+
112
+ if ( found ) {
113
+ filtered . push ( {
114
+ label : key ,
115
+ options : values . map ( ( v ) => {
116
+ return {
117
+ label : v . name ,
118
+ value : v . id
119
+ }
120
+ } ) ,
121
+ } )
122
+ }
123
+
124
+ return filtered ;
125
+ } , [ ] ) ;
126
+
127
+ setFieldValue ( 'supplyCategories' , supplyCategoriesSelected . map ( el => el . value ) ) ;
128
+ setSupplyOptions ( supplyOptionsFiltered ) ;
129
+ }
130
+
131
+ const handleSupplySelected = ( supplySelected : readonly IComplexSelectData [ ] ) => {
132
+ setFieldValue ( 'supplies' , supplySelected . map ( el => el . value ) ) ;
133
+ }
134
+
135
+ const handlePriorityOptionSelected = ( priorityOptionSelected : IComplexSelectData ) => {
136
+ setFieldValue ( 'priority' , parseInt ( priorityOptionSelected . value ) ) ;
137
+ }
138
+
139
+ if ( loading || result . loading ) return < LoadingScreen /> ;
140
+
141
+ return (
142
+ < Dialog open = { props . isModalOpen } onOpenChange = { props . closeModal } >
143
+ < DialogContent className = "rounded-md" >
144
+ < DialogHeader className = 'pad-10' >
145
+ < DialogTitle className = "text-base font-medium" > Faça sua busca:</ DialogTitle >
146
+ </ DialogHeader >
147
+ < form onSubmit = { handleSubmit } >
148
+ < div className = "pl-4 pr-4 pb-4 flex flex-col max-w-5xl w-full items-start h-full" >
149
+ < div className = "flex flex-col gap-2 w-full my-4" >
150
+ < div className = "relative" >
151
+ < Input
152
+ placeholder = "Buscar por abrigo ou endereço"
153
+ className = "h-12 text-md font-medium text-zinc-600 pl-10 pr-4"
154
+ onChange = { ( ev ) => {
155
+ setFieldValue ( 'search' , ev . target . value ) ;
156
+ } }
157
+ />
158
+ < div className = "absolute inset-y-0 left-0 flex items-center pl-3" >
159
+ < Search name = "search" size = "20" className = "stroke-zinc-300" />
160
+ </ div >
161
+ </ div >
162
+ </ div >
163
+ < Separator className = "mt-2" />
164
+ < div className = "flex flex-col gap-2 w-full my-4" >
165
+ < p className = "text-muted-foreground text-sm md:text-lg font-medium" >
166
+ Busca avançada
167
+ </ p >
168
+ < p className = "text-muted-foreground text-sm md:text-lg font-medium" >
169
+ Você pode buscar pelo item que os abrigos precisam urgentemente de doação ou por itens que os abrigos tem disponibilidade para doar.
170
+ </ p >
171
+ < div className = "flex flex-col gap-1 w-full" >
172
+ < label className = "text-muted-foreground text-sm md:text-lg font-medium" > Status do item no abrigo</ label >
173
+ < Select
174
+ defaultValue = { props . filters . priority ? priorityOptions . filter ( el => props . filters . priority === parseInt ( el . value ) )
175
+ . map ( element => {
176
+ return {
177
+ 'label' : element . label ,
178
+ 'value' : element . value
179
+ }
180
+ } ) :
181
+ [ ] }
182
+ name = "colors"
183
+ placeholder = { < div > Selecione</ div > }
184
+ options = { priorityOptions as any }
185
+ className = "basic-select"
186
+ classNamePrefix = "select"
187
+ onChange = { ( priorityOptionsSelected : any ) => handlePriorityOptionSelected ( priorityOptionsSelected ) }
188
+ />
189
+ </ div >
190
+ < div className = "flex flex-col gap-1 w-full" >
191
+ < label className = "text-muted-foreground text-sm md:text-lg font-medium" > Categoria</ label >
192
+ < Select
193
+ defaultValue = { props . filters . supplyCategories ?
194
+ supplyCategories . filter ( el => props . filters . supplyCategories . some ( ( suplyCategoryId : string ) => suplyCategoryId === el . id ) ) . map ( element => {
195
+ return {
196
+ 'label' : element . name ,
197
+ 'value' : element . id
198
+ }
199
+ } ) :
200
+ [ ] }
201
+ isMulti
202
+ placeholder = { < div > Selecione</ div > }
203
+ name = "colors"
204
+ options = { supplyCategories . map ( ( element : ISupplyCategory ) => {
205
+ return {
206
+ 'label' : element . name ,
207
+ 'value' : element . id
208
+ }
209
+ } ) as any }
210
+ className = "basic-multi-select"
211
+ classNamePrefix = "select"
212
+ onChange = { ( supplyCategoriesSelected ) => handleSupplyCategoriesSelected ( supplyCategoriesSelected ) }
213
+ />
214
+ </ div >
215
+ < div className = "flex flex-col w-full" >
216
+ < label className = "text-muted-foreground text-sm md:text-lg font-medium" > Itens</ label >
217
+ < Select
218
+ defaultValue = { initSupplyOptions }
219
+ placeholder = { < div > Selecione</ div > }
220
+ isMulti
221
+ options = { supplyOptions }
222
+ onChange = { ( suppliesSelected ) => handleSupplySelected ( suppliesSelected ) }
223
+ />
224
+ </ div >
225
+ </ div >
226
+ < Separator className = "mt-2" />
227
+ < div className = "flex flex-col gap-2 w-full my-4" >
228
+ < p className = "text-muted-foreground text-sm md:text-lg font-medium" >
229
+ Status do abrigo
230
+ </ p >
231
+ < div >
232
+ < label className = "flex items-center mb-4" >
233
+ < input
234
+ name = "filterAvailableShelter"
235
+ type = "checkbox"
236
+ className = "mr-2 w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
237
+ onChange = { ( ) => setFieldValue ( 'filterAvailableShelter' , ! values . filterAvailableShelter ) }
238
+ defaultChecked = { values . filterAvailableShelter }
239
+ />
240
+ Abrigo Disponivel
241
+ </ label >
242
+ </ div >
243
+ < div >
244
+ < label className = "flex items-center mb-4" >
245
+ < input
246
+ name = "filterUnavailableShelter"
247
+ type = "checkbox"
248
+ className = "mr-2 w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
249
+ onChange = { ( ) => setFieldValue ( 'filterUnavailableShelter' , ! values . filterUnavailableShelter ) }
250
+ defaultChecked = { values . filterUnavailableShelter }
251
+ />
252
+ Abrigo Indisponível
253
+ </ label >
254
+ </ div >
255
+ < div >
256
+ < label className = "flex items-center mb-4" >
257
+ < input
258
+ name = "waitingShelterAvailability"
259
+ type = "checkbox"
260
+ className = "mr-2 w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
261
+ onChange = { ( ) => setFieldValue ( 'waitingShelterAvailability' , ! values . waitingShelterAvailability ) }
262
+ defaultChecked = { values . waitingShelterAvailability }
263
+ />
264
+ Aguardando disponibilidade
265
+ </ label >
266
+ </ div >
267
+ </ div >
268
+
269
+ < div className = "flex flex-1 flex-col justify-end md:justify-start w-full py-6" >
270
+ < Button
271
+ type = "submit"
272
+ className = "flex gap-2 text-white font-medium text-lg bg-blue-500 hover:bg-blue-600 w-full"
273
+ >
274
+ Filtrar resultados
275
+ </ Button >
276
+ </ div >
277
+ </ div >
278
+ </ form >
279
+ </ DialogContent >
280
+ </ Dialog >
281
+ ) ;
282
+ } ;
283
+
284
+ export { Filter } ;
0 commit comments