@@ -2,41 +2,92 @@ import { useState, useEffect } from 'react';
22import { Container , Row , Col , Card , CardBody , Button , Input } from 'reactstrap' ;
33import './CPDashboard.css' ;
44import { FaCalendarAlt , FaMapMarkerAlt , FaUserAlt } from 'react-icons/fa' ;
5+ import { ENDPOINTS } from '../../utils/URL' ;
6+ import axios from 'axios' ;
57
68export function CPDashboard ( ) {
79 const [ events , setEvents ] = useState ( [ ] ) ;
810 const [ search , setSearch ] = useState ( '' ) ;
11+ const [ isLoading , setIsLoading ] = useState ( false ) ;
12+ const [ error , setError ] = useState ( null ) ;
13+ const [ pagination , setPagination ] = useState ( {
14+ currentPage : 1 ,
15+ totalPages : 5 ,
16+ total : 0 ,
17+ limit : 6 ,
18+ } ) ;
19+
20+ const FALLBACK_IMG =
21+ 'https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=600&q=60' ;
22+
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+ ) ;
948
1049 useEffect ( ( ) => {
11- const mockEvents = [
12- {
13- id : 1 ,
14- title : 'PGSA Lunch Talks' ,
15- date : 'Friday, December 6 at 12:00PM EST' ,
16- location : 'Disque 919' ,
17- organizer : 'Physics Graduate Student Association' ,
18- image : 'https://via.placeholder.com/300' ,
19- } ,
20- {
21- id : 2 ,
22- title : 'Hot Chocolate/Bake Sale' ,
23- date : 'Friday, December 6 at 12:00PM EST' ,
24- location : 'G.C LeBow - Lobby Tabling Space 2' ,
25- organizer : 'Kappa Phi Gamma, Sorority Inc.' ,
26- image : 'https://via.placeholder.com/300' ,
27- } ,
28- {
29- id : 3 ,
30- title : 'Holiday Lunch' ,
31- date : 'Friday, December 6 at 12:00PM EST' ,
32- location : 'Hill Conference Room' ,
33- organizer : 'Chemical and Biological Engineering Graduate Society' ,
34- image : 'https://via.placeholder.com/300' ,
35- } ,
36- ] ;
37- setEvents ( mockEvents ) ;
50+ const fetchEvents = async ( ) => {
51+ setIsLoading ( true ) ;
52+
53+ try {
54+ const response = await axios . get ( ENDPOINTS . EVENTS ) ;
55+ console . log ( 'Fetched events:' , response . data . events ) ;
56+ setEvents ( response . data . events ) ;
57+ } catch ( err ) {
58+ console . error ( 'Here' , err ) ;
59+ setError ( 'Failed to load events' ) ;
60+ } finally {
61+ setIsLoading ( false ) ;
62+ }
63+ } ;
64+
65+ fetchEvents ( ) ;
3866 } , [ ] ) ;
3967
68+ const formatDate = dateStr => {
69+ if ( ! dateStr ) return 'Date TBD' ;
70+ const date = new Date ( dateStr ) ;
71+ return date . toLocaleString ( 'en-US' , {
72+ weekday : 'long' ,
73+ month : 'long' ,
74+ day : 'numeric' ,
75+ hour : 'numeric' ,
76+ minute : '2-digit' ,
77+ } ) ;
78+ } ;
79+
80+ const filteredEvents = events . filter ( event =>
81+ event . title ?. toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ,
82+ ) ;
83+
84+ const totalPages = Math . ceil ( filteredEvents . length / pagination . limit ) ;
85+
86+ const displayedEvents = filteredEvents . slice (
87+ ( pagination . currentPage - 1 ) * pagination . limit ,
88+ pagination . currentPage * pagination . limit ,
89+ ) ;
90+
4091 return (
4192 < Container fluid className = "dashboard-container" >
4293 < header className = "dashboard-header" >
@@ -51,17 +102,6 @@ export function CPDashboard() {
51102 className = "dashboard-search"
52103 />
53104 </ div >
54- { /* <Dropdown isOpen={dropdownOpen} toggle={toggleDropdown} className="community-dropdown">
55- <DropdownToggle caret color="secondary">
56- Community Portal
57- </DropdownToggle>
58- <DropdownMenu>
59- <DropdownItem onClick={() => handleNavigation('/home')}>Home</DropdownItem>
60- <DropdownItem onClick={() => handleNavigation('/events')}>Events</DropdownItem>
61- <DropdownItem onClick={() => handleNavigation('/about')}>About Us</DropdownItem>
62- <DropdownItem onClick={() => handleNavigation('/contact')}>Contact</DropdownItem>
63- </DropdownMenu>
64- </Dropdown> */ }
65105 </ div >
66106 </ header >
67107
@@ -110,34 +150,81 @@ export function CPDashboard() {
110150
111151 < Col md = { 9 } className = "dashboard-main" >
112152 < h2 className = "section-title" > Events</ h2 >
113- < Row >
114- { events . length > 0 ? (
115- events . map ( event => (
116- < Col md = { 4 } key = { event . id } className = "event-card-col" >
117- < Card className = "event-card" >
153+
154+ { error && < div className = "alert alert-danger" > { error } </ div > }
155+
156+ { isLoading ? (
157+ < div className = "d-flex justify-content-center mt-4" > </ div >
158+ ) : displayedEvents . length > 0 ? (
159+ < Row >
160+ { displayedEvents . map ( event => (
161+ < Col md = { 4 } key = { event . _id || event . id } className = "event-card-col" >
162+ < Card
163+ className = "event-card"
164+ style = { {
165+ display : 'flex' ,
166+ flexDirection : 'column' ,
167+ borderRadius : 14 ,
168+ overflow : 'hidden' ,
169+ } }
170+ >
118171 < div className = "event-card-img-container" >
119- < img src = { event . image } alt = { event . title } className = "event-card-img" />
172+ < FixedRatioImage
173+ src = { event . coverImage }
174+ alt = { event . title }
175+ fallback = { FALLBACK_IMG }
176+ />
120177 </ div >
121- < CardBody >
178+ < CardBody style = { { flex : 1 , display : 'flex' , flexDirection : 'column' } } >
122179 < h5 className = "event-title" > { event . title } </ h5 >
123180 < p className = "event-date" >
124- < FaCalendarAlt className = "event-icon" /> { event . date }
181+ < FaCalendarAlt className = "event-icon" /> { formatDate ( event . date ) }
125182 </ p >
126183 < p className = "event-location" >
127- < FaMapMarkerAlt className = "event-icon" /> { event . location }
184+ < FaMapMarkerAlt className = "event-icon" /> { event . location || 'TBD' }
128185 </ p >
129186 < p className = "event-organizer" >
130- < FaUserAlt className = "event-icon" /> { event . organizer }
187+ < FaUserAlt className = "event-icon" /> { event . maxAttendees || 'No limit' } { ' ' }
188+ Attendees limit
131189 </ p >
132190 </ CardBody >
133191 </ Card >
134192 </ Col >
135- ) )
136- ) : (
137- < div className = "no-events" > No events available</ div >
138- ) }
139- </ Row >
140- < div className = "dashboard-actions" >
193+ ) ) }
194+ </ Row >
195+ ) : (
196+ < div className = "no-events" > No events available</ div >
197+ ) }
198+
199+ < div className = "d-flex justify-content-center mt-4" >
200+ < div className = "pagination-controls" >
201+ < Button
202+ color = "secondary"
203+ disabled = { pagination . currentPage === 1 }
204+ onClick = { ( ) =>
205+ setPagination ( prev => ( { ...prev , currentPage : prev . currentPage - 1 } ) )
206+ }
207+ >
208+ Previous
209+ </ Button >
210+
211+ < span className = "mx-3" >
212+ Page { pagination . currentPage } of { totalPages }
213+ </ span >
214+
215+ < Button
216+ color = "secondary"
217+ disabled = { pagination . currentPage === totalPages }
218+ onClick = { ( ) =>
219+ setPagination ( prev => ( { ...prev , currentPage : prev . currentPage + 1 } ) )
220+ }
221+ >
222+ Next
223+ </ Button >
224+ </ div >
225+ </ div >
226+
227+ < div className = "dashboard-actions text-center mt-4" >
141228 < Button color = "primary" > Show Past Events</ Button >
142229 </ div >
143230 </ Col >
0 commit comments