return $tournament->toJson();
}
+ public function discordSettings(Request $request, Tournament $tournament) {
+ $this->authorize('update', $tournament);
+ $validatedData = $request->validate([
+ 'round_template' => 'string|nullable',
+ ]);
+ if (array_key_exists('round_template', $validatedData)) {
+ $tournament->discord_round_template = $validatedData['round_template'];
+ }
+ $tournament->save();
+ if ($tournament->wasChanged()) {
+ TournamentChanged::dispatch($tournament);
+ Protocol::tournamentDiscordSettings($tournament, $request->user());
+ }
+ return $tournament->toJson();
+ }
+
public function open(Request $request, Tournament $tournament) {
$this->authorize('update', $tournament);
$tournament->accept_applications = true;
ProtocolAdded::dispatch($protocol);
}
+ public static function tournamentDiscordSettings(Tournament $tournament, User $user = null) {
+ $protocol = static::create([
+ 'tournament_id' => $tournament->id,
+ 'user_id' => $user ? $user->id : null,
+ 'type' => 'tournament.discordSettings',
+ 'details' => [
+ 'tournament' => static::tournamentMemo($tournament),
+ ],
+ ]);
+ ProtocolAdded::dispatch($protocol);
+ }
+
public static function tournamentLocked(Tournament $tournament, User $user = null) {
$protocol = static::create([
'tournament_id' => $tournament->id,
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::table('tournaments', function(Blueprint $table) {
+ $table->string('discord_round_template')->default('');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('tournaments', function(Blueprint $table) {
+ $table->dropColumn('discord_round_template');
+ });
+ }
+};
--- /dev/null
+import axios from 'axios';
+import { withFormik } from 'formik';
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Button, Col, Form, Row } from 'react-bootstrap';
+import { withTranslation } from 'react-i18next';
+import toastr from 'toastr';
+
+import laravelErrorsToFormik from '../../helpers/laravelErrorsToFormik';
+import i18n from '../../i18n';
+import yup from '../../schema/yup';
+
+const DiscordForm = ({
+ errors,
+ handleBlur,
+ handleChange,
+ handleSubmit,
+ touched,
+ values,
+}) =>
+<Form noValidate onSubmit={handleSubmit}>
+ <fieldset>
+ <legend>{i18n.t('tournaments.discordSettings')}</legend>
+ <Form.Group controlId="tournament.discord_round_template">
+ <Form.Label>
+ {i18n.t('tournaments.discordRoundTemplate')}
+ </Form.Label>
+ <Form.Control
+ isInvalid={!!(touched.round_template && errors.round_template)}
+ name="round_template"
+ onBlur={handleBlur}
+ onChange={handleChange}
+ type="text"
+ value={values.round_template || ''}
+ />
+ </Form.Group>
+ <Button className="mt-3" type="submit" variant="primary">
+ {i18n.t('button.save')}
+ </Button>
+ </fieldset>
+</Form>;
+
+DiscordForm.propTypes = {
+ errors: PropTypes.shape({
+ round_template: PropTypes.string,
+ }),
+ handleBlur: PropTypes.func,
+ handleChange: PropTypes.func,
+ handleSubmit: PropTypes.func,
+ touched: PropTypes.shape({
+ round_template: PropTypes.bool,
+ }),
+ values: PropTypes.shape({
+ round_template: PropTypes.string,
+ }),
+};
+
+export default withFormik({
+ displayName: 'DiscordForm',
+ enableReinitialize: true,
+ handleSubmit: async (values, actions) => {
+ const { round_template } = values;
+ const { setErrors } = actions;
+ const { tournament } = actions.props;
+ try {
+ await axios.post(`/api/tournaments/${tournament.id}/discord-settings`, {
+ round_template,
+ });
+ toastr.success(i18n.t('tournaments.discordSettingsSuccess'));
+ } catch (e) {
+ toastr.error(i18n.t('tournaments.discordSettingsError'));
+ if (e.response && e.response.data && e.response.data.errors) {
+ setErrors(laravelErrorsToFormik(e.response.data.errors));
+ }
+ }
+ },
+ mapPropsToValues: ({ tournament }) => ({
+ round_template: tournament.discord_round_template || '',
+ }),
+ validationSchema: yup.object().shape({
+ round_template: yup.string(),
+ }),
+})(withTranslation()(DiscordForm));
import axios from 'axios';
import PropTypes from 'prop-types';
import React from 'react';
-import { Button, Modal } from 'react-bootstrap';
+import { Button, Col, Modal, Row } from 'react-bootstrap';
import { withTranslation } from 'react-i18next';
import toastr from 'toastr';
+import DiscordForm from './DiscordForm';
import DiscordSelect from '../common/DiscordSelect';
import Icon from '../common/Icon';
import ToggleSwitch from '../common/ToggleSwitch';
}
};
+const inviteUrl = 'https://discordapp.com/oauth2/authorize?client_id=951113702839549982&scope=bot';
+
const SettingsDialog = ({
onHide,
show,
tournament,
}) =>
-<Modal className="settings-dialog" onHide={onHide} show={show}>
+<Modal
+ className="settings-dialog"
+ onHide={onHide}
+ show={show}
+ size={tournament.discord ? 'lg' : 'md'}
+>
<Modal.Header closeButton>
<Modal.Title>
{i18n.t('tournaments.settings')}
</Modal.Title>
</Modal.Header>
<Modal.Body>
- <div className="d-flex align-items-center justify-content-between mb-3">
- <span>{i18n.t('tournaments.open')}</span>
- <ToggleSwitch
- onChange={({ target: { value } }) => value ? open(tournament) : close(tournament)}
- value={tournament.accept_applications}
- />
- </div>
- <div className="d-flex align-items-center justify-content-between mb-3">
- <span>{i18n.t('tournaments.locked')}</span>
- <ToggleSwitch
- onChange={({ target: { value } }) => value ? lock(tournament) : unlock(tournament)}
- value={tournament.locked}
- />
- </div>
- <div className="d-flex align-items-center justify-content-between">
- <div>
- <p>{i18n.t('tournaments.discord')}</p>
- <div>
- <Button
- href="https://discordapp.com/oauth2/authorize?client_id=951113702839549982&scope=bot"
- target="_blank"
- variant="discord"
- >
- <Icon.DISCORD />
- {' '}
- {i18n.t('tournaments.inviteBot')}
- </Button>
+ <Row>
+ <Col sm={tournament.discord ? 6 : 12}>
+ <div className="d-flex align-items-center justify-content-between mb-3">
+ <span>{i18n.t('tournaments.open')}</span>
+ <ToggleSwitch
+ onChange={({ target: { value } }) => value
+ ? open(tournament) : close(tournament)}
+ value={tournament.accept_applications}
+ />
+ </div>
+ <div className="d-flex align-items-center justify-content-between mb-3">
+ <span>{i18n.t('tournaments.locked')}</span>
+ <ToggleSwitch
+ onChange={({ target: { value } }) => value
+ ? lock(tournament) : unlock(tournament)}
+ value={tournament.locked}
+ />
+ </div>
+ <div className="d-flex align-items-center justify-content-between">
+ <div>
+ <p>{i18n.t('tournaments.discord')}</p>
+ {!tournament.discord ?
+ <div>
+ <Button
+ href={inviteUrl}
+ target="_blank"
+ variant="discord"
+ >
+ <Icon.DISCORD />
+ {' '}
+ {i18n.t('tournaments.inviteBot')}
+ </Button>
+ </div>
+ : null}
+ </div>
+ <DiscordSelect
+ onChange={({ target: { value } }) => setDiscord(tournament, value)}
+ value={tournament.discord}
+ />
</div>
- </div>
- <DiscordSelect
- onChange={({ target: { value } }) => setDiscord(tournament, value)}
- value={tournament.discord}
- />
- </div>
+ </Col>
+ {tournament.discord ?
+ <Col sm={6}>
+ <DiscordForm tournament={tournament} />
+ </Col>
+ : null}
+ </Row>
</Modal.Body>
<Modal.Footer>
<Button onClick={onHide} variant="secondary">
closeSuccess: 'Anmeldung geschlossen',
discord: 'Discord',
discordError: 'Fehler beim Zuordnen',
+ discordRoundTemplate: 'Template für Runden-Kanäle',
+ discordSettings: 'Discord Einstellungen',
+ discordSettingsError: 'Fehler beim Speichern der Discord Einstellungen',
+ discordSettingsSuccess: 'Discord Einstellungen gespeichert',
discordSuccess: 'Discord verknüpft',
inviteBot: 'Bot einladen',
locked: 'Turnier sperren',
closeSuccess: 'Registration closed',
discord: 'Discord',
discordError: 'Error connecting',
+ discordRoundTemplate: 'Template for round channels',
+ discordSettings: 'Discord settings',
+ discordSettingsError: 'Error saving discord settings',
+ discordSettingsSuccess: 'Discord settings saved',
discordSuccess: 'Discord associated',
inviteBot: 'Invite bot',
locked: 'Lock rounds',
Route::post('tournaments/{tournament}/apply', 'App\Http\Controllers\TournamentController@apply');
Route::post('tournaments/{tournament}/close', 'App\Http\Controllers\TournamentController@close');
Route::post('tournaments/{tournament}/discord', 'App\Http\Controllers\TournamentController@discord');
+Route::post('tournaments/{tournament}/discord-settings', 'App\Http\Controllers\TournamentController@discordSettings');
Route::post('tournaments/{tournament}/lock', 'App\Http\Controllers\TournamentController@lock');
Route::post('tournaments/{tournament}/open', 'App\Http\Controllers\TournamentController@open');
Route::post('tournaments/{tournament}/unlock', 'App\Http\Controllers\TournamentController@unlock');