]> git.localhorst.tv Git - alttp.git/commitdiff
tournament admins
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Sun, 20 Mar 2022 20:14:26 +0000 (21:14 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Sun, 20 Mar 2022 20:14:26 +0000 (21:14 +0100)
app/Models/Participant.php
app/Models/User.php
app/Policies/TournamentPolicy.php
database/migrations/2022_03_20_191356_participant_roles.php [new file with mode: 0644]
resources/js/components/results/List.js
resources/js/components/tournament/Detail.js
resources/js/helpers/Participant.js
resources/js/helpers/Tournament.js
resources/js/helpers/permissions.js
resources/js/i18n/de.js
resources/js/i18n/en.js

index c2a8d8ab861525c1bdedfa7b75a1e420613e5d80..8aac88ce54a04877ecefe43c6f67950ec0d09c63 100644 (file)
@@ -17,6 +17,10 @@ class Participant extends Model
                return $this->belongsTo(User::class);
        }
 
+       protected $casts = [
+               'roles' => 'array',
+       ];
+
        protected $with = [
                'user',
        ];
index 535af2e07e828232ba2e9e2843ad507d1222d481..4f56ac17f93b8af4ef2aff0c8478ee913ee19fd1 100644 (file)
@@ -14,13 +14,31 @@ class User extends Authenticatable
 
        public function isParticipant(Tournament $tournament) {
                foreach ($tournament->participants as $participant) {
-                       if ($participant->user->id == $this->id) {
+                       if ($participant->user_id == $this->id) {
                                return true;
                        }
                }
                return false;
        }
 
+       public function isRunner(Tournament $tournament) {
+               foreach ($tournament->participants as $participant) {
+                       if ($participant->user_id == $this->id) {
+                               return in_array('runner', $participant->roles);
+                       }
+               }
+               return false;
+       }
+
+       public function isTournamentAdmin(Tournament $tournament) {
+               foreach ($tournament->participants as $participant) {
+                       if ($participant->user_id == $this->id) {
+                               return in_array('admin', $participant->roles);
+                       }
+               }
+               return false;
+       }
+
        public function participation() {
                return $this->hasMany(Participant::class);
        }
index 2cc6b6ed2932dd3c1eb60d4424cfe4b322a563d0..fa27298435246cd4f2f186ca4be4f886ce42c5a7 100644 (file)
@@ -113,7 +113,7 @@ class TournamentPolicy
         */
        public function viewProtocol(User $user, Tournament $tournament)
        {
-               return $user->role === 'admin';
+               return $user->role === 'admin' || $user->isTournamentAdmin($tournament);
        }
 
 }
diff --git a/database/migrations/2022_03_20_191356_participant_roles.php b/database/migrations/2022_03_20_191356_participant_roles.php
new file mode 100644 (file)
index 0000000..33867e8
--- /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('participants', function(Blueprint $table) {
+                       $table->text('roles')->default('["runner"]');
+               });
+       }
+
+       /**
+        * Reverse the migrations.
+        *
+        * @return void
+        */
+       public function down()
+       {
+               Schema::table('participants', function(Blueprint $table) {
+                       $table->dropColumn('roles');
+               });
+       }
+};
index 8775529493d6d9909ff1cf3fd9674b41ca62edb7..cdd90e847e91785b587ee8376f30912f67ba91f4 100644 (file)
@@ -3,9 +3,10 @@ import React from 'react';
 
 import Item from './Item';
 import { sortByResult } from '../../helpers/Participant';
+import { getRunners } from '../../helpers/Tournament';
 
 const List = ({ round, tournament }) => <div className="results d-flex flex-wrap">
