Skip to content

Commit 386e681

Browse files
Merge pull request #4568 from OneCommunityGlobal/Neeraj_Add_Event_Sorting_By_Start_Time
Neeraj add event sorting by start time
2 parents b8274e4 + 11f5d0c commit 386e681

3 files changed

Lines changed: 120 additions & 95 deletions

File tree

src/components/CommunityPortal/Activities/ActivityList.jsx

Lines changed: 59 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
// Activity List Component
22
import { useState, useEffect } from 'react';
3-
import { useSelector } from 'react-redux';
3+
import { useSelector, useStore } 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+
const store = useStore();
12+
darkMode = store?.getState()?.theme?.darkMode ?? false;
13+
} catch (e) {
14+
darkMode = false;
15+
}
16+
1017
const [activities, setActivities] = useState([]);
1118
const [loading, setLoading] = useState(true);
1219
const [error, setError] = useState(null);
@@ -17,6 +24,7 @@ function ActivityList() {
1724
});
1825
const [locationSuggestions, setLocationSuggestions] = useState([]);
1926
const [showSuggestions, setShowSuggestions] = useState(false);
27+
const [sortOrder, setSortOrder] = useState('earliest');
2028

2129
useEffect(() => {
2230
if (darkMode) {
@@ -35,22 +43,10 @@ function ActivityList() {
3543
try {
3644
setLoading(true);
3745
setError(null);
38-
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,29 @@ 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.filter(loc => loc.toLowerCase().startsWith(lowerInput)).slice(0, 10);
7866
};
7967

8068
const handleFilterChange = e => {
8169
const { name, value } = e.target;
8270
setFilter({ ...filter, [name]: value });
8371

84-
// Update location suggestions when location input changes
8572
if (name === 'location') {
8673
const suggestions = getLocationSuggestions(value);
8774
setLocationSuggestions(suggestions);
8875
setShowSuggestions(true);
8976
}
9077
};
9178

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-
});
79+
const handleSortChange = e => {
80+
setSortOrder(e.target.value);
81+
};
10082

10183
const handleSuggestionClick = location => {
10284
setFilter({ ...filter, location });
@@ -114,6 +96,21 @@ function ActivityList() {
11496
setShowSuggestions(false);
11597
};
11698

99+
const filteredActivities = activities
100+
.filter(activity => {
101+
return (
102+
(!filter.type || activity.type === filter.type) &&
103+
(!filter.date || activity.date === filter.date) &&
104+
(!filter.location ||
105+
activity.location.toLowerCase().startsWith(filter.location.toLowerCase()))
106+
);
107+
})
108+
.sort((a, b) => {
109+
const dateA = new Date(a.date);
110+
const dateB = new Date(b.date);
111+
return sortOrder === 'earliest' ? dateA - dateB : dateB - dateA;
112+
});
113+
117114
return (
118115
<div className={`${styles.activityListContainer} ${darkMode ? 'bg-oxford-blue' : ''}`}>
119116
<h1 className={`${styles.heading} ${darkMode ? 'text-light' : ''}`}>Activity List</h1>
@@ -142,6 +139,18 @@ function ActivityList() {
142139
/>
143140
</label>
144141

142+
<label className={darkMode ? 'text-light' : ''}>
143+
Sort By:
144+
<select
145+
value={sortOrder}
146+
onChange={handleSortChange}
147+
className={darkMode ? styles.darkModeInput : ''}
148+
>
149+
<option value="earliest">Start Time: Earliest to Latest</option>
150+
<option value="latest">Start Time: Latest to Earliest</option>
151+
</select>
152+
</label>
153+
145154
<label className={darkMode ? 'text-light' : ''}>
146155
Location:
147156
<div style={{ position: 'relative' }}>
@@ -157,29 +166,22 @@ function ActivityList() {
157166
setShowSuggestions(true);
158167
}
159168
}}
160-
onBlur={() => {
161-
// Delay to allow click on suggestion
162-
setTimeout(() => setShowSuggestions(false), 200);
163-
}}
169+
onBlur={() => setTimeout(() => setShowSuggestions(false), 200)}
164170
placeholder="Enter location"
165171
autoComplete="off"
166172
className={darkMode ? styles.darkModeInput : ''}
167173
/>
174+
168175
{showSuggestions && locationSuggestions.length > 0 && (
169-
<div
170-
className={`${styles.suggestions} ${darkMode ? styles.darkSuggestions : ''}`}
171-
role="listbox"
172-
aria-label="Location suggestions"
173-
>
176+
<div className={`${styles.suggestions} ${darkMode ? styles.darkSuggestions : ''}`}>
174177
{locationSuggestions.map((location, index) => (
175178
<div
176179
key={index}
177-
className={styles.suggestionItem}
178-
role="option"
180+
role="button"
179181
tabIndex={0}
180-
aria-selected="false"
182+
className={styles.suggestionItem}
181183
onMouseDown={e => {
182-
e.preventDefault(); // Prevent blur from firing
184+
e.preventDefault();
183185
handleSuggestionClick(location);
184186
}}
185187
onKeyDown={e => {
@@ -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,21 @@ 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} ${darkMode ? styles.darkModeItem : ''}`}
223+
>
224+
<strong>{activity.name}</strong>
225+
<span>
226+
{activity.type}{activity.date}{activity.location}
227+
</span>
219228
</li>
220229
))}
221230
</ul>

src/components/CommunityPortal/Activities/ActivityList.module.css

Lines changed: 12 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Enhanced Aesthetic CSS for Activity List */
1+
/* ===== ORIGINAL STYLES (UNCHANGED) ===== */
22

33
/* Activity List Container */
44
.activityListContainer {
@@ -20,12 +20,12 @@
2020
font-weight: 600;
2121
}
2222

23-
/* Dark mode heading - uses global text-light class */
23+
/* Dark mode heading */
2424
.activityListContainer:global(.bg-oxford-blue) .heading {
2525
color: #ffffff !important;
2626
}
2727

28-
/* Body background helper when Activity List is in dark mode */
28+
/* Body background helper */
2929
:global(.activity-list-dark-body),
3030
:global(.activity-list-dark-body #root),
3131
:global(.activity-list-dark-body .App) {
@@ -45,7 +45,6 @@
4545
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
4646
}
4747

48-
/* Dark mode filters - navy-blue theme matching Dashboard */
4948
.darkModeFilters {
5049
background: #1e2a3a;
5150
box-shadow: none;
@@ -60,7 +59,8 @@
6059
color: #1f2937;
6160
}
6261

63-
.filters input {
62+
.filters input,
63+
.filters select {
6464
padding: 10px;
6565
border: 1px solid #d1d5db;
6666
border-radius: 6px;
@@ -71,15 +71,13 @@
7171
color: #333;
7272
}
7373

74-
/* Dark mode inputs - navy-blue theme (#1e2a3a bg, #3a4a5c borders) */
7574
.darkModeInput {
7675
background-color: #1e2a3a !important;
7776
color: #ffffff !important;
7877
border: 1px solid #3a4a5c !important;
7978
color-scheme: dark;
8079
}
8180

82-
/* Dark mode date input - style the calendar icon */
8381
.darkModeInput[type="date"]::-webkit-calendar-picker-indicator {
8482
filter: invert(1);
8583
cursor: pointer;
@@ -91,18 +89,16 @@
9189
box-shadow: 0 0 8px rgba(59, 130, 246, 0.3);
9290
}
9391

94-
/* Dark mode input focus */
9592
.darkModeInput:focus {
9693
border-color: #5c9ce6 !important;
9794
box-shadow: 0 0 8px rgba(92, 156, 230, 0.5) !important;
9895
}
9996

100-
/* Dark mode input placeholder */
10197
.darkModeInput::placeholder {
10298
color: #a0a0a0;
10399
}
104100

105-
/* Clear All button */
101+
/* Clear button */
106102
.clearButton {
107103
padding: 10px 20px;
108104
background-color: #ef4444;
@@ -135,7 +131,6 @@
135131
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
136132
}
137133

138-
/* Dark mode activity list - remove border-radius, margin, box-shadow artifacts */
139134
.darkModeList {
140135
background: #1e2a3a;
141136
box-shadow: none;
@@ -149,7 +144,7 @@
149144
padding: 0;
150145
}
151146

152-
.activityList li {
147+
.activityItem {
153148
display: flex;
154149
flex-direction: column;
155150
gap: 10px;
@@ -158,10 +153,8 @@
158153
border: 1px solid #e5e7eb;
159154
border-radius: 8px;
160155
background-color: #f9fafb;
161-
transition: transform 0.2s ease, box-shadow 0.2s ease;
162156
}
163157

164-
/* Dark mode list items - navy-blue theme */
165158
.darkModeItem {
166159
background-color: #1e2a3a !important;
167160
border: 1px solid #3a4a5c !important;
@@ -174,27 +167,24 @@
174167
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
175168
}
176169

177-
/* Dark mode list item hover - button color theme (#3d4f62) */
178170
.darkModeItem:hover {
179171
background-color: #3d4f62 !important;
180172
box-shadow: none !important;
181173
}
182174

183-
.activityList li strong {
175+
.activityItem strong {
184176
color: #1e3a8a;
185177
font-size: 1.2rem;
186178
}
187179

188-
/* Dark mode strong text */
189180
.darkModeItem strong {
190181
color: #5c9ce6 !important;
191182
}
192183

193-
.activityList li span {
184+
.activityItem span {
194185
color: #4b5563;
195186
}
196187

197-
/* Dark mode span text */
198188
.darkModeItem span {
199189
color: #d1d5db !important;
200190
}
@@ -206,7 +196,7 @@
206196
margin: 20px 0;
207197
}
208198

209-
/* Autocomplete Suggestions Dropdown */
199+
/* Suggestions */
210200
.suggestions {
211201
position: absolute;
212202
top: 100%;
@@ -242,29 +232,11 @@
242232
background-color: #f3f4f6;
243233
}
244234

245-
.suggestionItem:focus {
246-
outline: 2px solid #3b82f6;
247-
outline-offset: -2px;
248-
background-color: #f3f4f6;
249-
}
250-
251235
.darkSuggestions .suggestionItem:hover {
252236
background-color: #334155;
253237
}
254238

255-
.darkSuggestions .suggestionItem:focus {
256-
outline-color: #60a5fa;
257-
background-color: #334155;
258-
}
259-
260-
.suggestionItem:not(:last-child) {
261-
border-bottom: 1px solid #e5e7eb;
262-
}
263-
264-
.darkSuggestions .suggestionItem:not(:last-child) {
265-
border-bottom-color: #475569;
266-
}
267-
239+
/* Responsive */
268240
@media (max-width: 768px) {
269241
.filters {
270242
flex-direction: column;
@@ -279,9 +251,4 @@
279251
.activityList {
280252
padding: 20px;
281253
}
282-
283-
.activityList li {
284-
flex-direction: column;
285-
padding: 10px;
286-
}
287-
}
254+
}

0 commit comments

Comments
 (0)