1+ import React , { useState , useEffect , useCallback } from 'react' ;
2+ import {
3+ Box , Typography , TextField , IconButton , Grid , Pagination ,
4+ CircularProgress , FormControl , InputLabel , MenuItem , Select ,
5+ } from '@mui/material' ;
6+ import {
7+ Clear as ClearIcon ,
8+ ArrowUpward as ArrowUpIcon ,
9+ ArrowDownward as ArrowDownIcon ,
10+ SortByAlpha
11+ } from '@mui/icons-material' ;
12+ import { styled } from '@mui/material/styles' ;
13+ import { CalendarIcon } from '@mui/x-date-pickers' ;
14+ import { onAuthStateChanged } from 'firebase/auth' ;
15+ import CardItemAcervo from '../components/CardItemAcervo/CardItemAcervo' ;
16+ import { ItemAcervo } from '../interfaces/ItemAcervo' ;
17+ import { auth } from '../../firebase/firebase' ;
18+ import { getItensAcervo } from '../Utils/itemAcervoFirebase' ;
19+
20+ const Content = styled ( Box ) ( ( { theme } ) => ( {
21+ padding : theme . spacing ( 3 ) ,
22+ display : 'flex' ,
23+ flexDirection : 'column' ,
24+ gap : theme . spacing ( 3 )
25+ } ) ) ;
26+
27+ const Heading = styled ( Box ) ( ( ) => ( {
28+ textAlign : 'center'
29+ } ) ) ;
30+
31+ const List = styled ( Box ) ( ( { theme } ) => ( {
32+ display : 'flex' ,
33+ flexDirection : 'column' ,
34+ gap : theme . spacing ( 2 )
35+ } ) ) ;
36+
37+ const Filter = styled ( Box ) ( ( { theme } ) => ( {
38+ display : 'flex' ,
39+ gap : theme . spacing ( 2 )
40+ } ) ) ;
41+
42+ const Description = styled ( Box ) ( ( ) => ( {
43+ display : 'flex' ,
44+ justifyContent : 'space-between' ,
45+ alignItems : 'center'
46+ } ) ) ;
47+
48+ const Acervo : React . FC = ( ) => {
49+ const [ itens , setItens ] = useState < ItemAcervo [ ] > ( [ ] ) ;
50+ const [ filteredItens , setFilteredItens ] = useState < ItemAcervo [ ] > ( [ ] ) ;
51+ const [ loading , setLoading ] = useState ( true ) ;
52+ const [ error , setError ] = useState ( '' ) ;
53+
54+ const [ nomeFilter , setNomeFilter ] = useState ( '' ) ;
55+ const [ colecaoFilter , setColecaoFilter ] = useState ( '' ) ;
56+ const [ colecoes , setColecoes ] = useState < string [ ] > ( [ ] ) ;
57+
58+ const [ alfabeticOrder , setAlfabeticOrder ] = useState < 'asc' | 'desc' | 'none' > ( 'none' ) ;
59+ const [ dateOrder , setDateOrder ] = useState < 'asc' | 'desc' | 'none' > ( 'desc' ) ;
60+
61+ const [ page , setPage ] = useState ( 1 ) ;
62+ const itemsPerPage = 6 ;
63+
64+ const [ isLoggedIn , setIsLoggedIn ] = useState ( false ) ;
65+
66+ useEffect ( ( ) => {
67+ const unsubscribe = onAuthStateChanged ( auth , ( user ) => {
68+ setIsLoggedIn ( ! ! user ) ;
69+ } ) ;
70+
71+ return ( ) => unsubscribe ( ) ;
72+ } , [ ] ) ;
73+
74+ const fetchItens = useCallback ( async ( ) => {
75+ try {
76+ setLoading ( true ) ;
77+ const data = await getItensAcervo ( isLoggedIn ) ;
78+ setItens ( data ) ;
79+ setFilteredItens ( data ) ;
80+ const uniqueColecoes = Array . from ( new Set ( data . map ( item => item . colecao ) ) ) ;
81+ setColecoes ( uniqueColecoes ) ;
82+ } catch ( err ) {
83+ setError ( 'Erro ao buscar itens do acervo' ) ;
84+ } finally {
85+ // Add a slight delay to ensure the loading state is visible
86+ setTimeout ( ( ) => setLoading ( false ) , 500 ) ;
87+ }
88+ } , [ isLoggedIn ] ) ;
89+
90+ useEffect ( ( ) => {
91+ fetchItens ( ) ;
92+ } , [ fetchItens ] ) ;
93+
94+ useEffect ( ( ) => {
95+ let filtered = itens ;
96+
97+ if ( nomeFilter ) {
98+ filtered = filtered . filter ( item =>
99+ item . nome . toLowerCase ( ) . includes ( nomeFilter . toLowerCase ( ) )
100+ ) ;
101+ }
102+
103+ if ( colecaoFilter ) {
104+ filtered = filtered . filter ( item => item . colecao === colecaoFilter ) ;
105+ }
106+
107+ if ( ! isLoggedIn ) {
108+ filtered = filtered . filter ( item => ! item . privado ) ;
109+ }
110+
111+ if ( alfabeticOrder !== 'none' ) {
112+ filtered . sort ( ( a , b ) => {
113+ return alfabeticOrder === 'asc'
114+ ? a . nome . localeCompare ( b . nome )
115+ : b . nome . localeCompare ( a . nome ) ;
116+ } ) ;
117+ } else if ( dateOrder !== 'none' ) {
118+ filtered . sort ( ( a , b ) => {
119+ const dateA = a . dataDoacao instanceof Date ? a . dataDoacao . getTime ( ) : 0 ;
120+ const dateB = b . dataDoacao instanceof Date ? b . dataDoacao . getTime ( ) : 0 ;
121+ return dateOrder === 'asc' ? dateA - dateB : dateB - dateA ;
122+ } ) ;
123+ }
124+
125+ setFilteredItens ( filtered ) ;
126+ setPage ( 1 ) ;
127+ } , [ itens , nomeFilter , colecaoFilter , alfabeticOrder , dateOrder , isLoggedIn ] ) ;
128+
129+ const clearFilters = useCallback ( ( ) => {
130+ setNomeFilter ( '' ) ;
131+ setColecaoFilter ( '' ) ;
132+ setAlfabeticOrder ( 'none' ) ;
133+ setDateOrder ( 'desc' ) ;
134+ } , [ ] ) ;
135+
136+ const toggleAlfabeticOrder = useCallback ( ( ) => {
137+ setAlfabeticOrder ( prev => {
138+ if ( prev === 'asc' ) return 'desc' ;
139+ if ( prev === 'desc' ) return 'none' ;
140+ return 'asc' ;
141+ } ) ;
142+ setDateOrder ( 'none' ) ;
143+ setPage ( 1 ) ;
144+ } , [ ] ) ;
145+
146+ const toggleDateOrder = useCallback ( ( ) => {
147+ setDateOrder ( prev => {
148+ if ( prev === 'asc' ) return 'desc' ;
149+ if ( prev === 'desc' ) return 'none' ;
150+ return 'asc' ;
151+ } ) ;
152+ setAlfabeticOrder ( 'none' ) ;
153+ setPage ( 1 ) ;
154+ } , [ ] ) ;
155+
156+ const handlePageChange = useCallback ( ( event : React . ChangeEvent < unknown > , value : number ) => {
157+ setPage ( value ) ;
158+ void ( event )
159+ } , [ ] ) ;
160+
161+ if ( loading ) {
162+ return < CircularProgress data-cy = "loading" /> ;
163+ }
164+
165+ if ( error ) {
166+ return < Typography color = "error" data-cy = "error-message" > { error } </ Typography > ;
167+ }
168+
169+ const paginatedItens = filteredItens . slice (
170+ ( page - 1 ) * itemsPerPage ,
171+ page * itemsPerPage
172+ ) ;
173+
174+ return (
175+ < Content data-cy = "acervo-content" >
176+ < Heading >
177+ < Typography variant = "h4" sx = { { paddingTop : '32px' } } data-cy = "acervo-title" > Acervo</ Typography >
178+ < Typography variant = "subtitle1" data-cy = "acervo-subtitle" > Explore os itens do nosso acervo</ Typography >
179+ </ Heading >
180+
181+ < List sx = { {
182+ display : 'flex' ,
183+ alignItems : 'center' ,
184+ } }
185+ >
186+ < Filter >
187+ < TextField
188+ label = "Nome do item"
189+ value = { nomeFilter }
190+ onChange = { ( e ) => setNomeFilter ( e . target . value ) }
191+ inputProps = { { 'data-cy' : 'nome-filter' } }
192+ />
193+ < FormControl sx = { { minWidth : '150px' } } >
194+ < InputLabel > Coleção</ InputLabel >
195+ < Select
196+ value = { colecaoFilter }
197+ onChange = { ( e ) => setColecaoFilter ( e . target . value as string ) }
198+ label = "Coleção"
199+ inputProps = { { 'data-cy' : 'colecao-filter' } }
200+ >
201+ < MenuItem value = "" > Todas</ MenuItem >
202+ { colecoes . map ( ( colecao ) => (
203+ < MenuItem key = { colecao } value = { colecao } > { colecao } </ MenuItem >
204+ ) ) }
205+ </ Select >
206+ </ FormControl >
207+ </ Filter >
208+
209+ < Description >
210+ < Box >
211+ < IconButton onClick = { toggleAlfabeticOrder } data-cy = "alfabetic-order" >
212+ < SortByAlpha />
213+ { alfabeticOrder === 'asc' ? < ArrowUpIcon /> : alfabeticOrder === 'desc' ? < ArrowDownIcon /> : null }
214+ </ IconButton >
215+ < IconButton onClick = { toggleDateOrder } data-cy = "date-order" >
216+ < CalendarIcon />
217+ { dateOrder === 'asc' ? < ArrowUpIcon /> : dateOrder === 'desc' ? < ArrowDownIcon /> : null }
218+ </ IconButton >
219+ < IconButton onClick = { clearFilters } data-cy = "clear-filters" >
220+ < ClearIcon />
221+ </ IconButton >
222+ </ Box >
223+ < Typography data-cy = "items-count" >
224+ { filteredItens . length } itens encontrados
225+ </ Typography >
226+ </ Description >
227+
228+ < Pagination
229+ count = { Math . ceil ( filteredItens . length / itemsPerPage ) }
230+ page = { page }
231+ onChange = { handlePageChange }
232+ data-cy = "pagination"
233+ />
234+
235+ { filteredItens . length === 0 ? (
236+ < Typography data-cy = "no-items-message" > Nenhum item encontrado com os filtros atuais</ Typography >
237+ ) : (
238+ < >
239+ < Grid container spacing = { 2 } data-cy = "items-grid" >
240+ { paginatedItens . map ( item => (
241+ < Grid item xs = { 12 } sm = { 6 } md = { 4 } key = { item . id } >
242+ < CardItemAcervo item = { item } data-cy = "item-card" data-id = { item . id } />
243+ </ Grid >
244+ ) ) }
245+ </ Grid >
246+ < Pagination
247+ count = { Math . ceil ( filteredItens . length / itemsPerPage ) }
248+ page = { page }
249+ onChange = { handlePageChange }
250+ data-cy = "pagination"
251+ />
252+ </ >
253+ ) }
254+ </ List >
255+ </ Content >
256+ ) ;
257+ } ;
258+
259+ export default Acervo ;
0 commit comments