Skip to content

Commit 1fd6b32

Browse files
authored
Added events search (#94)
1 parent 4b17659 commit 1fd6b32

File tree

4 files changed

+105
-4
lines changed

4 files changed

+105
-4
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Create a new directory named `dist` in the root directory and run :
4343
npm run getEvents
4444
```
4545

46-
Copy `events.json` from `dist` to root directory.
46+
Copy `events.json` from `dist` to `src` directory.
4747

4848
### Run development server
4949

src/App.jsx

+21-3
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,24 @@ import parse from 'html-react-parser';
77
import timeGridPlugin from '@fullcalendar/timegrid';
88
import rrulePlugin from '@fullcalendar/rrule';
99

10+
import SearchHeader from './components/SearchHeader';
11+
import './App.css';
12+
1013
import useEscKey from './hooks/useEscKey';
1114

1215
import Icon from '@mdi/react';
1316
import { mdiCalendarRange, mdiMapMarkerOutline, mdiClose } from '@mdi/js';
14-
import './App.css';
17+
18+
import eventData from './events.json'
1519

1620
const htmlRegex = /<\/*html-blob>/;
1721

1822
function App() {
1923
const calendarRef = createRef();
2024

25+
const eventsArray = Array.from(eventData)
26+
const [events, setEvents] = useState(eventsArray)
27+
2128
const [ loading, setLoading ] = useState(true);
2229
const [ showEventDetails, setShowEventDetails ] = useState(false);
2330
const [ eventDetails, setEventDetails ] = useState(false);
@@ -51,6 +58,16 @@ function App() {
5158
setPopupPosition({left: position.left + 'px', top: position.top + 'px'})
5259
}
5360

61+
const filterEvents = (searchTerm)=>{
62+
if(!searchTerm) return setEvents(eventsArray) //handles searchbox clear
63+
let matchingEvents = eventsArray.filter((event) => {
64+
const titleIncludes = event.title?.toLowerCase().includes(searchTerm.toLowerCase())
65+
const descriptionIncludes = event.description?.toLowerCase().includes(searchTerm.toLowerCase())
66+
return titleIncludes || descriptionIncludes
67+
})
68+
setEvents(matchingEvents)
69+
}
70+
5471
const handleEventClick = useCallback((clickInfo) => {
5572
window.outerWidth > 600 && createPopupPosition(clickInfo.jsEvent)
5673
setEventDetails(clickInfo.event);
@@ -174,7 +191,7 @@ function App() {
174191
aspectRatio={aspectRatio}
175192
handleWindowResize={true}
176193
windowResize={windowResize}
177-
events="events.json"
194+
events={events}
178195
headerToolbar={{
179196
left : 'prev,next today',
180197
center : 'title',
@@ -188,10 +205,11 @@ function App() {
188205
eventClick={handleEventClick}
189206
loading={(isLoading) => setLoading(isLoading)}
190207
/>
191-
),[aspectRatio, initialView]);
208+
),[aspectRatio, initialView, events]);
192209

193210
return (
194211
<div className="App main">
212+
<SearchHeader filterEvents={filterEvents} />
195213
<div className="finos-calendar">{renderFullCalendar}</div>
196214
{showEventDetails && renderEventDetails()}
197215
{loading && <div className="finos-calendar-overlay" />}

src/components/SearchHeader.css

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
.search-header{
2+
display:flex;
3+
justify-content: center;
4+
padding: 20px 0;
5+
gap:10px;
6+
}
7+
8+
.search-header input{
9+
max-width: 70%;
10+
width: 250px;
11+
padding: 8px 10px;
12+
font-size: 16px;
13+
border-radius: 4px;
14+
border: 1px solid black;
15+
}
16+
17+
.search-header input:focus, .search-header input:active{
18+
border-color: #3788d8;
19+
outline: none;
20+
}
21+
22+
.search-header button{
23+
font-size: 16px;
24+
background-color: #3788d8;
25+
color: white;
26+
border: 0;
27+
border-radius: 4px;
28+
padding: 0 20px;
29+
cursor: pointer
30+
}
31+
32+
.search-header button:hover{
33+
transition: 300ms ease;
34+
background-color: #2a6097;
35+
}
36+
37+
@media only screen and (prefers-color-scheme : dark){
38+
.search-header input{
39+
border-color: transparent;
40+
background-color: #2c3e50;
41+
color: #eee;
42+
}
43+
.search-header button{
44+
background-color: #2c3e50;
45+
color: #eee;
46+
}
47+
::-ms-input-placeholder{
48+
color: #d0d0d0;
49+
opacity: 1;
50+
}
51+
::placeholder{
52+
color: #d0d0d0;
53+
}
54+
}

src/components/SearchHeader.jsx

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useState } from 'react';
2+
import './SearchHeader.css'
3+
4+
const SearchHeader = ({filterEvents})=>{
5+
const [ searchTerm, setSearchTerm ] = useState("")
6+
7+
const handleInputTextChange = (e)=>{
8+
setSearchTerm(e.target.value)
9+
if(!e.target.value.trim()) filterEvents(false) // handles searchbox clear
10+
}
11+
12+
const handleSearch = ()=>{
13+
if(!searchTerm.trim()) return
14+
filterEvents(searchTerm)
15+
}
16+
17+
const handleEnterPress = (e)=>{
18+
if(e.key === 'Enter') handleSearch()
19+
}
20+
21+
return(
22+
<div className="search-header">
23+
<input type='text' placeholder='Search events' value={searchTerm} onChange={handleInputTextChange} onKeyDown={handleEnterPress}/>
24+
<button onClick={handleSearch}>Search</button>
25+
</div>
26+
)
27+
}
28+
29+
export default SearchHeader;

0 commit comments

Comments
 (0)