+import axios from 'axios';
+import React from 'react';
+import { Container } from 'react-bootstrap';
+import { Helmet } from 'react-helmet';
+import { useTranslation } from 'react-i18next';
+
+import CanonicalLinks from '../components/common/CanonicalLinks';
+import ErrorBoundary from '../components/common/ErrorBoundary';
+import ErrorMessage from '../components/common/ErrorMessage';
+import Loading from '../components/common/Loading';
+import List from '../components/events/List';
+
+const Events = () => {
+ const { t } = useTranslation();
+
+ const [error, setError] = React.useState(null);
+ const [loading, setLoading] = React.useState(true);
+ const [events, setEvents] = React.useState([]);
+
+ const fetchEvents = React.useCallback(async (controller) => {
+ const params = {
+ order: 'recency',
+ with: ['description'],
+ };
+ try {
+ const response = await axios.get(`/api/events`, {
+ signal: controller.signal,
+ params,
+ });
+ return response.data || [];
+ } catch (error) {
+ if (!axios.isCancel(error)) {
+ throw error;
+ }
+ return [];
+ }
+ }, []);
+
+ React.useEffect(() => {
+ const controller = new AbortController();
+ setLoading(true);
+ fetchEvents(controller)
+ .then(events => {
+ setError(null);
+ setLoading(false);
+ setEvents(events);
+ })
+ .catch(error => {
+ setError(error);
+ setLoading(false);
+ setEvents([]);
+ });
+ return () => {
+ controller.abort();
+ };
+ }, [fetchEvents]);
+
+ const evergreen = React.useMemo(() =>
+ events.filter(event => !event.start)
+ , [events]);
+ const ongoing = React.useMemo(() =>
+ events.filter(event => event.start && !event.end)
+ , [events]);
+ const past = React.useMemo(() =>
+ events.filter(event => event.end)
+ , [events]);
+
+ if (loading) {
+ return <Loading />;
+ }
+
+ if (error) {
+ return <ErrorMessage error={error} />;
+ }
+
+ return <ErrorBoundary>
+ <Helmet>
+ <title>
+ {t('events.heading')}
+ </title>
+ </Helmet>
+ <CanonicalLinks base={`/events`} />
+ <Container>
+ <h1>{t('events.ongoing')}</h1>
+ <List events={ongoing} />
+ <h1>{t('events.evergreen')}</h1>
+ <List events={evergreen} />
+ <h1>{t('events.past')}</h1>
+ <List events={past} />
+ </Container>
+ </ErrorBoundary>;
+};
+
+export default Events;