11import { useState , useEffect } from 'react' ;
22import { Container , Row , Col , Card , CardBody , Button , Input } from 'reactstrap' ;
3+ import { FaCalendarAlt , FaMapMarkerAlt , FaUserAlt , FaSearch , FaTimes } from 'react-icons/fa' ;
34import styles from './CPDashboard.module.css' ;
4- import { FaCalendarAlt , FaMapMarkerAlt , FaUserAlt } from 'react-icons/fa' ;
55import { ENDPOINTS } from '../../utils/URL' ;
66import axios from 'axios' ;
77
8+ const FixedRatioImage = ( { src, alt, fallback } ) => (
9+ < div
10+ style = { {
11+ width : '100%' ,
12+ aspectRatio : '4 / 3' ,
13+ overflow : 'hidden' ,
14+ background : '#f2f2f2' ,
15+ } }
16+ >
17+ < img
18+ src = { src || fallback }
19+ alt = { alt }
20+ loading = "lazy"
21+ onError = { e => {
22+ if ( e . currentTarget . src !== fallback ) e . currentTarget . src = fallback ;
23+ } }
24+ style = { {
25+ width : '100%' ,
26+ height : '100%' ,
27+ objectFit : 'cover' ,
28+ display : 'block' ,
29+ } }
30+ />
31+ </ div >
32+ ) ;
33+
834export function CPDashboard ( ) {
935 const [ events , setEvents ] = useState ( [ ] ) ;
10- const [ search , setSearch ] = useState ( '' ) ;
36+ const [ searchInput , setSearchInput ] = useState ( '' ) ;
37+ const [ searchQuery , setSearchQuery ] = useState ( '' ) ;
1138 const [ isLoading , setIsLoading ] = useState ( false ) ;
1239 const [ error , setError ] = useState ( null ) ;
1340 const [ pagination , setPagination ] = useState ( {
@@ -20,40 +47,18 @@ export function CPDashboard() {
2047 const FALLBACK_IMG =
2148 'https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=600&q=60' ;
2249
23- const FixedRatioImage = ( { src, alt, fallback } ) => (
24- < div
25- style = { {
26- width : '100%' ,
27- aspectRatio : '4 / 3' ,
28- overflow : 'hidden' ,
29- background : '#f2f2f2' ,
30- } }
31- >
32- < img
33- src = { src || fallback }
34- alt = { alt }
35- loading = "lazy"
36- onError = { e => {
37- if ( e . currentTarget . src !== fallback ) e . currentTarget . src = fallback ;
38- } }
39- style = { {
40- width : '100%' ,
41- height : '100%' ,
42- objectFit : 'cover' ,
43- display : 'block' ,
44- } }
45- />
46- </ div >
47- ) ;
48-
4950 useEffect ( ( ) => {
5051 const fetchEvents = async ( ) => {
5152 setIsLoading ( true ) ;
5253
5354 try {
5455 const response = await axios . get ( ENDPOINTS . EVENTS ) ;
5556 console . log ( 'Fetched events:' , response . data . events ) ;
56- setEvents ( response . data . events ) ;
57+ setEvents ( response . data . events || [ ] ) ;
58+ setPagination ( prev => ( {
59+ ...prev ,
60+ total : response . data . events ?. length || 0 ,
61+ } ) ) ;
5762 } catch ( err ) {
5863 console . error ( 'Here' , err ) ;
5964 setError ( 'Failed to load events' ) ;
@@ -65,6 +70,20 @@ export function CPDashboard() {
6570 fetchEvents ( ) ;
6671 } , [ ] ) ;
6772
73+ const handleSearchClick = ( ) => {
74+ const trimmed = searchInput . trim ( ) ;
75+ setSearchQuery ( trimmed ) ;
76+ setPagination ( prev => ( { ...prev , currentPage : 1 } ) ) ;
77+ } ;
78+
79+ const handleSearchKeyDown = e => {
80+ if ( e . key === 'Enter' ) {
81+ const trimmed = searchInput . trim ( ) ;
82+ setSearchQuery ( trimmed ) ;
83+ setPagination ( prev => ( { ...prev , currentPage : 1 } ) ) ;
84+ }
85+ } ;
86+
6887 const formatDate = dateStr => {
6988 if ( ! dateStr ) return 'Date TBD' ;
7089 const date = new Date ( dateStr ) ;
@@ -77,30 +96,84 @@ export function CPDashboard() {
7796 } ) ;
7897 } ;
7998
80- const filteredEvents = events . filter ( event =>
81- event . title ?. toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ,
82- ) ;
99+ const filteredEvents = events . filter ( event => {
100+ if ( ! searchQuery ) return true ;
101+ const term = searchQuery . toLowerCase ( ) ;
102+
103+ return (
104+ event . title ?. toLowerCase ( ) . includes ( term ) ||
105+ event . location ?. toLowerCase ( ) . includes ( term ) ||
106+ event . organizer ?. toLowerCase ( ) . includes ( term )
107+ ) ;
108+ } ) ;
83109
84- const totalPages = Math . ceil ( filteredEvents . length / pagination . limit ) ;
110+ const totalPages = Math . ceil ( filteredEvents . length / pagination . limit ) || 1 ;
85111
86112 const displayedEvents = filteredEvents . slice (
87113 ( pagination . currentPage - 1 ) * pagination . limit ,
88114 pagination . currentPage * pagination . limit ,
89115 ) ;
90116
117+ const goToPage = newPage => {
118+ if ( newPage < 1 || newPage > totalPages ) return ;
119+ setPagination ( prev => ( { ...prev , currentPage : newPage } ) ) ;
120+ } ;
121+
122+ if ( isLoading ) {
123+ return (
124+ < Container className = { styles [ 'dashboard-container' ] } >
125+ < p > Loading events...</ p >
126+ </ Container >
127+ ) ;
128+ }
129+
130+ if ( error ) {
131+ return (
132+ < Container className = { styles [ 'dashboard-container' ] } >
133+ < p className = { styles [ 'error-text' ] } > { error } </ p >
134+ </ Container >
135+ ) ;
136+ }
137+
91138 return (
92139 < Container className = { styles [ 'dashboard-container' ] } >
93140 < header className = { styles [ 'dashboard-header' ] } >
94141 < h1 > All Events</ h1 >
142+
95143 < div className = { styles [ 'dashboard-controls' ] } >
96144 < div className = { styles [ 'dashboard-search-container' ] } >
97145 < Input
146+ id = "search"
98147 type = "search"
99148 placeholder = "Search events..."
100- value = { search }
101- onChange = { e => setSearch ( e . target . value ) }
102- className = { styles [ 'dashboard-search' ] }
149+ value = { searchInput }
150+ onChange = { e => setSearchInput ( e . target . value ) }
151+ onKeyDown = { handleSearchKeyDown }
152+ className = { styles [ 'dashboard-search-input' ] }
103153 />
154+
155+ { searchInput && (
156+ < button
157+ type = "button"
158+ className = { styles [ 'dashboard-clear-btn' ] }
159+ onClick = { ( ) => {
160+ setSearchInput ( '' ) ;
161+ setSearchQuery ( '' ) ;
162+ setPagination ( prev => ( { ...prev , currentPage : 1 } ) ) ;
163+ } }
164+ >
165+ < FaTimes />
166+ </ button >
167+ ) }
168+
169+ < button
170+ type = "button"
171+ className = { styles [ 'dashboard-search-icon-btn' ] }
172+ onClick = { handleSearchClick }
173+ aria-label = "Search events"
174+ >
175+ < FaSearch />
176+ </ button >
104177 </ div >
105178 </ div >
106179 </ header >
@@ -122,24 +195,28 @@ export function CPDashboard() {
122195 </ div >
123196 < Input type = "date" placeholder = "Ending After" className = { styles [ 'date-filter' ] } />
124197 </ div >
198+
125199 < div className = { styles [ 'filter-item' ] } >
126200 < label htmlFor = "online-only" > Online</ label >
127201 < div >
128202 < Input type = "checkbox" /> Online Only
129203 </ div >
130204 </ div >
205+
131206 < div className = { styles [ 'filter-item' ] } >
132207 < label htmlFor = "branches" > Branches</ label >
133208 < Input type = "select" >
134209 < option > Select branches</ option >
135210 </ Input >
136211 </ div >
212+
137213 < div className = { styles [ 'filter-item' ] } >
138214 < label htmlFor = "themes" > Themes</ label >
139215 < Input type = "select" >
140216 < option > Select themes</ option >
141217 </ Input >
142218 </ div >
219+
143220 < div className = { styles [ 'filter-item' ] } >
144221 < label htmlFor = "categories" > Categories</ label >
145222 < Input type = "select" >
@@ -152,22 +229,23 @@ export function CPDashboard() {
152229
153230 < Col md = { 9 } className = { styles [ 'dashboard-main' ] } >
154231 < h2 className = { styles [ 'section-title' ] } > Events</ h2 >
232+
155233 < Row >
156- { events . length > 0 ? (
157- events . map ( event => (
234+ { displayedEvents . length > 0 ? (
235+ displayedEvents . map ( event => (
158236 < Col md = { 4 } key = { event . id } className = { styles [ 'event-card-col' ] } >
159237 < Card className = { styles [ 'event-card' ] } >
160238 < div className = { styles [ 'event-card-img-container' ] } >
161- < img
239+ < FixedRatioImage
162240 src = { event . image }
163241 alt = { event . title }
164- className = { styles [ 'event-card-img' ] }
242+ fallback = { FALLBACK_IMG }
165243 />
166244 </ div >
167245 < CardBody >
168246 < h5 className = { styles [ 'event-title' ] } > { event . title } </ h5 >
169247 < p className = { styles [ 'event-date' ] } >
170- < FaCalendarAlt className = { styles [ 'event-icon' ] } /> { event . date }
248+ < FaCalendarAlt className = { styles [ 'event-icon' ] } /> { formatDate ( event . date ) }
171249 </ p >
172250 < p className = { styles [ 'event-location' ] } >
173251 < FaMapMarkerAlt className = { styles [ 'event-icon' ] } /> { event . location }
@@ -183,6 +261,30 @@ export function CPDashboard() {
183261 < div className = { styles [ 'no-events' ] } > No events available</ div >
184262 ) }
185263 </ Row >
264+
265+ { /* Simple pagination controls if needed */ }
266+ { totalPages > 1 && (
267+ < div className = { styles [ 'pagination-container' ] } >
268+ < Button
269+ color = "secondary"
270+ disabled = { pagination . currentPage === 1 }
271+ onClick = { ( ) => goToPage ( pagination . currentPage - 1 ) }
272+ >
273+ Previous
274+ </ Button >
275+ < span className = { styles [ 'pagination-info' ] } >
276+ Page { pagination . currentPage } of { totalPages }
277+ </ span >
278+ < Button
279+ color = "secondary"
280+ disabled = { pagination . currentPage === totalPages }
281+ onClick = { ( ) => goToPage ( pagination . currentPage + 1 ) }
282+ >
283+ Next
284+ </ Button >
285+ </ div >
286+ ) }
287+
186288 < div className = { styles [ 'dashboard-actions' ] } >
187289 < Button color = "primary" > Show Past Events</ Button >
188290 </ div >
0 commit comments