Skip to content

Commit 552c74e

Browse files
Add sorting to Activities list and fix dark mode styling
1 parent 8c52d46 commit 552c74e

3 files changed

Lines changed: 126 additions & 99 deletions

File tree

src/components/CommunityPortal/Activities/ActivityList.jsx

Lines changed: 65 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@ import { useState, useEffect } from 'react';
33
import { useSelector } from 'react-redux';
44
import styles from './ActivityList.module.css';
55
import { mockActivities } from './mockActivities';
6-
// import { useHistory } from 'react-router-dom';
76

87
function ActivityList() {
9-
const darkMode = useSelector(state => state.theme.darkMode);
8+
let darkMode = false;
9+
10+
try {
11+
darkMode = useSelector(state => state.theme?.darkMode);
12+
} catch (error) {
13+
darkMode = false;
14+
}
15+
1016
const [activities, setActivities] = useState([]);
1117
const [loading, setLoading] = useState(true);
1218
const [error, setError] = useState(null);
@@ -17,6 +23,7 @@ function ActivityList() {
1723
});
1824
const [locationSuggestions, setLocationSuggestions] = useState([]);
1925
const [showSuggestions, setShowSuggestions] = useState(false);
26+
const [sortOrder, setSortOrder] = useState('earliest');
2027

