'before' => 'nullable|date',
'event' => 'nullable|array',
'event.*' => 'numeric',
+ 'limit' => 'numeric',
+ 'offset' => 'numeric',
+ 'reverse' => 'boolean',
]);
- $after = isset($validatedData['after']) ? $validatedData['after'] : Carbon::now();
$before = isset($validatedData['before']) ? $validatedData['before'] : Carbon::now()->add(1, 'days');
+ $limit = isset($validatedData['limit']) ? $validatedData['limit'] : 100;
+ $reverse = isset($validatedData['reverse']) ? $validatedData['reverse'] : false;
$episodes = Episode::with(['channels', 'event', 'players', 'players.user'])
->select('episodes.*')
->join('events', 'episodes.event_id', '=', 'events.id')
->where('episodes.confirmed', '=', true)
- ->where(DB::raw('DATE_ADD(`episodes`.`start`, INTERVAL COALESCE(`episodes`.`estimate`, 0) SECOND)'), '>=', $after)
- ->where('episodes.start', '<=', $before)
->where('events.visible', '=', true)
- ->orderBy('episodes.start')
+ ->orderBy('episodes.start', $reverse ? 'DESC' : 'ASC')
->limit(1000);
+ if (isset($validatedData['after'])) {
+ $episodes = $episodes->where(
+ DB::raw('DATE_ADD(`episodes`.`start`, INTERVAL COALESCE(`episodes`.`estimate`, 0) SECOND)'),
+ '>=', $validatedData['after']);
+ }
+ if (isset($validatedData['before'])) {
+ $episodes = $episodes->where('episodes.start', '<=', $validatedData['before']);
+ }
if (!empty($validatedData['event'])) {
$episodes = $episodes->whereIn('episodes.event_id', $validatedData['event']);
}
'crew.user',
]);
}
+ $episodes->limit($limit);
return $episodes->get()->toJson();
}
import PropTypes from 'prop-types';
import React from 'react';
-import { Button } from 'react-bootstrap';
+import { Alert, Button } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import Icon from '../common/Icon';
import RawHTML from '../common/RawHTML';
+import { hasConcluded } from '../../helpers/Event';
import { getTranslation } from '../../helpers/Technique';
import i18n from '../../i18n';
{event.description ?
<RawHTML html={getTranslation(event.description, 'description', i18n.language)} />
: null}
+ {hasConcluded(event) ?
+ <Alert variant="info">
+ {t('events.concluded')}
+ </Alert>
+ : null}
</>;
};
event: PropTypes.shape({
description: PropTypes.shape({
}),
+ end: PropTypes.string,
title: PropTypes.string,
}),
};
--- /dev/null
+import moment from 'moment';
+
+export const hasConcluded = event => event && event.end && moment(event.end).isBefore(moment());
},
},
events: {
+ concluded: 'Diese Veranstaltung is abgeschlossen.',
+ pastEpisodes: 'Vergangene Rennen',
upcomingEpisodes: 'Anstehende Rennen',
},
footer: {
},
},
events: {
+ concluded: 'This event has concluded.',
+ pastEpisodes: 'Past races',
upcomingEpisodes: 'Upcoming races',
},
footer: {
import EpisodeList from '../components/episodes/List';
import Detail from '../components/events/Detail';
import Dialog from '../components/techniques/Dialog';
+import { hasConcluded } from '../helpers/Event';
import {
mayEditContent,
} from '../helpers/permissions';
setEpisodes([]);
return;
}
+ const params = {
+ event: [event.id],
+ };
+ if (hasConcluded(event)) {
+ params.limit = 25;
+ params.reverse = '1';
+ } else {
+ params.after = moment().subtract(3, 'hours').toISOString();
+ params.before = moment().add(14, 'days').toISOString();
+ }
axios.get(`/api/episodes`, {
signal: controller.signal,
- params: {
- after: moment().subtract(3, 'hours').toISOString(),
- before: moment().add(14, 'days').toISOString(),
- event: [event.id],
- },
+ params,
}).then(response => {
setEpisodes(response.data || []);
}).catch(e => {
<Container>
<Detail actions={actions} event={event} />
{episodes.length ? <>
- <h2 className="mt-4">{i18n.t('events.upcomingEpisodes')}</h2>
+ <h2 className="mt-4">
+ {i18n.t(hasConcluded(event)
+ ? 'events.pastEpisodes'
+ : 'events.upcomingEpisodes'
+ )}
+ </h2>
<EpisodeList episodes={episodes} />
</> : null}
</Container>