Skip to content

Commit 2e2b4dd

Browse files
committed
DBC22-904: Added components and redux logic
1 parent 30fa8c1 commit 2e2b4dd

File tree

6 files changed

+126
-67
lines changed

6 files changed

+126
-67
lines changed

src/frontend/src/Components/Map.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { MapContext } from '../App.js';
3030
import AdvisoriesAccordion from './advisories/AdvisoriesAccordion';
3131
import CurrentCameraIcon from './CurrentCameraIcon';
3232
import EventTypeIcon from './EventTypeIcon';
33+
import EventFilter from '../Components/events/EventFilter';
3334
import FriendlyTime from './FriendlyTime';
3435
import Layers from './Layers.js';
3536
import RouteSearch from './map/RouteSearch.js';
@@ -255,6 +256,7 @@ export default function MapWrapper({
255256
animation: {
256257
duration: 250,
257258
},
259+
margin: 90,
258260
},
259261
});
260262
const vectorLayer = new VectorTileLayer({
@@ -763,6 +765,11 @@ export default function MapWrapper({
763765
<AdvisoriesAccordion />
764766
</div>
765767
)}
768+
{!isPreview && (
769+
<div>
770+
<EventFilter/>
771+
</div>
772+
)}
766773
</div>
767774
);
768775
}

src/frontend/src/Components/Map.scss

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
@import "../styles/variables.scss";
22
@import '~ol/ol.css';
33

