public function __construct(Round $round)
{
$this->round = $round;
+ $this->round->setRelations([]);
}
/**
public function settings(Request $request, Tournament $tournament) {
$this->authorize('update', $tournament);
$validatedData = $request->validate([
+ 'result_reveal' => 'string|nullable|in:always,finishers,never,participants',
'show_numbers' => 'boolean|nullable',
]);
if (array_key_exists('show_numbers', $validatedData)) {
$tournament->show_numbers = $validatedData['show_numbers'];
}
+ if (isset($validatedData['result_reveal'])) {
+ $tournament->result_reveal = $validatedData['result_reveal'];
+ }
$tournament->save();
if ($tournament->wasChanged()) {
TournamentChanged::dispatch($tournament);
public function isComplete() {
if (count($this->tournament->participants) == 0) return false;
+ if ($this->tournament->type == 'open-async') return false;
if (count($this->results) == 0) return false;
foreach ($this->tournament->getRunners() as $participant) {
$result = $participant->findResult($this);
namespace App\Models;
-use App\Events\ParticipantChanged;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
}
usort($runners, [Participant::class, 'compareScore']);
- $reversed = array_reverse($runners);
$placement = count($runners);
$skipped = 0;
$lastScore = $runners[0]->score;
*/
public function seeResults(?User $user, Round $round)
{
- return
+ if (
$round->locked ||
- ($user && $user->hasFinished($round)) ||
($user && $user->isTournamentMonitor($round->tournament)) ||
- $round->isComplete();
+ $round->isComplete()
+ ) {
+ return true;
+ }
+ if ($round->tournament->result_reveal == 'finishers') {
+ return ($user && $user->hasFinished($round));
+ }
+ if ($round->tournament->result_reveal == 'participants') {
+ return ($user && $user->isRunner($round->tournament));
+ }
+ return false;
}
/**
--- /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.
+ */
+ public function up(): void
+ {
+ Schema::table('tournaments', function(Blueprint $table) {
+ $table->string('result_reveal')->default('finishers');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('tournaments', function(Blueprint $table) {
+ $table->dropColumn('result_reveal');
+ });
+ }
+};
import axios from 'axios';
import PropTypes from 'prop-types';
import React from 'react';
-import { Button, Col, Modal, Row } from 'react-bootstrap';
+import { Button, Col, Form, Modal, Row } from 'react-bootstrap';
import { withTranslation } from 'react-i18next';
import toastr from 'toastr';
value={tournament.show_numbers}
/>
</div>
+ <div className="d-flex align-items-center justify-content-between mb-3">
+ <span title={i18n.t('tournaments.resultRevealDescription')}>
+ {i18n.t('tournaments.resultReveal')}
+ </span>
+ <Form.Select
+ onChange={({ target: { value } }) =>
+ settings(tournament, { result_reveal: value })}
+ style={{ width: '50%' }}
+ value={tournament.result_reveal}
+ >
+ {['never', 'finishers', 'participants', 'always'].map((key) =>
+ <option
+ key={key}
+ title={i18n.t(`tournaments.resultRevealOptionDescription.${key}`)}
+ value={key}
+ >
+ {i18n.t(`tournaments.resultRevealOption.${key}`)}
+ </option>
+ )}
+ </Form.Select>
+ </div>
<div className="d-flex align-items-center justify-content-between">
<div>
<p>{i18n.t('tournaments.discord')}</p>
accept_applications: PropTypes.bool,
discord: PropTypes.string,
locked: PropTypes.bool,
+ result_reveal: PropTypes.string,
show_numbers: PropTypes.bool,
}),
};
export const isComplete = (tournament, round) => {
if (!tournament || !tournament.participants) return false;
+ if (tournament.type === 'open-async') return false;
if (!round || !round.results) return false;
const runners = Tournament.getRunners(tournament);
if (!runners.length) return false;
return p && p.roles && p.roles.includes('admin');
};
-export const isTournamentCrew = (user, tournament) =>
- isTournamentAdmin(user, tournament) || isTournamentMonitor(user, tournament);
-
export const isTournamentMonitor = (user, tournament) => {
const p = isParticipant(user, tournament);
return p && p.roles && p.roles.includes('monitor');
};
+export const isTournamentCrew = (user, tournament) =>
+ isTournamentAdmin(user, tournament) || isTournamentMonitor(user, tournament);
+
export const hasFinished = (user, round) =>
user && round && round.results &&
round.results.find(r => r.user_id == user.id && r.has_finished);
export const mayViewProtocol = (user, tournament) =>
isTournamentCrew(user, tournament);
-export const maySeeResults = (user, tournament, round) =>
- round.locked ||
- hasFinished(user, round) ||
- isTournamentMonitor(user, tournament) ||
- Round.isComplete(tournament, round);
+export const maySeeResults = (user, tournament, round) => {
+ if (
+ round.locked ||
+ isTournamentMonitor(user, tournament) ||
+ Round.isComplete(tournament, round)
+ ) {
+ return true;
+ }
+ if (tournament.result_reveal === 'finishers') {
+ return hasFinished(user, round);
+ }
+ if (tournament.result_reveal === 'participants') {
+ return isRunner(user, tournament);
+ }
+ return false;
+};
// Twitch
open: 'Anmeldung geöffnet',
openError: 'Fehler beim Öffnen der Anmledung',
openSuccess: 'Anmeldung geöffnet',
+ resultReveal: 'Frühzeitige Ergebnisse',
+ resultRevealDescription: 'Ob und wann Ergebnisse von offenen Runden angezeigt werden sollen.',
+ resultRevealOption: {
+ always: 'Immer',
+ finishers: 'Absolventen',
+ never: 'Nie',
+ participants: 'Teilnehmern',
+ },
+ resultRevealOptionDescription: {
+ always: 'Ergebnis wird allen angezeigt, sobald es eingetragen wird.',
+ finishers: 'Ergebnis wird allen Teilnehmern angezeigt, die eine Zeit (oder DNF) für die Runde eingetragen haben.',
+ never: 'Ergebnis wird erst mit Abschluss der Runde veröffentlicht.',
+ participants: 'Ergebnis wird allen registrierten Teilnehmern des Turniers angezeigt.',
+ },
scoreboard: 'Scoreboard',
scoreChart: 'Turnierverlauf',
settings: 'Einstellungen',
open: 'Open registration',
openError: 'Error opening registration',
openSuccess: 'Registration opened',
+ resultReveal: 'Early results',
+ resultRevealDescription: 'When to show results for open rounds.',
+ resultRevealOption: {
+ always: 'Always',
+ finishers: 'Finishers',
+ never: 'Never',
+ participants: 'Participants',
+ },
+ resultRevealOptionDescription: {
+ always: 'Show all results as they come in.',
+ finishers: 'Show results to participants who submitted a time (or DNF).',
+ never: 'Only reveal results once the round has closed down.',
+ participants: 'Show results to only registered tournament members.',
+ },
scoreboard: 'Scoreboard',
scoreChart: 'Score chart',
settings: 'Settings',