$this->authorize('addRound', $tournament);
$tournament->loadMax('rounds', 'number');
+ $number = intval($tournament->rounds_max_number) + 1;
+
+ $rounds = [];
+
+ for ($i = 0; $i < $tournament->group_size; ++$i) {
+ $group = static::$GROUP_NAMES[$i];
+ $round = Round::create([
+ 'game' => $tournament->game,
+ 'group' => $group,
+ 'number' => $number,
+ 'no_record' => $tournament->no_record,
+ 'tournament_id' => $tournament->id,
+ ]);
+ Protocol::roundAdded(
+ $tournament,
+ $round,
+ $request->user(),
+ );
+ RoundAdded::dispatch($round);
+ $rounds[] = $round;
+ }
- $round = Round::create([
- 'game' => $tournament->game,
- 'number' => intval($tournament->rounds_max_number) + 1,
- 'no_record' => $tournament->no_record,
- 'tournament_id' => $validatedData['tournament_id'],
- ]);
-
- Protocol::roundAdded(
- $tournament,
- $round,
- $request->user(),
- );
-
- RoundAdded::dispatch($round);
-
- return $round->toJson();
+ return $rounds;
}
public function delete(Request $request, Round $round) {
return $round->toJson();
}
+ private static $GROUP_NAMES = [
+ 'A', 'B', 'C', 'D', 'E',
+ ];
+
}
'participants',
'participants.user',
]);
- $rounds = $tournament->rounds()->with(['results', 'results.user'])->limit(25)->get();
+ $rounds = $tournament->rounds()
+ ->with(['results', 'results.user'])
+ ->limit($tournament->ceilRoundLimit(25))
+ ->get();
foreach ($rounds as $round) {
if (!Gate::allows('seeResults', $round)) {
$round->hideResults($request->user());
$rounds = $tournament->rounds()
->where('number', '<', $validatedData['last_known'])
->with(['results', 'results.user'])
- ->limit(25)->get();
+ ->limit($tournament->ceilRoundLimit(25))->get();
foreach ($rounds as $round) {
if (!Gate::allows('seeResults', $round)) {
$round->hideResults($request->user());
protected static function roundMemo(Round $round) {
return [
'id' => $round->id,
+ 'group' => $round->group,
'locked' => $round->locked,
'no_record' => $round->no_record,
'number' => $round->number,
protected $fillable = [
'game',
+ 'group',
'number',
'no_record',
'tournament_id',
}
public function rounds() {
- return $this->hasMany(Round::class)->orderBy('number', 'DESC');
+ return $this->hasMany(Round::class)->orderBy('number', 'DESC')->orderBy('group', 'ASC');
}
public function getTranslatedTitle(): string {
return '';
}
+ public function ceilRoundLimit(int $limit): int {
+ if ($this->group_size > 1 && ($limit % $this->group_size)) {
+ return $limit + $this->group_size - ($limit % $this->group_size);
+ }
+ return $limit;
+ }
+
protected $casts = [
'accept_applications' => 'boolean',
--- /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->unsignedInteger('group_size')->default(1);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('tournaments', function (Blueprint $table) {
+ $table->dropColumn('group_size');
+ });
+ }
+
+};
--- /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('rounds', function (Blueprint $table) {
+ $table->string('group')->default('A');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('rounds', function (Blueprint $table) {
+ $table->dropColumn('group');
+ });
+ }
+
+};
import { useTranslation } from 'react-i18next';
import toastr from 'toastr';
-const EditDialog = ({
+import { formatNumber } from '../../helpers/Round';
+
+const DeleteDialog = ({
onHide,
round,
show,
+ tournament,
}) => {
const { t } = useTranslation();
{t('rounds.deleteConfirmMessage', {
...round,
date: new Date(round.created_at),
+ number: formatNumber(tournament, round),
})}
</Alert>
</Modal.Body>
</Modal>;
};
-EditDialog.propTypes = {
+DeleteDialog.propTypes = {
onHide: PropTypes.func,
round: PropTypes.shape({
created_at: PropTypes.string,
}),
};
-export default EditDialog;
+export default DeleteDialog;
mayViewProtocol,
isRunner,
} from '../../helpers/permissions';
-import { isComplete } from '../../helpers/Round';
+import { formatNumber, isComplete } from '../../helpers/Round';
import { hasFinishedRound } from '../../helpers/User';
import { useUser } from '../../hooks/user';
<div className="d-flex">
<div className="info">
<p className="date">
- {tournament.show_numbers && round.number ? `#${round.number} ` : ''}
+ {formatNumber(tournament, round)}
{t('rounds.date', { date: new Date(round.created_at) })}
</p>
<p className="seed">
import Participant from './Participant';
import Tournament from './Tournament';
+export const formatNumber = (tournament, round) => {
+ const group = (tournament?.group_size > 1 && round?.group) || '';
+ return tournament.show_numbers && round?.number ? `#${round.number}${group} ` : '';
+};
+
export const hasResults = (round) => {
return round && round.results && round.results.length > 0;
};
code: 'Code',
date: '{{ date, L }}',
delete: 'Runde löschen',
- deleteConfirmMessage: 'Runde #{{ number }} vom {{ date, L }} löschen?',
+ deleteConfirmMessage: 'Runde {{ number }} vom {{ date, L }} löschen?',
deleteError: 'Fehler beim Löschen',
deleteSuccess: 'Runde gelöscht',
edit: 'Runde bearbeiten',
code: 'Code',
date: '{{ date, L }}',
delete: 'Delete round',
- deleteConfirmMessage: 'Remove round #{{ number }} from {{ date, L }}?',
+ deleteConfirmMessage: 'Remove round {{ number }} from {{ date, L }}?',
deleteError: 'Error deleting round',
deleteSuccess: 'Round deleted',
edit: 'Edit round',