Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 143 additions & 41 deletions src/components/CommunityPortal/CPDashboard.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,40 @@
import { useState, useEffect } from 'react';
import { Container, Row, Col, Card, CardBody, Button, Input } from 'reactstrap';
import { FaCalendarAlt, FaMapMarkerAlt, FaUserAlt, FaSearch, FaTimes } from 'react-icons/fa';
import styles from './CPDashboard.module.css';
import { FaCalendarAlt, FaMapMarkerAlt, FaUserAlt } from 'react-icons/fa';
import { ENDPOINTS } from '../../utils/URL';
import axios from 'axios';

const FixedRatioImage = ({ src, alt, fallback }) => (
<div
style={{
width: '100%',
aspectRatio: '4 / 3',
overflow: 'hidden',
background: '#f2f2f2',
}}
>
<img
src={src || fallback}
alt={alt}
loading="lazy"
onError={e => {
if (e.currentTarget.src !== fallback) e.currentTarget.src = fallback;
}}
style={{
width: '100%',
height: '100%',
objectFit: 'cover',
display: 'block',
}}
/>
</div>
);

export function CPDashboard() {
const [events, setEvents] = useState([]);
const [search, setSearch] = useState('');
const [searchInput, setSearchInput] = useState('');
const [searchQuery, setSearchQuery] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [pagination, setPagination] = useState({
Expand All @@ -20,40 +47,18 @@ export function CPDashboard() {
const FALLBACK_IMG =
'https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=600&q=60';

const FixedRatioImage = ({ src, alt, fallback }) => (
<div
style={{
width: '100%',
aspectRatio: '4 / 3',
overflow: 'hidden',
background: '#f2f2f2',
}}
>
<img
src={src || fallback}
alt={alt}
loading="lazy"
onError={e => {
if (e.currentTarget.src !== fallback) e.currentTarget.src = fallback;
}}
style={{
width: '100%',
height: '100%',
objectFit: 'cover',
display: 'block',
}}
/>
</div>
);

useEffect(() => {
const fetchEvents = async () => {
setIsLoading(true);

try {
const response = await axios.get(ENDPOINTS.EVENTS);
console.log('Fetched events:', response.data.events);
setEvents(response.data.events);
setEvents(response.data.events || []);
setPagination(prev => ({
...prev,
total: response.data.events?.length || 0,
}));
} catch (err) {
console.error('Here', err);
setError('Failed to load events');
Expand All @@ -65,6 +70,20 @@ export function CPDashboard() {
fetchEvents();
}, []);

const handleSearchClick = () => {
const trimmed = searchInput.trim();
setSearchQuery(trimmed);
setPagination(prev => ({ ...prev, currentPage: 1 }));
};

const handleSearchKeyDown = e => {
if (e.key === 'Enter') {
const trimmed = searchInput.trim();
setSearchQuery(trimmed);
setPagination(prev => ({ ...prev, currentPage: 1 }));
}
};

const formatDate = dateStr => {
if (!dateStr) return 'Date TBD';
const date = new Date(dateStr);
Expand All @@ -77,30 +96,84 @@ export function CPDashboard() {
});
};

const filteredEvents = events.filter(event =>
event.title?.toLowerCase().includes(search.toLowerCase()),
);
const filteredEvents = events.filter(event => {
if (!searchQuery) return true;
const term = searchQuery.toLowerCase();

return (
event.title?.toLowerCase().includes(term) ||
event.location?.toLowerCase().includes(term) ||
event.organizer?.toLowerCase().includes(term)
);
});

const totalPages = Math.ceil(filteredEvents.length / pagination.limit);
const totalPages = Math.ceil(filteredEvents.length / pagination.limit) || 1;

const displayedEvents = filteredEvents.slice(
(pagination.currentPage - 1) * pagination.limit,
pagination.currentPage * pagination.limit,
);

const goToPage = newPage => {
if (newPage < 1 || newPage > totalPages) return;
setPagination(prev => ({ ...prev, currentPage: newPage }));
};

if (isLoading) {
return (
<Container className={styles['dashboard-container']}>
<p>Loading events...</p>
</Container>
);
}

if (error) {
return (
<Container className={styles['dashboard-container']}>
<p className={styles['error-text']}>{error}</p>
</Container>
);
}

return (
<Container className={styles['dashboard-container']}>
<header className={styles['dashboard-header']}>
<h1>All Events</h1>

<div className={styles['dashboard-controls']}>
<div className={styles['dashboard-search-container']}>
<Input
id="search"
type="search"
placeholder="Search events..."
value={search}
onChange={e => setSearch(e.target.value)}
className={styles['dashboard-search']}
value={searchInput}
onChange={e => setSearchInput(e.target.value)}
onKeyDown={handleSearchKeyDown}
className={styles['dashboard-search-input']}
/>

{searchInput && (
<button
type="button"
className={styles['dashboard-clear-btn']}
onClick={() => {
setSearchInput('');
setSearchQuery('');
setPagination(prev => ({ ...prev, currentPage: 1 }));
}}
>
<FaTimes />
</button>
)}

<button
type="button"
className={styles['dashboard-search-icon-btn']}
onClick={handleSearchClick}
aria-label="Search events"
>
<FaSearch />
</button>
</div>
</div>
</header>
Expand All @@ -122,24 +195,28 @@ export function CPDashboard() {
</div>
<Input type="date" placeholder="Ending After" className={styles['date-filter']} />
</div>

<div className={styles['filter-item']}>
<label htmlFor="online-only">Online</label>
<div>
<Input type="checkbox" /> Online Only
</div>
</div>

<div className={styles['filter-item']}>
<label htmlFor="branches">Branches</label>
<Input type="select">
<option>Select branches</option>
</Input>
</div>

<div className={styles['filter-item']}>
<label htmlFor="themes">Themes</label>
<Input type="select">
<option>Select themes</option>
</Input>
</div>

<div className={styles['filter-item']}>
<label htmlFor="categories">Categories</label>
<Input type="select">
Expand All @@ -152,22 +229,23 @@ export function CPDashboard() {

<Col md={9} className={styles['dashboard-main']}>
<h2 className={styles['section-title']}>Events</h2>

<Row>
{events.length > 0 ? (
events.map(event => (
{displayedEvents.length > 0 ? (
displayedEvents.map(event => (
<Col md={4} key={event.id} className={styles['event-card-col']}>
<Card className={styles['event-card']}>
<div className={styles['event-card-img-container']}>
<img
<FixedRatioImage
src={event.image}
alt={event.title}
className={styles['event-card-img']}
fallback={FALLBACK_IMG}
/>
</div>
<CardBody>
<h5 className={styles['event-title']}>{event.title}</h5>
<p className={styles['event-date']}>
<FaCalendarAlt className={styles['event-icon']} /> {event.date}
<FaCalendarAlt className={styles['event-icon']} /> {formatDate(event.date)}
</p>
<p className={styles['event-location']}>
<FaMapMarkerAlt className={styles['event-icon']} /> {event.location}
Expand All @@ -183,6 +261,30 @@ export function CPDashboard() {
<div className={styles['no-events']}>No events available</div>
)}
</Row>

{/* Simple pagination controls if needed */}
{totalPages > 1 && (
<div className={styles['pagination-container']}>
<Button
color="secondary"
disabled={pagination.currentPage === 1}
onClick={() => goToPage(pagination.currentPage - 1)}
>
Previous
</Button>
<span className={styles['pagination-info']}>
Page {pagination.currentPage} of {totalPages}
</span>
<Button
color="secondary"
disabled={pagination.currentPage === totalPages}
onClick={() => goToPage(pagination.currentPage + 1)}
>
Next
</Button>
</div>
)}

<div className={styles['dashboard-actions']}>
<Button color="primary">Show Past Events</Button>
</div>
Expand Down
Loading
Loading