From 7caf2ef743d7eeb9adf8494b1d194998886226e8 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Wed, 27 Apr 2022 16:50:24 +0200 Subject: [PATCH] round category select --- .../Controllers/DiscordChannelController.php | 37 ++++++++++ app/Http/Controllers/TournamentController.php | 4 + app/Models/DiscordGuild.php | 4 +- .../2022_04_27_144535_round_category.php | 32 ++++++++ .../components/common/DiscordChannelSelect.js | 74 +++++++++++++++++++ .../js/components/tournament/DiscordForm.js | 29 +++++++- resources/js/i18n/de.js | 2 + resources/js/i18n/en.js | 2 + routes/api.php | 1 + 9 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 app/Http/Controllers/DiscordChannelController.php create mode 100644 database/migrations/2022_04_27_144535_round_category.php create mode 100644 resources/js/components/common/DiscordChannelSelect.js diff --git a/app/Http/Controllers/DiscordChannelController.php b/app/Http/Controllers/DiscordChannelController.php new file mode 100644 index 0000000..aef47e3 --- /dev/null +++ b/app/Http/Controllers/DiscordChannelController.php @@ -0,0 +1,37 @@ +firstOrFail(); + $this->authorize('view', $guild); + + $validatedData = $request->validate([ + 'parents' => 'array|nullable', + 'parents.*' => 'string', + 'phrase' => 'string|nullable', + 'types' => 'array|nullable', + 'types.*' => 'integer', + ]); + + $channels = $guild->channels(); + if (!empty($validatedData['parents'])) { + $channels = $channels->whereIn('parent', $validatedData['parents']); + } + if (!empty($validatedData['phrase'])) { + $channels = $channels->where('name', 'LIKE', '%'.$validatedData['phrase'].'%'); + } + if (!empty($validatedData['types'])) { + $channels = $channels->whereIn('type', $validatedData['types']); + } + return $channels->get()->toJson(); + } + +} diff --git a/app/Http/Controllers/TournamentController.php b/app/Http/Controllers/TournamentController.php index e2fbe94..7912cda 100644 --- a/app/Http/Controllers/TournamentController.php +++ b/app/Http/Controllers/TournamentController.php @@ -63,8 +63,12 @@ class TournamentController extends Controller public function discordSettings(Request $request, Tournament $tournament) { $this->authorize('update', $tournament); $validatedData = $request->validate([ + 'round_category' => 'string|nullable', 'round_template' => 'string|nullable', ]); + if (array_key_exists('round_category', $validatedData)) { + $tournament->discord_round_category = $validatedData['round_category']; + } if (array_key_exists('round_template', $validatedData)) { $tournament->discord_round_template = $validatedData['round_template']; } diff --git a/app/Models/DiscordGuild.php b/app/Models/DiscordGuild.php index 84fd77b..bcbd12d 100644 --- a/app/Models/DiscordGuild.php +++ b/app/Models/DiscordGuild.php @@ -45,11 +45,11 @@ class DiscordGuild extends Model } public function channels() { - return $this->hasMany(DiscordChannel::class); + return $this->hasMany(DiscordChannel::class)->orderBy('position'); } public function roles() { - return $this->hasMany(DiscordRole::class); + return $this->hasMany(DiscordRole::class)->orderBy('position'); } protected $fillable = [ diff --git a/database/migrations/2022_04_27_144535_round_category.php b/database/migrations/2022_04_27_144535_round_category.php new file mode 100644 index 0000000..ccf064d --- /dev/null +++ b/database/migrations/2022_04_27_144535_round_category.php @@ -0,0 +1,32 @@ +string('discord_round_category')->default(''); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('tournaments', function(Blueprint $table) { + $table->dropColumn('discord_round_category'); + }); + } +}; diff --git a/resources/js/components/common/DiscordChannelSelect.js b/resources/js/components/common/DiscordChannelSelect.js new file mode 100644 index 0000000..400ab9f --- /dev/null +++ b/resources/js/components/common/DiscordChannelSelect.js @@ -0,0 +1,74 @@ +import axios from 'axios'; +import PropTypes from 'prop-types'; +import React, { useCallback, useEffect, useState } from 'react'; +import { Form } from 'react-bootstrap'; +import { withTranslation } from 'react-i18next'; + +import debounce from '../../helpers/debounce'; +import i18n from '../../i18n'; + +const DiscordChannelSelect = ({ + guild, + isInvalid, + name, + onBlur, + onChange, + types, + value, +}) => { + const [results, setResults] = useState([]); + + let ctrl = null; + const fetch = useCallback(debounce(async (guild, types) => { + if (ctrl) { + ctrl.abort(); + } + ctrl = new AbortController(); + try { + const response = await axios.get(`/api/discord-guilds/${guild}/channels`, { + params: { + types, + }, + signal: ctrl.signal, + }); + ctrl = null; + setResults(response.data); + } catch (e) { + ctrl = null; + console.error(e); + } + return () => { + if (ctrl) ctrl.abort(); + }; + }, 300), []); + + useEffect(() => { + fetch(guild, types); + }, [guild, ...types]); + + return + + {results && results.length ? results.map(result => + + ) : null} + ; +}; + +DiscordChannelSelect.propTypes = { + guild: PropTypes.string, + isInvalid: PropTypes.bool, + name: PropTypes.string, + onBlur: PropTypes.func, + onChange: PropTypes.func, + types: PropTypes.arrayOf(PropTypes.number), + value: PropTypes.string, +}; + +export default withTranslation()(DiscordChannelSelect); diff --git a/resources/js/components/tournament/DiscordForm.js b/resources/js/components/tournament/DiscordForm.js index f9eba93..d896504 100644 --- a/resources/js/components/tournament/DiscordForm.js +++ b/resources/js/components/tournament/DiscordForm.js @@ -2,10 +2,11 @@ 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 { Button, Form } from 'react-bootstrap'; import { withTranslation } from 'react-i18next'; import toastr from 'toastr'; +import DiscordChannelSelect from '../common/DiscordChannelSelect'; import laravelErrorsToFormik from '../../helpers/laravelErrorsToFormik'; import i18n from '../../i18n'; import yup from '../../schema/yup'; @@ -16,11 +17,26 @@ const DiscordForm = ({ handleChange, handleSubmit, touched, + tournament, values, }) =>
{i18n.t('tournaments.discordSettings')} + + + {i18n.t('tournaments.discordRoundCategory')} + + + {i18n.t('tournaments.discordRoundTemplate')} @@ -42,15 +58,21 @@ const DiscordForm = ({ DiscordForm.propTypes = { errors: PropTypes.shape({ + round_category: PropTypes.string, round_template: PropTypes.string, }), handleBlur: PropTypes.func, handleChange: PropTypes.func, handleSubmit: PropTypes.func, touched: PropTypes.shape({ + round_category: PropTypes.bool, round_template: PropTypes.bool, }), + tournament: PropTypes.shape({ + discord: PropTypes.string, + }), values: PropTypes.shape({ + round_category: PropTypes.string, round_template: PropTypes.string, }), }; @@ -59,11 +81,12 @@ export default withFormik({ displayName: 'DiscordForm', enableReinitialize: true, handleSubmit: async (values, actions) => { - const { round_template } = values; + const { round_category, round_template } = values; const { setErrors } = actions; const { tournament } = actions.props; try { await axios.post(`/api/tournaments/${tournament.id}/discord-settings`, { + round_category, round_template, }); toastr.success(i18n.t('tournaments.discordSettingsSuccess')); @@ -75,9 +98,11 @@ export default withFormik({ } }, mapPropsToValues: ({ tournament }) => ({ + round_category: tournament.discord_round_category || '', round_template: tournament.discord_round_template || '', }), validationSchema: yup.object().shape({ + round_category: yup.string(), round_template: yup.string(), }), })(withTranslation()(DiscordForm)); diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index 5a4889c..331346e 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -221,6 +221,8 @@ export default { closeSuccess: 'Anmeldung geschlossen', discord: 'Discord', discordError: 'Fehler beim Zuordnen', + discordNoCategory: 'Keine Kategorie', + discordRoundCategory: 'Kategorie für Runden-Kanäle', discordRoundTemplate: 'Template für Runden-Kanäle', discordSettings: 'Discord Einstellungen', discordSettingsError: 'Fehler beim Speichern der Discord Einstellungen', diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index 9d4b621..de9b303 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -221,6 +221,8 @@ export default { closeSuccess: 'Registration closed', discord: 'Discord', discordError: 'Error connecting', + discordNoCategory: 'No category', + discordRoundCategory: 'Category for round channels', discordRoundTemplate: 'Template for round channels', discordSettings: 'Discord settings', discordSettingsError: 'Error saving discord settings', diff --git a/routes/api.php b/routes/api.php index fc7fd9c..39a7531 100644 --- a/routes/api.php +++ b/routes/api.php @@ -23,6 +23,7 @@ Route::post('application/{application}/reject', 'App\Http\Controllers\Applicatio Route::get('discord-guilds', 'App\Http\Controllers\DiscordGuildController@search'); Route::get('discord-guilds/{guild_id}', 'App\Http\Controllers\DiscordGuildController@single'); +Route::get('discord-guilds/{guild_id}/channels', 'App\Http\Controllers\DiscordChannelController@search'); Route::get('protocol/{tournament}', 'App\Http\Controllers\ProtocolController@forTournament'); -- 2.39.2