return $episode->toArray();
}
+ public function delete(Request $request, Episode $episode) {
+ $this->authorize('delete', $episode);
+ $episode->callOff();
+ $episode->delete();
+ return $episode->toArray();
+ }
+
public function update(Request $request, Episode $episode) {
$this->authorize('update', $episode);
$validatedEpisode = $this->validateEpisode($request);
*/
public function delete(User $user, Episode $episode)
{
- return false;
+ return $user->isEventAdmin($episode->event);
}
/**
--- /dev/null
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Alert, Button, Modal } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+
+import Players from './Players';
+
+const Dialog = ({
+ episode,
+ onHide,
+ onSubmit,
+ show,
+}) => {
+ const { t } = useTranslation();
+
+ return <Modal className="episode-delete-dialog" onHide={onHide} show={show} size="lg">
+ <Modal.Header closeButton>
+ <Modal.Title>
+ {t('episodes.delete')}
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ {episode.title || episode.event ?
+ <h4 className="episode-title fs-5 fs-md-4">
+ {!episode.confirmed ?
+ <span>{`${t('episodes.unconfirmed')} `}</span>
+ : null}
+ <span>{episode.title || episode.event.title}</span>
+ </h4>
+ : null}
+ <p>{t('episodes.startTime', { date: new Date(episode.start) })}</p>
+ {episode.comment ?
+ <p className="episode-comment">
+ {episode.comment}
+ </p>
+ : null}
+ {episode.players && episode.players.length ?
+ <Players players={episode.players} />
+ : null}
+ <Alert variant="warning">
+ {t('episodes.deleteQuestion')}
+ </Alert>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button onClick={onHide} variant="secondary">
+ {t('button.cancel')}
+ </Button>
+ <Button onClick={onSubmit} variant="danger">
+ {t('button.delete')}
+ </Button>
+ </Modal.Footer>
+ </Modal>;
+};
+
+Dialog.propTypes = {
+ episode: PropTypes.shape({
+ comment: PropTypes.string,
+ confirmed: PropTypes.bool,
+ event: PropTypes.shape({
+ title: PropTypes.string,
+ }),
+ id: PropTypes.number,
+ players: PropTypes.arrayOf(PropTypes.shape({
+ })),
+ start: PropTypes.string,
+ title: PropTypes.string,
+ }),
+ onHide: PropTypes.func,
+ onSubmit: PropTypes.func,
+ show: PropTypes.bool,
+};
+
+export default Dialog;
const Dialog = ({
episode,
+ onDelete,
onHide,
onSubmit,
show,
<Form
episode={episode}
onCancel={onHide}
+ onDelete={onDelete}
onSubmit={onSubmit}
/>
</React.Suspense>
episode: PropTypes.shape({
id: PropTypes.number,
}),
+ onDelete: PropTypes.func,
onHide: PropTypes.func,
onSubmit: PropTypes.func,
show: PropTypes.bool,
handleChange,
handleSubmit,
onCancel,
+ onDelete,
setFieldValue,
touched,
values,
</Col>
</Row>
<Modal.Footer>
+ {onDelete ?
+ <Button className="me-auto" onClick={() => onDelete(episode)} variant="outline-danger">
+ {t('episodes.delete')}
+ </Button>
+ : null}
{onCancel ?
<Button onClick={onCancel} variant="secondary">
{t('button.cancel')}
handleChange: PropTypes.func,
handleSubmit: PropTypes.func,
onCancel: PropTypes.func,
+ onDelete: PropTypes.func,
setFieldValue: PropTypes.func,
touched: PropTypes.shape({
comment: PropTypes.bool,
import { useUser } from './user';
import ApplyDialog from '../components/episodes/ApplyDialog';
+import DeleteEpisodeDialog from '../components/episodes/DeleteDialog';
import EpisodeDialog from '../components/episodes/Dialog';
import RestreamDialog from '../components/episodes/RestreamDialog';
onAddEpisode: null,
onAddRestream: null,
onApplyRestream: null,
+ onDeleteEpisode: null,
onEditEpisode: null,
onEditRestream: null,
});
export const EpisodesProvider = ({ children, setEpisodes }) => {
const [applyAs, setApplyAs] = React.useState('commentary');
+ const [deleteEpisode, setDeleteEpisode] = React.useState(null);
const [editEpisode, setEditEpisode] = React.useState(null);
const [restreamChannel, setRestreamChannel] = React.useState(null);
const [restreamEpisode, setRestreamEpisode] = React.useState(null);
const [showApplyDialog, setShowApplyDialog] = React.useState(false);
+ const [showDeleteEpisodeDialog, setShowDeleteEpisodeDialog] = React.useState(false);
const [showEpisodeDialog, setShowEpisodeDialog] = React.useState(false);
const [showRestreamDialog, setShowRestreamDialog] = React.useState(false);
setShowEpisodeDialog(true);
}, []);
+ const onDeleteEpisode = React.useCallback((episode) => {
+ setDeleteEpisode(episode);
+ setShowDeleteEpisodeDialog(true);
+ setShowEpisodeDialog(false);
+ }, []);
+
+ const onHideDeleteEpisodeDialog = React.useCallback(() => {
+ setShowDeleteEpisodeDialog(false);
+ if (editEpisode?.id === deleteEpisode.id) {
+ setShowEpisodeDialog(true);
+ }
+ }, [editEpisode, deleteEpisode]);
+
+ const onSubmitDeleteEpisodeDialog = React.useCallback(async () => {
+ try {
+ await axios.delete(`/api/episodes/${deleteEpisode.id}`);
+ setEpisodes(episodes => episodes.filter(e => e.id !== deleteEpisode.id));
+ setShowDeleteEpisodeDialog(false);
+ toastr.success(t('episodes.deleteSuccess'));
+ } catch (error) {
+ toastr.error(t('episodes.deleteError', { error }));
+ }
+ }, [deleteEpisode, t]);
+
const onEditEpisode = React.useCallback((episode) => {
setEditEpisode(episode);
setShowEpisodeDialog(true);
onAddEpisode,
onAddRestream,
onApplyRestream,
+ onDeleteEpisode,
onEditEpisode,
onEditRestream,
}), [
onAddEpisode,
onAddRestream,
onApplyRestream,
+ onDeleteEpisode,
onEditEpisode,
onEditRestream,
]);
onSubmit={onSubmitApplyDialog}
show={showApplyDialog}
/>
+ <DeleteEpisodeDialog
+ episode={deleteEpisode}
+ onHide={onHideDeleteEpisodeDialog}
+ onSubmit={onSubmitDeleteEpisodeDialog}
+ show={showDeleteEpisodeDialog}
+ />
<EpisodeDialog
episode={editEpisode}
+ onDelete={onDeleteEpisode}
onHide={onHideEpisodeDialog}
onSubmit={onSubmitEpisodeDialog}
show={showEpisodeDialog}
create: 'Neue Episode',
createError: 'Fehler beim Speichern',
createSuccess: 'Episode eingetragen',
+ delete: 'Episode löschen',
+ deleteError: 'Fehler beim Löschen',
+ deleteQuestion: 'Diese Episode wirklich löschen? Alle eingetragenen Restreams werden abgesagt und erstellte Discord Events gelöscht.',
+ deleteSuccess: 'Episode gelöscht',
edit: 'Episode bearbeiten',
editError: 'Fehler beim Speichern',
editSuccess: 'Änderungen gespeichert',
create: 'Add episode',
createError: 'Error saving episode',
createSuccess: 'Episode added',
+ delete: 'Delete episode',
+ deleteError: 'Error deleting episode',
+ deleteQuestion: 'Are you sure you want to delete this episode? All associated restreams will be called off and created Discord events cancelled.',
+ deleteSuccess: 'Episode deleted',
edit: 'Edit episode',
editError: 'Error saving episode',
editSuccess: 'Saved changes',
margin-bottom: 0;
}
}
- .episode-players {
- display: grid;
- grid-template-columns: 1fr 1fr;
- }
@include media-breakpoint-down(md) {
.episode-event {
margin-left: 5rem;
text-decoration: underline;
}
}
+}
+
+.episodes-item,
+.episode-delete-dialog {
+ .episode-players {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ }
.player-link {
border: none;