]> git.localhorst.tv Git - alttp.git/commitdiff
round category select
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 27 Apr 2022 14:50:24 +0000 (16:50 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 27 Apr 2022 14:50:24 +0000 (16:50 +0200)
app/Http/Controllers/DiscordChannelController.php [new file with mode: 0644]
app/Http/Controllers/TournamentController.php
app/Models/DiscordGuild.php
database/migrations/2022_04_27_144535_round_category.php [new file with mode: 0644]
resources/js/components/common/DiscordChannelSelect.js [new file with mode: 0644]
resources/js/components/tournament/DiscordForm.js
resources/js/i18n/de.js
resources/js/i18n/en.js
routes/api.php

diff --git a/app/Http/Controllers/DiscordChannelController.php b/app/Http/Controllers/DiscordChannelController.php
new file mode 100644 (file)
index 0000000..aef47e3
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Models\DiscordChannel;
+use App\Models\DiscordGuild;
+use Illuminate\Http\Request;
+
+class DiscordChannelController extends Controller
+{
+
+       public function search(Request $request, $guild_id) {
+               $guild = DiscordGuild::where('guild_id', '=', $guild_id)->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();
+       }
+
+}
index e2fbe941a31b6b2b705b1e35c69724e495351191..7912cda36c95b817c2d6cdbf1fdc1b7c9a416449 100644 (file)
@@ -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'];
                }
index 84fd77bc4af0b785ff7df37aaf3ff3a1f899dc9d..bcbd12dd32b835a4b71bcabf774a0e6a1e31f424 100644 (file)
@@ -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 (file)
index 0000000..ccf064d
--- /dev/null
@@ -0,0 +1,32 @@
+<?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_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 (file)
index 0000000..400ab9f
--- /dev/null
@@ -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 <Form.Select
+               isInvalid={isInvalid}
+               name={name}
+               onBlur={onBlur}
+               onChange={onChange}
+               type="search"
+               value={value}
+       >
+               <option value="">{i18n.t('tournaments.discordNoCategory')}</option>
+               {results && results.length ? results.map(result =>
+                       <option key={result.id} value={result.channel_id}>{result.name}</option>
+               ) : null}
+       </Form.Select>;
+};
+
+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);
index f9eba93e516615f08437c302b3ea5070bb801a8b..d896504c60bab62d6c0617d68ccc8e34fcdf1c51 100644 (file)
@@ -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,
 }) =>
 <Form noValidate onSubmit={handleSubmit}>
        <fieldset>
                <legend>{i18n.t('tournaments.discordSettings')}</legend>
+               <Form.Group controlId="tournament.discord_round_category">
+                       <Form.Label>
+                               {i18n.t('tournaments.discordRoundCategory')}
+                       </Form.Label>
+                       <DiscordChannelSelect
+                               guild={tournament.discord}
+                               isInvalid={!!(touched.round_category && errors.round_category)}
+                               name="round_category"
+                               onBlur={handleBlur}
+                               onChange={handleChange}
+                               types={[4]}
+                               value={values.round_category || ''}
+                       />
+               </Form.Group>
                <Form.Group controlId="tournament.discord_round_template">
                        <Form.Label>
                                {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));
index 5a4889ca2d301ace748c2eb1026136c303e08fea..331346e69424bfe2ebb665a1d87099ce657180e4 100644 (file)
@@ -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',
index 9d4b6210f5b45d1ffe0ba411b01aa09ace75a4ea..de9b30379c0732ef064e3e197cc89504134b5d82 100644 (file)
@@ -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',
index fc7fd9ca7aea32833a478b68b52b745edd8fc907..39a75317017d7ff405d211364bd99a44790e5b6b 100644 (file)
@@ -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');