Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 54e9fc2

Browse files
authoredFeb 11, 2025··
Merge pull request #2553 from gordon-cs/improve-events
Remove defunct attended events and improve events page
2 parents 87d43c1 + ce26a4a commit 54e9fc2

File tree

19 files changed

+505
-864
lines changed

19 files changed

+505
-864
lines changed
 

‎package-lock.json

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@types/react-big-calendar": "^1.8.8",
4545
"@types/react-dom": "^18.3.0",
4646
"@types/react-router-dom": "^5.3.3",
47+
"@types/react-router-hash-link": "^2.4.9",
4748
"@vitejs/plugin-react": "^4.2.0",
4849
"eslint-config-react-app": "^7.0.1",
4950
"eslint-plugin-import": "^2.29.1",

‎src/components/EventList/components/EventItem/EventItem.module.scss

-34
This file was deleted.

‎src/components/EventList/components/EventItem/index.tsx

-106
This file was deleted.

‎src/components/EventList/index.tsx

-80
This file was deleted.

‎src/components/Loader/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ type Props = CircularProgressProps & {
55
inline?: boolean;
66
};
77

8-
const GordonLoader = ({ size = 100, color = 'primary', inline = false }: Props) => {
8+
const GordonLoader = ({ size = 100, color = 'primary', inline = false, ...otherProps }: Props) => {
99
if (inline) return <CircularProgress size={size} color={color} />;
1010
return (
1111
<Grid className={styles.gordon_loader} container justifyContent="center" alignItems="center">
1212
<Grid item>
13-
<CircularProgress size={size} color={color} />
13+
<CircularProgress size={size} color={color} {...otherProps} />
1414
</Grid>
1515
</Grid>
1616
);

‎src/routes.jsx

-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import BannerSubmission from './views/BannerSubmission';
55
import CoCurricularTranscript from './views/CoCurricularTranscript';
66
import EnrollmentCheckIn from './views/EnrollmentCheckIn';
77
import Events from './views/Events';
8-
import EventsAttended from './views/EventsAttended';
98
import Feedback from './views/Feedback';
109
import Help from './views/Help';
1110
import Home from './views/Home';
@@ -23,8 +22,6 @@ import PublicProfile from './views/PublicProfile';
2322
import Timesheets from './views/Timesheets';
2423
import RecIM from './views/RecIM';
2524
import RoomRanges from 'views/ResLife/components/RDView/components/RoomRanges';
26-
import { element } from 'prop-types';
27-
import { Room } from '@mui/icons-material';
2825
import CampusSafety from './views/CampusSafety';
2926

3027
// Route order must be from most specific to least specific (i.e. `/user/:username` before `/user`)
@@ -69,11 +66,6 @@ const routes = [
6966
path: '/events',
7067
element: <Events />,
7168
},
72-
{
73-
name: 'Attended',
74-
path: '/attended',
75-
element: <EventsAttended />,
76-
},
7769
{
7870
name: 'Feedback',
7971
path: '/feedback',

‎src/services/event.ts

+39-46
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,56 @@
1+
import { addMonths, addWeeks } from 'date-fns';
12
import http from './http';
2-
import session from './session';
3-
import { compareByProperty, filter } from './utils';
3+
import { compareByProperty } from './utils';
44

5-
type BaseEvent = {
5+
type UnformattedEvent = {
66
Event_Name: string;
77
Event_Title: string;
88
Description: string;
99
StartDate: string;
1010
EndDate: string;
1111
Location: string;
1212
Organization: string;
13-
};
14-
15-
type UnformattedEvent = BaseEvent & {
1613
Event_ID: string;
1714
Event_Type_Name: string;
1815
HasCLAWCredit: boolean;
1916
IsPublic: boolean;
2017
};
2118

22-
type UnformattedAttendedEvent = BaseEvent & {
23-
LiveID: string;
24-
CHDate?: Date;
25-
CHTermCD: string;
26-
Required?: number;
27-
};
28-
29-
type EventDisplayProperties = {
19+
export type Event = UnformattedEvent & {
3020
timeRange: string;
3121
date: string;
3222
title: string;
3323
location: string;
3424
};
3525

36-
type Event = UnformattedEvent & EventDisplayProperties;
37-
type AttendedEvent = UnformattedAttendedEvent & EventDisplayProperties;
38-
3926
const shortTimeFormatter = new Intl.DateTimeFormat('en-US', { timeStyle: 'short' });
4027
const shortDateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'short' });
4128

42-
function formatEvent<T extends BaseEvent>(event: T): T & EventDisplayProperties {
29+
function formatEvent(event: UnformattedEvent): Event {
4330
const startDate = new Date(event.StartDate);
4431
const endDate = new Date(event.EndDate);
32+
33+
let timeRange = 'No time listed';
34+
let date = 'No date listed';
35+
36+
try {
37+
timeRange = shortTimeFormatter.formatRange(startDate, endDate);
38+
} catch {
39+
// `Intl.DateTimeFormat#format` throws for invalid dates. We will just catch potential errors and
40+
// Catch any potential error and fallback to the default specified above
41+
}
42+
43+
try {
44+
date = shortDateFormatter.format(startDate);
45+
} catch {
46+
// `Intl.DateTimeFormat#formatRange` throws if it finds an invalid date
47+
// Catch any potential error and fallback to the default specified above
48+
}
49+
4550
return {
4651
...event,
47-
timeRange: shortTimeFormatter.formatRange(startDate, endDate),
48-
date: shortDateFormatter.format(startDate),
52+
timeRange,
53+
date,
4954
title: event.Event_Title || event.Event_Name,
5055
location: event.Location || 'No Location Listed',
5156
Description:
@@ -54,29 +59,15 @@ function formatEvent<T extends BaseEvent>(event: T): T & EventDisplayProperties
5459
};
5560
}
5661

57-
const formatAndSort = <T extends BaseEvent>(events: T[]): (T & EventDisplayProperties)[] =>
62+
const formatAndSort = (events: UnformattedEvent[]): Event[] =>
5863
events.map(formatEvent).sort(compareByProperty('StartDate'));
5964

6065
const getAllEvents = (): Promise<Event[]> =>
6166
http.get<UnformattedEvent[]>('events').then(formatAndSort);
6267

63-
// TODO: Unused. Consider removing
64-
const getCLWEvents = (): Promise<Event[]> => {
65-
const now = Date.now();
66-
return http
67-
.get<UnformattedEvent[]>('events/claw')
68-
.then(filter((e) => new Date(e.StartDate).getTime() > now))
69-
.then(formatAndSort);
70-
};
71-
7268
const getAllGuestEvents = (): Promise<Event[]> =>
7369
http.get<UnformattedEvent[]>('events/public').then(formatAndSort);
7470

75-
const getAttendedChapelEvents = (): Promise<AttendedEvent[]> =>
76-
http
77-
.get<UnformattedAttendedEvent[]>(`events/attended/${session.getTermCode()}`)
78-
.then(formatAndSort);
79-
8071
const getFutureEvents = (allEvents: Event[]): Event[] => {
8172
const now = Date.now();
8273
return allEvents
@@ -124,25 +115,29 @@ const getFilteredEvents = (
124115
}
125116
};
126117

118+
export const TIME_FILTERS = Object.freeze(['1 Week', '2 Weeks', '1 Month', '4 Months']);
119+
127120
/**
128121
* Make a closure over a time filter.
129122
*
130-
* The returned closure determines whether a given `event` falls before the time range
123+
* The returned closure determines whether a given `event` falls within the time range
131124
*
132125
* @param timeFilter The time filter to use
133126
* @returns A function that matches a given event against `timeFilter`
134127
*/
135128
const makeMatchesTimeFilter =
136129
(timeFilter: string) =>
137130
(event: Event): boolean => {
138-
if (timeFilter == '1 Week') {
139-
return new Date(event.StartDate) <= new Date(new Date().setDate(new Date().getDate() + 7));
140-
} else if (timeFilter == '2 Weeks') {
141-
return new Date(event.StartDate) <= new Date(new Date().setDate(new Date().getDate() + 14));
142-
} else if (timeFilter == '1 Month') {
143-
return new Date(event.StartDate) <= new Date(new Date().setMonth(new Date().getMonth() + 1));
144-
} else if (timeFilter == '4 Months') {
145-
return new Date(event.StartDate) <= new Date(new Date().setMonth(new Date().getMonth() + 4));
131+
const eventStart = new Date(event.StartDate);
132+
const now = new Date();
133+
if (timeFilter === '1 Week') {
134+
return eventStart <= addWeeks(now, 1) && eventStart >= addWeeks(now, -1);
135+
} else if (timeFilter === '2 Weeks') {
136+
return eventStart <= addWeeks(now, 2) && eventStart >= addWeeks(now, -2);
137+
} else if (timeFilter === '1 Month') {
138+
return eventStart <= addMonths(now, 1) && eventStart >= addMonths(now, -1);
139+
} else if (timeFilter === '4 Months') {
140+
return eventStart <= addMonths(now, 4) && eventStart >= addMonths(now, -4);
146141
} else {
147142
return false;
148143
}
@@ -234,10 +229,8 @@ const makeMatchesFilters =
234229
const eventService = {
235230
getAllEvents,
236231
getFutureEvents,
237-
getCLWEvents,
238232
getFilteredEvents,
239233
getAllGuestEvents,
240-
getAttendedChapelEvents,
241234
};
242235

243236
export default eventService;

‎src/theme.ts

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ declare module '@mui/material/styles' {
1818
declare module '@mui/material' {
1919
interface ButtonPropsColorOverrides {
2020
neutral: any;
21+
link: any;
2122
}
2223
}
2324

‎src/views/Events/Events.module.scss ‎src/views/Events/components/EventFilters/EventFilters.module.scss

-7
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,4 @@
66
color: var(--mui-palette-neutral-700);
77
font-size: 20;
88
}
9-
&_filter_button {
10-
font-weight: bold;
11-
}
12-
}
13-
14-
.card_style {
15-
padding: 0 3vw;
169
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import {
2+
Autocomplete,
3+
Button,
4+
Card,
5+
CardContent,
6+
CardHeader,
7+
Checkbox,
8+
Chip,
9+
FormControlLabel,
10+
InputAdornment,
11+
MenuItem,
12+
TextField,
13+
CardActions,
14+
Grid,
15+
} from '@mui/material';
16+
import { HashLink } from 'react-router-hash-link';
17+
import React, { useEffect, useMemo, useState } from 'react';
18+
import gordonEvent, { Event, EVENT_FILTERS, TIME_FILTERS } from 'services/event';
19+
import { useSearchParams } from 'react-router-dom';
20+
import styles from './EventFilters.module.css';
21+
import { Search } from '@mui/icons-material';
22+
23+
const FiltersKey = 'filters';
24+
const IncludePastKey = 'includePast';
25+
const DefaultTimeFilter = '2 Weeks';
26+
27+
type Props = {
28+
unfilteredEvents: Event[];
29+
onFilterEvents: (filteredEvents: Event[]) => void;
30+
setLoading: (isLoading: boolean) => void;
31+
};
32+
33+
const EventFilters = ({ unfilteredEvents, onFilterEvents, setLoading }: Props) => {
34+
const [search, setSearch] = useState('');
35+
const futureEvents = useMemo(
36+
() => gordonEvent.getFutureEvents(unfilteredEvents),
37+
[unfilteredEvents],
38+
);
39+
const [timeFilter, setTimeFilter] = useState(DefaultTimeFilter);
40+
const [searchParams, setSearchParams] = useSearchParams();
41+
const includePast = useMemo(() => searchParams.get(IncludePastKey) === 'true', [searchParams]);
42+
const filters = useMemo(() => searchParams.getAll('filters'), [searchParams]);
43+
44+
useEffect(() => {
45+
const events = includePast ? unfilteredEvents : futureEvents;
46+
const filteredEvents = gordonEvent.getFilteredEvents(events, filters, search, timeFilter);
47+
onFilterEvents(filteredEvents);
48+
setLoading(false);
49+
}, [
50+
includePast,
51+
unfilteredEvents,
52+
futureEvents,
53+
filters,
54+
search,
55+
timeFilter,
56+
onFilterEvents,
57+
setLoading,
58+
]);
59+
60+
const clearAll = () => {
61+
setLoading(true);
62+
setSearchParams(new URLSearchParams());
63+
setSearch('');
64+
setTimeFilter(DefaultTimeFilter);
65+
};
66+
67+
const handleChangeSearch = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
68+
setLoading(true);
69+
setSearch(event.target.value);
70+
};
71+
72+
const handleChangeFilters = (_event: unknown, filters: string[]) => {
73+
setLoading(true);
74+
setSearchParams((prev) => {
75+
prev.delete(FiltersKey);
76+
filters.forEach((filter) => prev.append(FiltersKey, filter));
77+
return prev;
78+
});
79+
};
80+
81+
const handleChangeIncludePast = () => {
82+
setLoading(true);
83+
setSearchParams((prev) => {
84+
const didInclude = prev.get(IncludePastKey) === 'true';
85+
const willInclude = !didInclude;
86+
87+
if (willInclude) {
88+
prev.set(IncludePastKey, willInclude.toString());
89+
} else {
90+
prev.delete(IncludePastKey);
91+
}
92+
93+
return prev;
94+
});
95+
};
96+
97+
const handleChangeTimeWindow = (
98+
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
99+
) => {
100+
setLoading(true);
101+
setTimeFilter(event.target.value);
102+
};
103+
104+
return (
105+
<Card>
106+
<CardHeader
107+
sx={{ textAlign: 'center' }}
108+
title={
109+
<>
110+
Search
111+
<b className={styles.events_gordon_text}> Gordon </b>
112+
Events
113+
</>
114+
}
115+
className="gc360_header"
116+
id="top"
117+
/>
118+
<CardContent>
119+
<Grid container spacing={2}>
120+
<Grid item xs={12} sm={6} md={3}>
121+
<TextField
122+
id="search"
123+
label="Search"
124+
type="search"
125+
variant="filled"
126+
value={search}
127+
onChange={handleChangeSearch}
128+
InputProps={{
129+
startAdornment: (
130+
<InputAdornment position="start">
131+
<Search className={styles.events_icon} />
132+
</InputAdornment>
133+
),
134+
}}
135+
fullWidth
136+
/>
137+
</Grid>
138+
139+
<Grid item xs={12} sm={6} md={3}>
140+
<Autocomplete
141+
id="event-filters"
142+
multiple
143+
options={EVENT_FILTERS}
144+
value={filters}
145+
onChange={handleChangeFilters}
146+
filterSelectedOptions
147+
renderTags={(value, getTagProps) =>
148+
value.map((option, index) => (
149+
<Chip label={option} {...getTagProps({ index })} color="secondary" />
150+
))
151+
}
152+
renderInput={(param) => <TextField {...param} variant="filled" label="Event Type" />}
153+
fullWidth
154+
/>
155+
</Grid>
156+
157+
<Grid item xs={12} sm={6} md={3}>
158+
<TextField
159+
value={timeFilter}
160+
onChange={handleChangeTimeWindow}
161+
select
162+
label="Time Window"
163+
variant="filled"
164+
fullWidth
165+
>
166+
<MenuItem value="" key="All">
167+
<em>All</em>
168+
</MenuItem>
169+
{TIME_FILTERS.map((timeFilter) => (
170+
<MenuItem value={timeFilter} key={timeFilter}>
171+
{timeFilter}
172+
</MenuItem>
173+
))}
174+
</TextField>
175+
</Grid>
176+
177+
<Grid item xs={12} sm={6} md={3}>
178+
<FormControlLabel
179+
control={
180+
<Checkbox
181+
checked={includePast}
182+
onChange={handleChangeIncludePast}
183+
color="secondary"
184+
/>
185+
}
186+
label="Show Past Events"
187+
/>
188+
</Grid>
189+
</Grid>
190+
</CardContent>
191+
<CardActions>
192+
<Button color="neutral" variant="contained" onClick={clearAll}>
193+
CLEAR ALL
194+
</Button>
195+
196+
<Button
197+
variant="contained"
198+
color="secondary"
199+
component={HashLink}
200+
to="#bottom"
201+
smooth
202+
id="top"
203+
>
204+
Jump to Bottom
205+
</Button>
206+
</CardActions>
207+
</Card>
208+
);
209+
};
210+
export default EventFilters;

‎src/components/EventList/EventList.module.scss ‎src/views/Events/components/EventList/EventList.module.scss

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@
44
}
55

66
.header_text {
7-
padding-block: 0.3rem;
8-
padding-inline: 10px;
9-
}
7+
color: var(--mui-palette-primary-contrastText);
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.event_item {
2+
padding: 10px;
3+
4+
transition: background-color 200ms ease;
5+
cursor: pointer;
6+
7+
background-color: var(--mui-palette-neutral-light);
8+
&:nth-of-type(even) {
9+
background-color: var(--mui-palette-neutral-200);
10+
}
11+
12+
&:hover {
13+
background-color: var(--mui-palette-secondary-main);
14+
color: var(--mui-palette-primary-contrastText);
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { Collapse, Grid, Typography } from '@mui/material';
2+
import { useState } from 'react';
3+
import styles from './EventItem.module.css';
4+
import 'add-to-calendar-button';
5+
import { format } from 'date-fns';
6+
import { STORAGE_COLOR_PREFERENCE_KEY } from 'theme';
7+
import { Event } from 'services/event';
8+
9+
const checkLightMode = (mode: string | null) => {
10+
if (mode === 'dark') {
11+
return 'dark';
12+
} else if (mode === 'light') {
13+
return 'light';
14+
} else if (mode === 'bodyScheme') {
15+
return 'bodyScheme';
16+
} else {
17+
return 'system';
18+
}
19+
};
20+
21+
type Props = {
22+
event: Event;
23+
};
24+
25+
const EventItem = ({ event }: Props) => {
26+
const [expanded, setExpanded] = useState(false);
27+
28+
return (
29+
<Grid
30+
component="section"
31+
container
32+
direction="row"
33+
onClick={() => setExpanded((e) => !e)}
34+
className={styles.event_item}
35+
tabIndex={0}
36+
onKeyDown={(e) => {
37+
if (e.key === 'Enter' && e.currentTarget === e.target) {
38+
setExpanded((e) => !e);
39+
}
40+
}}
41+
>
42+
<Grid item xs={12} sm={4}>
43+
<Typography variant="h6">{event.title}</Typography>
44+
</Grid>
45+
<Grid item xs={4} sm={2}>
46+
<Typography>{event.date}</Typography>
47+
</Grid>
48+
<Grid item xs={8} sm={2}>
49+
<Typography>{event.timeRange}</Typography>
50+
</Grid>
51+
<Grid item xs={12} sm={4}>
52+
<Typography>{event.location}</Typography>
53+
</Grid>
54+
<Collapse in={expanded} timeout="auto" unmountOnExit>
55+
<Typography className={styles.descriptionText} tabIndex={0}>
56+
{event.Description || 'No description available'}
57+
</Typography>
58+
{event.StartDate !== '' && event.EndDate !== '' && (
59+
<add-to-calendar-button
60+
name={event.title}
61+
options="'Google','Microsoft365|Gordon Outlook','Apple','Outlook.com|Outlook','MicrosoftTeams'"
62+
location={event.location}
63+
startDate={format(new Date(event.StartDate), 'yyyy-MM-dd')}
64+
endDate={format(new Date(event.EndDate), 'yyyy-MM-dd')}
65+
startTime={format(new Date(event.StartDate), 'HH:mm')}
66+
endTime={format(new Date(event.EndDate), 'HH:mm')}
67+
//default timeZone setting is "currentBrowser", and saved setting "America/New_York" if needed in case
68+
timeZone="currentBrowser"
69+
label="Add to Calendar"
70+
description={event.Description}
71+
//Get user theme mode preference
72+
lightMode={checkLightMode(localStorage.getItem(STORAGE_COLOR_PREFERENCE_KEY))}
73+
></add-to-calendar-button>
74+
)}
75+
</Collapse>
76+
</Grid>
77+
);
78+
};
79+
80+
export default EventItem;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { windowBreakWidths } from 'theme';
2+
import EventItem from './components/EventItem';
3+
4+
import useWindowSize from 'hooks/useWindowSize';
5+
6+
import { Card, CardHeader, Grid, List, Typography } from '@mui/material';
7+
import { Event } from 'services/event';
8+
import styles from './EventList.module.css';
9+
import GordonLoader from 'components/Loader';
10+
11+
type Props = {
12+
events: Event[];
13+
loading: boolean;
14+
};
15+
16+
const tableHeader = (
17+
<Grid container direction="row">
18+
<Grid item xs={4} key="Name">
19+
<Typography variant="h5" className={styles.header_text}>
20+
Name
21+
</Typography>
22+
</Grid>
23+
<Grid item xs={2} key="Date">
24+
<Typography variant="h5" className={styles.header_text}>
25+
Date
26+
</Typography>
27+
</Grid>
28+
<Grid item xs={2} key="Time">
29+
<Typography variant="h5" className={styles.header_text}>
30+
Time
31+
</Typography>
32+
</Grid>
33+
<Grid item xs={4} key="Location">
34+
<Typography variant="h5" className={styles.header_text}>
35+
Location
36+
</Typography>
37+
</Grid>
38+
</Grid>
39+
);
40+
41+
const noEvents = (
42+
<Grid item alignItems="center">
43+
<br />
44+
<Typography variant="h4" align="center">
45+
No Events To Show
46+
</Typography>
47+
<br />
48+
</Grid>
49+
);
50+
51+
const EventList = ({ events, loading }: Props) => {
52+
const [width] = useWindowSize();
53+
54+
return (
55+
<Card>
56+
<CardHeader
57+
title="Events"
58+
subheader={width > windowBreakWidths.breakSM ? tableHeader : undefined}
59+
className={styles.header}
60+
/>
61+
{loading ? (
62+
<GordonLoader disableShrink />
63+
) : (
64+
<List className="gc360_event_list" disablePadding>
65+
{events?.length < 1
66+
? noEvents
67+
: events.map((event) => <EventItem event={event} key={event.Event_ID} />)}
68+
</List>
69+
)}
70+
</Card>
71+
);
72+
};
73+
export default EventList;

‎src/views/Events/index.jsx

-509
This file was deleted.

‎src/views/Events/index.tsx

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { useIsAuthenticated } from '@azure/msal-react';
2+
import { Button, CardHeader, Grid } from '@mui/material';
3+
import EventList from './components/EventList';
4+
import { useEffect, useState } from 'react';
5+
import gordonEvent, { Event } from 'services/event';
6+
import EventFilters from './components/EventFilters';
7+
import { HashLink } from 'react-router-hash-link';
8+
9+
const Events = () => {
10+
const [unfilteredEvents, setUnfilteredEvents] = useState<Event[]>([]);
11+
const [filteredEvents, setFilteredEvents] = useState<Event[]>([]);
12+
const [loading, setLoading] = useState(true);
13+
const isAuthenticated = useIsAuthenticated();
14+
15+
useEffect(() => {
16+
const loadEvents = async () => {
17+
setLoading(true);
18+
19+
const allEvents = isAuthenticated
20+
? await gordonEvent.getAllEvents()
21+
: await gordonEvent.getAllGuestEvents();
22+
setUnfilteredEvents(allEvents);
23+
24+
setLoading(false);
25+
};
26+
27+
loadEvents();
28+
}, [isAuthenticated]);
29+
30+
const content = <EventList loading={loading} events={filteredEvents} />;
31+
32+
return (
33+
<Grid container justifyContent="center" spacing={6}>
34+
<Grid item xs={12} lg={10} xl={8}>
35+
<EventFilters
36+
unfilteredEvents={unfilteredEvents}
37+
onFilterEvents={setFilteredEvents}
38+
setLoading={setLoading}
39+
/>
40+
<br />
41+
42+
<Grid item xs={12}>
43+
{content}
44+
</Grid>
45+
<CardHeader
46+
xs={12}
47+
className="gc360_header"
48+
title={
49+
<div style={{ textAlign: 'center' }}>
50+
<p>To find other events, adjust your filters above.</p>
51+
<Button
52+
variant="contained"
53+
color="secondary"
54+
component={HashLink}
55+
to="#top"
56+
smooth
57+
id="bottom"
58+
>
59+
Jump to Top
60+
</Button>
61+
</div>
62+
}
63+
id="bottom"
64+
/>
65+
</Grid>
66+
</Grid>
67+
);
68+
};
69+
export default Events;

‎src/views/EventsAttended/EventsAttended.module.scss

-3
This file was deleted.

‎src/views/EventsAttended/index.jsx

-66
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.