Skip to content

Commit 5847556

Browse files
Merge pull request #4466 from OneCommunityGlobal/Ajay-fixed-search-bar-in-communityportal
Ajay fixed search bar in community portal
2 parents 172e09c + fdd6110 commit 5847556

3 files changed

Lines changed: 310 additions & 85 deletions

File tree

src/components/CommunityPortal/CPDashboard.jsx

Lines changed: 143 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,40 @@
11
import { useState, useEffect } from 'react';
22
import { Container, Row, Col, Card, CardBody, Button, Input } from 'reactstrap';
3+
import { FaCalendarAlt, FaMapMarkerAlt, FaUserAlt, FaSearch, FaTimes } from 'react-icons/fa';
34
import styles from './CPDashboard.module.css';
4-
import { FaCalendarAlt, FaMapMarkerAlt, FaUserAlt } from 'react-icons/fa';
55
import { ENDPOINTS } from '../../utils/URL';
66
import 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+
834
export 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

Comments
 (0)