11// Activity List Component
22import { useState , useEffect } from 'react' ;
3- import { useSelector } from 'react-redux' ;
3+ import { useSelector , useStore } from 'react-redux' ;
44import styles from './ActivityList.module.css' ;
55import { mockActivities } from './mockActivities' ;
6- // import { useHistory } from 'react-router-dom';
76
87function 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 >
0 commit comments