2128
useEffect(() => {
2229
if (darkMode) {
@@ -36,21 +43,10 @@ function ActivityList() {
3643
setLoading(true);
3744
setError(null);
3845

39-
// TODO: Replace with actual API endpoint
40-
// const response = await fetch('/api/activities');
41-
// if (!response.ok) {
42-
// throw new Error('Failed to fetch activities');
43-
// }
44-
// const data = await response.json();
45-
// setActivities(data);
46-
47-
// Simulating API call - remove this when real API is available
48-
// For now, we'll use mock data directly
4946
throw new Error('API not implemented yet');
5047
} catch (err) {
5148
console.warn('Failed to fetch activities from API, using mock data:', err.message);
5249
setError(err.message);
53-
// Fallback to mock data
5450
setActivities(mockActivities);
5551
} finally {
5652
setLoading(false);
@@ -60,43 +56,31 @@ function ActivityList() {
6056
fetchActivities();
6157
}, []);
6258

63-
// Get location suggestions with STRICT prefix-based matching only
6459
const getLocationSuggestions = input => {
65-
if (!input.trim()) {
66-
return [];
67-
}
60+
if (!input.trim()) return [];
6861

69-
// Get unique locations
7062
const uniqueLocations = [...new Set(activities.map(a => a.location))];
7163
const lowerInput = input.toLowerCase();
7264

73-
// ONLY return locations that START with the input (prefix matching)
74-
const prefixMatches = uniqueLocations.filter(loc => loc.toLowerCase().startsWith(lowerInput));
75-
76-
// Limit to top 10 results
77-
return prefixMatches.slice(0, 10);
65+
return uniqueLocations
66+
.filter(loc => loc.toLowerCase().startsWith(lowerInput))
67+
.slice(0, 10);
7868
};
7969

8070
const handleFilterChange = e => {
8171
const { name, value } = e.target;
8272
setFilter({ ...filter, [name]: value });
8373

84-
// Update location suggestions when location input changes
8574
if (name === 'location') {
8675
const suggestions = getLocationSuggestions(value);
8776
setLocationSuggestions(suggestions);
8877
setShowSuggestions(true);
8978
}
9079
};
9180

92-
const filteredActivities = activities.filter(activity => {
93-
return (
94-
(!filter.type || activity.type === filter.type) &&
95-
(!filter.date || activity.date === filter.date) &&
96-
(!filter.location ||
97-
activity.location.toLowerCase().startsWith(filter.location.toLowerCase()))
98-
);
99-
});
81+
const handleSortChange = e => {
82+
setSortOrder(e.target.value);
83+
};
10084

10185
const handleSuggestionClick = location => {
10286
setFilter({ ...filter, location });
@@ -114,9 +98,26 @@ function ActivityList() {
11498
setShowSuggestions(false);
11599
};
116100

101+
const filteredActivities = activities
102+
.filter(activity => {
103+
return (
104+
(!filter.type || activity.type === filter.type) &&
105+
(!filter.date || activity.date === filter.date) &&
106+
(!filter.location ||
107+
activity.location.toLowerCase().startsWith(filter.location.toLowerCase()))
108+
);
109+
})
110+
.sort((a, b) => {
111+
const dateA = new Date(a.date);
112+
const dateB = new Date(b.date);
113+
return sortOrder === 'earliest' ? dateA - dateB : dateB - dateA;
114+
});
115+
117116
return (
118117
<div className={`${styles.activityListContainer} ${darkMode ? 'bg-oxford-blue' : ''}`}>
119-
<h1 className={`${styles.heading} ${darkMode ? 'text-light' : ''}`}>Activity List</h1>
118+
<h1 className={`${styles.heading} ${darkMode ? 'text-light' : ''}`}>
119+
Activity List
120+
</h1>
120121

121122
<div className={`${styles.filters} ${darkMode ? styles.darkModeFilters : ''}`}>
122123
<label className={darkMode ? 'text-light' : ''}>
@@ -142,6 +143,18 @@ function ActivityList() {
142143
/>
143144
</label>
144145

146+
<label className={darkMode ? 'text-light' : ''}>
147+
Sort By:
148+
<select
149+
value={sortOrder}
150+
onChange={handleSortChange}
151+
className={darkMode ? styles.darkModeInput : ''}
152+
>
153+
<option value="earliest">Start Time: Earliest to Latest</option>
154+
<option value="latest">Start Time: Latest to Earliest</option>
155+
</select>
156+
</label>
157+
145158
<label className={darkMode ? 'text-light' : ''}>
146159
Location:
147160
<div style={{ position: 'relative' }}>
@@ -157,37 +170,26 @@ function ActivityList() {
157170
setShowSuggestions(true);
158171
}
159172
}}
160-
onBlur={() => {
161-
// Delay to allow click on suggestion
162-
setTimeout(() => setShowSuggestions(false), 200);
163-
}}
173+
onBlur={() => setTimeout(() => setShowSuggestions(false), 200)}
164174
placeholder="Enter location"
165175
autoComplete="off"
166176
className={darkMode ? styles.darkModeInput : ''}
167177
/>
178+
168179
{showSuggestions && locationSuggestions.length > 0 && (
169180
<div
170-
className={`${styles.suggestions} ${darkMode ? styles.darkSuggestions : ''}`}
171-
role="listbox"
172-
aria-label="Location suggestions"
181+
className={`${styles.suggestions} ${
182+
darkMode ? styles.darkSuggestions : ''
183+
}`}
173184
>
174185
{locationSuggestions.map((location, index) => (
175186
<div
176187
key={index}
177188
className={styles.suggestionItem}
178-
role="option"
179-
tabIndex={0}
180-
aria-selected="false"
181189
onMouseDown={e => {
182-
e.preventDefault(); // Prevent blur from firing
190+
e.preventDefault();
183191
handleSuggestionClick(location);
184192
}}
185-
onKeyDown={e => {
186-
if (e.key === 'Enter' || e.key === ' ') {
187-
e.preventDefault();
188-
handleSuggestionClick(location);
189-
}
190-
}}
191193
>
192194
{location}
193195
</div>
@@ -196,6 +198,7 @@ function ActivityList() {
196198
)}
197199
</div>
198200
</label>
201+
199202
<div className={styles.clearButtonWrapper}>
200203
<button
201204
type="button"
@@ -207,15 +210,23 @@ function ActivityList() {
207210
</button>
208211
</div>
209212
</div>
213+
210214
<div className={`${styles.activityList} ${darkMode ? styles.darkModeList : ''}`}>
211215
{loading ? (
212216
<p className={darkMode ? 'text-light' : ''}>Loading activities...</p>
213217
) : filteredActivities.length > 0 ? (
214218
<ul>
215219
{filteredActivities.map(activity => (
216-
<li key={activity.id} className={darkMode ? styles.darkModeItem : ''}>
217-
<strong>{activity.name}</strong> - {activity.type} - {activity.date} -{' '}
218-
{activity.location}
220+
<li
221+
key={activity.id}
222+
className={`${styles.activityItem} ${
223+
darkMode ? styles.darkModeItem : ''
224+
}`}
225+
>
226+
<strong>{activity.name}</strong>
227+
<span>
228+
{activity.type}{activity.date}{activity.location}
229+
</span>
219230
</li>
220231
))}
221232
</ul>
@@ -227,4 +238,4 @@ function ActivityList() {
227238
);
228239
}
229240

230-
export default ActivityList;
241+
export default ActivityList;

0 commit comments

Comments
 (0)