-       {sortByResult(tournament.participants, round).map((participant, index) =>
+       {sortByResult(getRunners(tournament), round).map((participant, index) =>
                <Item
                        index={index}
                        key={participant.id}
index 3ce0b48998987405626aa82bf5de8244f63ab28f..c966ca5250685da8c6a582302dfe6d8bd84ccdb7 100644 (file)
@@ -6,10 +6,17 @@ import { withTranslation } from 'react-i18next';
 import Scoreboard from './Scoreboard';
 import Protocol from '../protocol/Protocol';
 import Rounds from '../rounds/List';
+import Box from '../users/Box';
 import {
        mayAddRounds,
        mayViewProtocol,
 } from '../../helpers/permissions';
+import {
+       getRunners,
+       getTournamentAdmins,
+       hasRunners,
+       hasTournamentAdmins,
+} from '../../helpers/Tournament';
 import { withUser } from '../../helpers/UserContext';
 import i18n from '../../i18n';
 
@@ -46,9 +53,19 @@ const Detail = ({
                        <div className="d-flex align-items-center justify-content-between">
                                <h2>{i18n.t('tournaments.scoreboard')}</h2>
                        </div>
-                       {tournament.participants ?
+                       {hasRunners(tournament) ?
                                <Scoreboard tournament={tournament} />
                        : null}
+                       {hasTournamentAdmins(tournament) ?
+                               <>
+                                       <div className="d-flex align-items-center justify-content-between">
+                                               <h2>{i18n.t('tournaments.admins')}</h2>
+                                       </div>
+                                       {getTournamentAdmins(tournament).map(p =>
+                                               <p key={p.id}><Box user={p.user} /></p>
+                                       )}
+                               </>
+                       : null}
                </Col>
        </Row>
 </Container>;
index eb943401f6074057b4e6fc625c0d7385c4d15042..d79349bca8776a65ae853943df671f8ffb8f114f 100644 (file)
@@ -40,6 +40,12 @@ export const findResult = (participant, round) => {
        return round.results.find(result => result.user_id === participant.user_id);
 };
 
+export const isRunner = participant =>
+       participant && participant.roles && participant.roles.includes('runner');
+
+export const isTournamentAdmin = participant =>
+       participant && participant.roles && participant.roles.includes('admin');
+
 export const patchUser = (participant, user) => {
        if (!participant || !user) return participant;
        if (participant.user_id != user.id) return participant;
@@ -62,6 +68,8 @@ export default {
        compareResult,
        compareUsername,
        findResult,
+       isRunner,
+       isTournamentAdmin,
        patchUser,
        sortByResult,
 };
index a3f97cc22b47a6e4a3f135a8bbb506ebf9134b0b..31414de957fffab192fd073b1f8cae067cdff306 100644 (file)
@@ -2,8 +2,7 @@ import Participant from './Participant';
 import Round from './Round';
 
 export const calculateScores = tournament => {
-       if (!tournament || !tournament.participants || !tournament.participants.length) return [];
-       const scores = tournament.participants.map(participant => ({ participant, score: 0 }));
+       const scores = getRunners(tournament).map(participant => ({ participant, score: 0 }));
        if (!tournament.rounds || !tournament.rounds.length) return scores;
        tournament.rounds.forEach(round => {
                const filtered = Participant
@@ -39,7 +38,7 @@ export const compareScore = (a, b) => {
        const b_score = b && b.score ? b.score : 0;
        if (a_score < b_score) return -1;
        if (b_score < a_score) return 1;
-       return 0;
+       return Participant.compareUsername(a.participant, b.participant);
 };
 
 export const findParticipant = (tournament, user) => {
@@ -48,6 +47,28 @@ export const findParticipant = (tournament, user) => {
        return tournament.participants.find(p => p.user_id == user.id);
 };
 
+export const getRunners = tournament => {
+       if (!tournament || !tournament.participants || !tournament.participants.length) return [];
+       return tournament.participants
+               .filter(Participant.isRunner)
+               .sort(Participant.compareUsername);
+};
+
+export const getTournamentAdmins = tournament => {
+       if (!tournament || !tournament.participants || !tournament.participants.length) return [];
+       return tournament.participants
+               .filter(Participant.isTournamentAdmin)
+               .sort(Participant.compareUsername);
+};
+
+export const hasRunners = tournament => {
+       return getRunners(tournament).length > 0;
+};
+
+export const hasTournamentAdmins = tournament => {
+       return getTournamentAdmins(tournament).length > 0;
+};
+
 export const patchResult = (tournament, result) => {
        if (!tournament || !tournament.rounds) return tournament;
        return {
index 23272d9ac14b0f561d53b1507d5fda79453d4d1d..21016f1a08f6786fecfdcbe775717ace91b8279b 100644 (file)
@@ -13,6 +13,16 @@ export const isParticipant = (user, tournament) =>
        user && tournament && tournament.participants &&
        tournament.participants.find(p => p.user && p.user.id == user.id);
 
+export const isRunner = (user, tournament) => {
+       const p = isParticipant(user, tournament);
+       return p && p.roles && p.roles.includes('runner');
+};
+
+export const isTournamentAdmin = (user, tournament) => {
+       const p = isParticipant(user, tournament);
+       return p && p.roles && p.roles.includes('admin');
+};
+
 export const hasFinished = (user, round) =>
        user && round && round.results &&
        round.results.find(r => r.user_id == user.id && r.has_finished);
@@ -23,8 +33,8 @@ export const mayAddRounds = (user, tournament) =>
 export const maySetSeed = (user, tournament) =>
        isAdmin(user) || isParticipant(user, tournament);
 
-export const mayViewProtocol = user =>
-       isAdmin(user);
+export const mayViewProtocol = (user, tournament) =>
+       isAdmin(user) || isTournamentAdmin(user, tournament);
 
 export const maySeeResults = (user, tournament, round) =>
        isAdmin(user) || hasFinished(user, round) || Round.isComplete(tournament, round);
index 75c0b02fb401b9d13f00484c614cbfb5d7edb8a9..3bfadc17c20663e82e345e9eaa55c1d554bd758d 100644 (file)
@@ -144,6 +144,7 @@ export default {
                        setSeedSuccess: 'Seed eingetragen',
                },
                tournaments: {
+                       admins: 'Organisation',
                        scoreboard: 'Scoreboard',
                },
                users: {
index d2ee9c09f5358c33925df987efb06a776b4754f7..ccd5e9553bc124e35256b0e50efe28c4b5dc5a27 100644 (file)
@@ -144,6 +144,7 @@ export default {
                        setSeedSuccess: 'Seed set',
                },
                tournaments: {
+                       admins: 'Admins',
                        scoreboard: 'Scoreboard',
                },
                users: {