4+
5+
6+
.delay-filter {
7+
background-color: black;
8+
position: absolute;
9+
top: 50px;
10+
height: 100000px;
11+
}
12+
413
.map-wrap {
514
position: absolute;
615
top: 50px;
@@ -10,7 +19,6 @@
1019
@media (min-width: 992px) {
1120
top: 58px;
1221
}
13-
1422
.map {
1523
position: absolute;
1624
width: 100%;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import React from 'react';
2+
import {useSelector, useDispatch } from 'react-redux'
3+
4+
// Styling
5+
import '../../pages/EventsPage.scss';
6+
7+
// Third party packages
8+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
9+
import { faFilter } from '@fortawesome/free-solid-svg-icons';
10+
import Dropdown from 'react-bootstrap/Dropdown';
11+
import Form from 'react-bootstrap/Form';
12+
import { updateEventFilters } from '../../slices/eventFiltersSlice';
13+
14+
export default function EventFilter() {
15+
const dispatch = useDispatch();
16+
const [ eventTypeFilter ] = useSelector((state) => [
17+
state.eventFilters.filterSet,
18+
]);
19+
20+
const eventTypeFilterHandler = e => {
21+
const eventType = e.target.value;
22+
23+
const newFilter = { ...eventTypeFilter };
24+
newFilter[eventType] = !newFilter[eventType];
25+
dispatch(updateEventFilters(newFilter));
26+
};
27+
28+
const filterProps = [
29+
{
30+
id: 'checkbox-filter-incident',
31+
label: 'Incidents',
32+
value: 'INCIDENT',
33+
},
34+
{
35+
id: 'checkbox-filter-weather',
36+
label: 'Road Conditions',
37+
value: 'WEATHER_CONDITION',
38+
},
39+
{
40+
id: 'checkbox-filter-construction',
41+
label: 'Current Events',
42+
value: 'CONSTRUCTION',
43+
},
44+
{
45+
id: 'checkbox-filter-special',
46+
label: 'Future Events',
47+
value: 'SPECIAL_EVENT',
48+
},
49+
];
50+
// Rendering
51+
return (
52+
<div className="sort-and-filter">
53+
<div className="sort"></div>
54+
<Dropdown align="end">
55+
<Dropdown.Toggle variant="outline-primary" id="filter-dropdown">
56+
Filters
57+
<FontAwesomeIcon icon={faFilter} />
58+
</Dropdown.Toggle>
59+
<Dropdown.Menu>
60+
{filterProps.map(fp => (
61+
<Form.Check
62+
id={fp.id}
63+
key={fp.id}
64+
label={
65+
<span>
66+
{fp.icon}
67+
{fp.label}
68+
</span>
69+
}
70+
value={fp.value}
71+
checked={eventTypeFilter[fp.value]}
72+
onChange={eventTypeFilterHandler}
73+
/>
74+
))}
75+
</Dropdown.Menu>
76+
</Dropdown>
77+
</div>
78+
);
79+
}

src/frontend/src/pages/EventsPage.js

Lines changed: 10 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
// React
22
import React, {useEffect, useRef, useState} from 'react';
33
import {useNavigate} from 'react-router-dom';
4+
import { useSelector } from 'react-redux'
45

56
// Third party packages
67
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
78
import {
89
faMapLocationDot,
9-
faFilter,
1010
} from '@fortawesome/free-solid-svg-icons';
1111
import {useMediaQuery} from '@uidotdev/usehooks';
1212
import Container from 'react-bootstrap/Container';
13-
import Dropdown from 'react-bootstrap/Dropdown';
14-
import Form from 'react-bootstrap/Form';
1513
import InfiniteScroll from 'react-infinite-scroll-component';
1614

1715
// Components and functions
1816
import {getEvents} from '../Components/data/events';
1917
import EventCard from '../Components/events/EventCard';
2018
import EventsTable from '../Components/events/EventsTable';
19+
import EventFilter from '../Components/events/EventFilter';
2120
import FriendlyTime from '../Components/FriendlyTime';
2221
import PageHeader from '../PageHeader';
2322
import Footer from '../Footer.js';
@@ -32,6 +31,10 @@ export default function EventsPage() {
3231

3332
const navigate = useNavigate();
3433

34+
const [ eventFilters ] = useSelector((state) => [
35+
state.eventFilters.filterSet,
36+
]);
37+
3538
const columns = [
3639
{
3740
header: 'Type',
@@ -69,38 +72,8 @@ export default function EventsPage() {
6972
},
7073
];
7174

72-
const filterProps = [
73-
{
74-
id: 'checkbox-filter-incident',
75-
label: 'Incidents',
76-
value: 'INCIDENT',
77-
},
78-
{
79-
id: 'checkbox-filter-weather',
80-
label: 'Road Conditions',
81-
value: 'WEATHER_CONDITION',
82-
},
83-
{
84-
id: 'checkbox-filter-construction',
85-
label: 'Current Events',
86-
value: 'CONSTRUCTION',
87-
},
88-
{
89-
id: 'checkbox-filter-special',
90-
label: 'Future Events',
91-
value: 'SPECIAL_EVENT',
92-
},
93-
];
94-
9575
const [sortingColumns, setSortingColumns] = useState([]);
9676

97-
const [eventTypeFilter, setEventTypeFilter] = useState({
98-
'CONSTRUCTION': false,
99-
'INCIDENT': false,
100-
'SPECIAL_EVENT': false,
101-
'WEATHER_CONDITION': false,
102-
});
103-
10477
const [events, setEvents] = useState([]);
10578
const [processedEvents, setProcessedEvents] = useState([]);
10679
const [displayedEvents, setDisplayedEvents] = useState([]);
@@ -131,13 +104,13 @@ export default function EventsPage() {
131104

132105
const processEvents = () => {
133106
const hasTrue = (val) => !!val;
134-
const hasFilterOn = Object.values(eventTypeFilter).some(hasTrue);
107+
const hasFilterOn = Object.values(eventFilters).some(hasTrue);
135108

136109
let res = [...events];
137110

138111
// Filter
139112
if (hasFilterOn) {
140-
res = res.filter((e) => !!eventTypeFilter[e.event_type]);
113+
res = res.filter((e) => !!eventFilters[e.event_type]);
141114
}
142115

143116
// Sort
@@ -174,16 +147,7 @@ export default function EventsPage() {
174147
if (!isInitialMount.current) { // Do not run on startup
175148
processEvents();
176149
}
177-
}, [events, eventTypeFilter, sortingColumns]);
178-
179-
const eventTypeFilterHandler = (e) => {
180-
const eventType = e.target.value;
181-
182-
const newFilter = {...eventTypeFilter};
183-
newFilter[eventType] = !newFilter[eventType];
184-
185-
setEventTypeFilter(newFilter);
186-
};
150+
}, [events, eventFilters, sortingColumns]);
187151

188152
const largeScreen = useMediaQuery('only screen and (min-width : 768px)');
189153

@@ -195,27 +159,7 @@ export default function EventsPage() {
195159
</PageHeader>
196160
<Container>
197161
<Advisories />
198-
<div className="sort-and-filter">
199-
<div className="sort"></div>
200-
<Dropdown align="end">
201-
<Dropdown.Toggle variant="outline-primary" id="filter-dropdown">
202-
Filters<FontAwesomeIcon icon={faFilter} />
203-
</Dropdown.Toggle>
204-
<Dropdown.Menu>
205-
{filterProps.map((fp) => (
206-
<Form.Check
207-
id={fp.id}
208-
key={fp.id}
209-
label={
210-
<span>{fp.icon}{fp.label}</span>
211-
}
212-
value={fp.value}
213-
checked={eventTypeFilter[fp.value]}
214-
onChange={eventTypeFilterHandler} />
215-
))}
216-
</Dropdown.Menu>
217-
</Dropdown>
218-
</div>
162+
<EventFilter />
219163

220164
{ events && !!events.length && (
221165
<InfiniteScroll
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { createSlice } from '@reduxjs/toolkit';
2+
3+
export const eventFiltersSlice = createSlice({
4+
name: 'eventFilters',
5+
initialState: {
6+
filterSet: {'CONSTRUCTION': false,
7+
'INCIDENT': false,
8+
'SPECIAL_EVENT': false,
9+
'WEATHER_CONDITION': false,},
10+
},
11+
reducers: {
12+
updateEventFilters: (state, action) => {
13+
state.filterSet = action.payload;
14+
},
15+
},
16+
});
17+
export const { updateEventFilters } = eventFiltersSlice.actions;
18+
19+
export default eventFiltersSlice.reducer;

src/frontend/src/store.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { configureStore } from '@reduxjs/toolkit';
22
import camerasReducer from './slices/camerasSlice';
33
import eventsReducer from './slices/eventsSlice';
4+
import eventFiltersReducer from './slices/eventFiltersSlice';
45
import routesReducer from './slices/routesSlice';
56
import mapReducer from './slices/mapSlice';
67

@@ -29,6 +30,7 @@ const store = configureStore({
2930
reducer: {
3031
cameras: persistReducer(getConfig('cameras'), camerasReducer),
3132
events: persistReducer(getConfig('events'), eventsReducer),
33+
eventFilters: persistReducer(getConfig('eventFilters'), eventFiltersReducer),
3234
routes: persistReducer(getConfig('routes'), routesReducer),
3335
map: persistReducer(getConfig('map'), mapReducer),
3436
},

0 commit comments

Comments
 (0)