finished tournaments
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
<template>
|
||||
<div class="tournaments-view">
|
||||
<h2>Turnier</h2>
|
||||
|
||||
<!-- Datumsauswahl / Neues Turnier -->
|
||||
<div class="tournament-config">
|
||||
<h3>Datum</h3>
|
||||
<select v-model="selectedDate">
|
||||
@@ -20,20 +18,31 @@
|
||||
<button @click="createTournament">Erstellen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Konfiguration & Gruppenphase -->
|
||||
<div v-if="selectedDate !== 'new'" class="tournament-setup">
|
||||
<label>
|
||||
<input type="checkbox" v-model="isGroupTournament" />
|
||||
<input type="checkbox" v-model="isGroupTournament" @change="onModusChange" />
|
||||
Spielen in Gruppen
|
||||
</label>
|
||||
|
||||
<section class="participants">
|
||||
<h4>Teilnehmer</h4>
|
||||
<ul>
|
||||
<li v-for="participant in participants" :key="participant.id">
|
||||
{{ participant.member.firstName }}
|
||||
{{ participant.member.lastName }}
|
||||
<template v-if="isGroupTournament">
|
||||
<label class="inline-label">
|
||||
Gruppe:
|
||||
<select v-model.number="participant.groupNumber">
|
||||
<option :value="null">–</option>
|
||||
<option v-for="group in groups" :key="group.groupId" :value="group.groupNumber">
|
||||
Gruppe {{ group.groupNumber }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
</template>
|
||||
<button @click="removeParticipant(participant)" style="margin-left:0.5rem">
|
||||
Entfernen
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<select v-model="selectedMember">
|
||||
@@ -44,20 +53,28 @@
|
||||
</select>
|
||||
<button @click="addParticipant">Hinzufügen</button>
|
||||
</section>
|
||||
|
||||
<section v-if="isGroupTournament && participants.length > 1" class="group-controls">
|
||||
<section v-if="isGroupTournament" class="group-controls">
|
||||
<label>
|
||||
Anzahl Gruppen:
|
||||
<input type="number" v-model.number="numberOfGroups" min="1" />
|
||||
<input type="number" v-model.number="numberOfGroups" min="1" @change="onGroupCountChange" />
|
||||
</label>
|
||||
<label style="margin-left:1em">
|
||||
Aufsteiger pro Gruppe:
|
||||
<input type="number" v-model.number="advancingPerGroup" min="1" @change="onModusChange" />
|
||||
</label>
|
||||
|
||||
<label style="margin-left:1em">
|
||||
Maximale Gruppengröße:
|
||||
<input type="number" v-model.number="maxGroupSize" min="1" />
|
||||
</label>
|
||||
|
||||
<button @click="createGroups">Gruppen erstellen</button>
|
||||
<button @click="randomizeGroups">Zufällig verteilen</button>
|
||||
</section>
|
||||
|
||||
<section v-if="groups.length" class="groups-overview">
|
||||
<h3>Gruppenübersicht</h3>
|
||||
<div v-for="group in groups" :key="group.groupId" class="group-table">
|
||||
<h4>Gruppe {{ group.groupId }}</h4>
|
||||
<h4>Gruppe {{ group.groupNumber }}</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -81,17 +98,102 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="reset-controls" style="margin-top:1rem">
|
||||
<button @click="resetGroups">
|
||||
Gruppen zurücksetzen
|
||||
</button>
|
||||
<button @click="resetMatches" style="margin-left:0.5rem">
|
||||
Gruppenspiele löschen
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<section v-if="groupMatches.length" class="group-matches">
|
||||
<h4>Gruppenspiele</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Runde</th>
|
||||
<th>Gruppe</th>
|
||||
<th>Begegnung</th>
|
||||
<th>Ergebnis</th>
|
||||
<th>Sätze</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="m in groupMatches" :key="m.id">
|
||||
<td>{{ m.groupRound }}</td>
|
||||
<td>{{ m.groupNumber }}</td>
|
||||
<td>
|
||||
<template v-if="m.isFinished">
|
||||
<span v-if="winnerIsPlayer1(m)">
|
||||
<strong>{{ getPlayerName(m.player1) }}</strong> – {{ getPlayerName(m.player2) }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ getPlayerName(m.player1) }} – <strong>{{ getPlayerName(m.player2) }}</strong>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ getPlayerName(m.player1) }} – {{ getPlayerName(m.player2) }}
|
||||
</template>
|
||||
</td>
|
||||
|
||||
<!-- K.o.-Runde starten -->
|
||||
<div v-if="participants.length > 1 && !showKnockout" class="ko-start">
|
||||
<td>
|
||||
<!-- 1. Fall: Match ist noch offen → Edit‑Mode -->
|
||||
<template v-if="!m.isFinished">
|
||||
<!-- existierende Sätze als klickbare Labels -->
|
||||
<template v-for="r in m.tournamentResults" :key="r.set">
|
||||
<span @click="startEditResult(m, r)" class="result-text">
|
||||
{{ r.pointsPlayer1 }}:{{ r.pointsPlayer2 }}
|
||||
</span>
|
||||
<span v-if="!isLastResult(m, r)">, </span>
|
||||
</template>
|
||||
|
||||
<!-- Eingabefeld für neue Sätze (immer sichtbar solange offen) -->
|
||||
<div class="new-set-line">
|
||||
<input v-model="m.resultInput" placeholder="Neuen Satz, z.B. 11:7"
|
||||
@keyup.enter="saveMatchResult(m, m.resultInput)"
|
||||
@blur="saveMatchResult(m, m.resultInput)" class="inline-input" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 2. Fall: Match ist abgeschlossen → Read‑only -->
|
||||
<template v-else>
|
||||
{{ formatResult(m) }}
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
{{ getSetsString(m) }}
|
||||
</td>
|
||||
<td>
|
||||
<!-- „Abschließen“-Button nur, wenn noch nicht fertig -->
|
||||
<button v-if="!m.isFinished" @click="finishMatch(m)">Abschließen</button>
|
||||
<!-- „Korrigieren“-Button nur, wenn fertig -->
|
||||
<button v-else @click="reopenMatch(m)">Korrigieren</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<div v-if="participants.length > 1
|
||||
&& !groupMatches.length
|
||||
&& !knockoutMatches.length" class="start-matches" style="margin-top:1.5rem">
|
||||
<button @click="startMatches">
|
||||
Spiele erstellen
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="canStartKnockout && !showKnockout" class="ko-start">
|
||||
<button @click="startKnockout">
|
||||
K.o.-Runde starten
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- K.o.-Runde anzeigen -->
|
||||
<div v-if="showKnockout && canResetKnockout" class="ko-reset" style="margin-top:1rem">
|
||||
<button @click="resetKnockout">
|
||||
K.o.-Runde löschen
|
||||
</button>
|
||||
</div>
|
||||
<section v-if="showKnockout" class="ko-round">
|
||||
<h4>K.-o.-Runde</h4>
|
||||
<table>
|
||||
@@ -100,6 +202,7 @@
|
||||
<th>Runde</th>
|
||||
<th>Begegnung</th>
|
||||
<th>Ergebnis</th>
|
||||
<th>Sätze</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -107,16 +210,68 @@
|
||||
<tr v-for="m in knockoutMatches" :key="m.id">
|
||||
<td>{{ m.round }}</td>
|
||||
<td>
|
||||
{{ getPlayerName(m.player1) }} –
|
||||
{{ getPlayerName(m.player2) }}
|
||||
<template v-if="m.isFinished">
|
||||
<span v-if="winnerIsPlayer1(m)">
|
||||
<strong>{{ getPlayerName(m.player1) }}</strong> – {{ getPlayerName(m.player2) }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ getPlayerName(m.player1) }} – <strong>{{ getPlayerName(m.player2) }}</strong>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ getPlayerName(m.player1) }} – {{ getPlayerName(m.player2) }}
|
||||
</template>
|
||||
</td>
|
||||
<td>{{ m.result || '-' }}</td>
|
||||
<td v-if="!m.isFinished">
|
||||
<input v-model="m.resultInput" placeholder="z.B. 11:4, 4:11, 4, -4"
|
||||
@keyup.enter="saveMatchResult(m, m.resultInput)" />
|
||||
<button @click="finishMatch(m)">
|
||||
Fertig
|
||||
</button>
|
||||
<td>
|
||||
<!-- 1. Fall: Match ist noch offen → Edit‑Mode -->
|
||||
<template v-if="!m.isFinished">
|
||||
<!-- existierende Sätze als klickbare Labels -->
|
||||
<template v-for="r in m.tournamentResults" :key="r.set">
|
||||
<span @click="startEditResult(m, r)" class="result-text">
|
||||
{{ r.pointsPlayer1 }}:{{ r.pointsPlayer2 }}
|
||||
</span>
|
||||
<span v-if="!isLastResult(m, r)">, </span>
|
||||
</template>
|
||||
|
||||
<!-- Eingabefeld für neue Sätze (immer sichtbar solange offen) -->
|
||||
<div class="new-set-line">
|
||||
<input v-model="m.resultInput" placeholder="Neuen Satz, z.B. 11:7"
|
||||
@keyup.enter="saveMatchResult(m, m.resultInput)"
|
||||
@blur="saveMatchResult(m, m.resultInput)" class="inline-input" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 2. Fall: Match ist abgeschlossen → Read‑only -->
|
||||
<template v-else>
|
||||
{{ formatResult(m) }}
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
{{ getSetsString(m) }}
|
||||
</td>
|
||||
<td>
|
||||
<button v-if="!m.isFinished" @click="finishMatch(m)">Fertig</button>
|
||||
<button v-else @click="reopenMatch(m)">Korrigieren</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section v-if="rankingList.length" class="ranking">
|
||||
<h4>Rangliste</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Platz</th>
|
||||
<th>Spieler</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(entry, idx) in rankingList" :key="`${entry.member.id}-${idx}`">
|
||||
<td>{{ entry.position }}.</td>
|
||||
<td>
|
||||
{{ entry.member.firstName }}
|
||||
{{ entry.member.lastName }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -136,24 +291,43 @@ export default {
|
||||
selectedDate: 'new',
|
||||
newDate: '',
|
||||
dates: [],
|
||||
|
||||
participants: [],
|
||||
selectedMember: null,
|
||||
clubMembers: [],
|
||||
|
||||
advancingPerGroup: 1,
|
||||
numberOfGroups: 1,
|
||||
maxGroupSize: null,
|
||||
isGroupTournament: false,
|
||||
groups: [],
|
||||
|
||||
matches: [],
|
||||
showKnockout: false,
|
||||
editingResult: {
|
||||
matchId: null, // aktuell bearbeitetes Match
|
||||
set: null, // aktuell bearbeitete Satz‑Nummer
|
||||
value: '' // Eingabewert
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isAuthenticated', 'currentClub']),
|
||||
|
||||
knockoutMatches() {
|
||||
return this.matches.filter(m => m.round !== 'group');
|
||||
},
|
||||
|
||||
groupMatches() {
|
||||
return this.matches
|
||||
.filter(m => m.round === 'group')
|
||||
.sort((a, b) => {
|
||||
// zuerst nach Runde
|
||||
if (a.groupRound !== b.groupRound) {
|
||||
return a.groupRound - b.groupRound;
|
||||
}
|
||||
// dann nach Gruppe
|
||||
return a.groupNumber - b.groupNumber;
|
||||
});
|
||||
},
|
||||
|
||||
groupRankings() {
|
||||
const byGroup = {};
|
||||
this.groups.forEach(g => {
|
||||
@@ -193,7 +367,52 @@ export default {
|
||||
}));
|
||||
});
|
||||
return rankings;
|
||||
}
|
||||
},
|
||||
|
||||
rankingList() {
|
||||
const finalMatch = this.knockoutMatches.find(
|
||||
m => m.round.toLowerCase() === 'finale'
|
||||
);
|
||||
if (!finalMatch || !finalMatch.isFinished) return [];
|
||||
const list = [];
|
||||
const [s1, s2] = finalMatch.result.split(':').map(n => +n);
|
||||
const winner = s1 > s2 ? finalMatch.player1 : finalMatch.player2;
|
||||
const loser = s1 > s2 ? finalMatch.player2 : finalMatch.player1;
|
||||
list.push({ position: 1, member: winner.member });
|
||||
list.push({ position: 2, member: loser.member });
|
||||
const roundsMap = {};
|
||||
this.knockoutMatches.forEach(m => {
|
||||
if (m.round.toLowerCase() === 'finale') return;
|
||||
(roundsMap[m.round] ||= []).push(m);
|
||||
});
|
||||
Object.values(roundsMap).forEach(matches => {
|
||||
const M = matches.length;
|
||||
const pos = M + 1;
|
||||
matches.forEach(match => {
|
||||
const [a, b] = match.result.split(':').map(n => +n);
|
||||
const knockedOut = a > b ? match.player2 : match.player1;
|
||||
list.push({ position: pos, member: knockedOut.member });
|
||||
});
|
||||
});
|
||||
return list.sort((a, b) => a.position - b.position);
|
||||
},
|
||||
|
||||
canStartKnockout() {
|
||||
if (this.participants.length < 2) return false;
|
||||
if (!this.isGroupTournament) {
|
||||
// kein Gruppenmodus → immer starten
|
||||
return true;
|
||||
}
|
||||
// Gruppenmodus → nur, wenn es Gruppenspiele gibt und alle beendet sind
|
||||
return this.groupMatches.length > 0
|
||||
&& this.groupMatches.every(m => m.isFinished);
|
||||
},
|
||||
|
||||
canResetKnockout() {
|
||||
// KO‑Matches existieren und keiner ist beendet
|
||||
return this.knockoutMatches.length > 0
|
||||
&& this.knockoutMatches.every(m => !m.isFinished);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedDate: {
|
||||
@@ -209,7 +428,6 @@ export default {
|
||||
this.$router.push('/login');
|
||||
return;
|
||||
}
|
||||
// Turniere und Mitglieder laden
|
||||
const d = await apiClient.get(`/tournament/${this.currentClub}`);
|
||||
this.dates = d.data;
|
||||
const m = await apiClient.get(
|
||||
@@ -218,23 +436,51 @@ export default {
|
||||
this.clubMembers = m.data;
|
||||
},
|
||||
methods: {
|
||||
normalizeResultInput(raw) {
|
||||
const s = raw.trim();
|
||||
if (s.includes(':')) {
|
||||
const [xRaw, yRaw] = s.split(':');
|
||||
const x = Number(xRaw), y = Number(yRaw);
|
||||
if (
|
||||
Number.isInteger(x) && Number.isInteger(y) &&
|
||||
(x >= 11 || y >= 11) &&
|
||||
Math.abs(x - y) >= 2
|
||||
) {
|
||||
return `${x}:${y}`;
|
||||
}
|
||||
console.warn('Ungültiges Satz-Ergebnis:', s);
|
||||
return null;
|
||||
}
|
||||
|
||||
const num = Number(s);
|
||||
if (isNaN(num)) {
|
||||
console.warn('Ungültiges Ergebnisformat:', raw);
|
||||
return null;
|
||||
}
|
||||
|
||||
const losing = Math.abs(num);
|
||||
const winning = losing < 10 ? 11 : losing + 2;
|
||||
|
||||
if (num >= 0) {
|
||||
return `${winning}:${losing}`;
|
||||
} else {
|
||||
return `${losing}:${winning}`;
|
||||
}
|
||||
},
|
||||
|
||||
async loadTournamentData() {
|
||||
// 1) Turnier‐Metadaten holen (Typ + Anzahl Gruppen)
|
||||
const tRes = await apiClient.get(
|
||||
`/tournament/${this.currentClub}/${this.selectedDate}`
|
||||
);
|
||||
const tournament = tRes.data;
|
||||
this.isGroupTournament = tournament.type === 'groups';
|
||||
this.numberOfGroups = tournament.numberOfGroups;
|
||||
|
||||
// 2) Teilnehmer
|
||||
this.advancingPerGroup = tournament.advancingPerGroup;
|
||||
const pRes = await apiClient.post('/tournament/participants', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate
|
||||
});
|
||||
this.participants = pRes.data;
|
||||
|
||||
// 3) Gruppen (mit Teilnehmern)
|
||||
const gRes = await apiClient.get('/tournament/groups', {
|
||||
params: {
|
||||
clubId: this.currentClub,
|
||||
@@ -242,14 +488,18 @@ export default {
|
||||
}
|
||||
});
|
||||
this.groups = gRes.data;
|
||||
|
||||
// 4) Alle Matches
|
||||
const mRes = await apiClient.get(
|
||||
`/tournament/matches/${this.currentClub}/${this.selectedDate}`
|
||||
);
|
||||
this.matches = mRes.data;
|
||||
|
||||
// 5) Steuere K.o.-Anzeige
|
||||
const grpMap = this.groups.reduce((m, g) => {
|
||||
m[g.groupId] = g.groupNumber;
|
||||
return m;
|
||||
}, {});
|
||||
this.matches = mRes.data.map(m => ({
|
||||
...m,
|
||||
groupNumber: grpMap[m.groupId] || 0,
|
||||
resultInput: ''
|
||||
}));
|
||||
this.showKnockout = this.matches.some(m => m.round !== 'group');
|
||||
},
|
||||
|
||||
@@ -269,19 +519,45 @@ export default {
|
||||
},
|
||||
|
||||
async addParticipant() {
|
||||
const oldMap = this.participants.reduce((map, p) => {
|
||||
map[p.id] = p.groupNumber
|
||||
return map
|
||||
}, {})
|
||||
const r = await apiClient.post('/tournament/participant', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate,
|
||||
participant: this.selectedMember
|
||||
});
|
||||
this.participants = r.data;
|
||||
})
|
||||
this.participants = r.data.map(p => ({
|
||||
...p,
|
||||
groupNumber:
|
||||
oldMap[p.id] != null
|
||||
? oldMap[p.id]
|
||||
: (p.groupId || null)
|
||||
}))
|
||||
this.selectedMember = null
|
||||
},
|
||||
|
||||
async createGroups() {
|
||||
await apiClient.put('/tournament/groups', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate
|
||||
});
|
||||
const assignments = this.participants.map(p => ({
|
||||
participantId: p.id,
|
||||
groupNumber: p.groupNumber
|
||||
}));
|
||||
const manual = assignments.some(a => a.groupNumber != null);
|
||||
if (manual) {
|
||||
await apiClient.post('/tournament/groups/manual', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate,
|
||||
assignments,
|
||||
numberOfGroups: this.numberOfGroups,
|
||||
maxGroupSize: this.maxGroupSize
|
||||
});
|
||||
} else {
|
||||
await apiClient.put('/tournament/groups', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate
|
||||
});
|
||||
}
|
||||
await this.loadTournamentData();
|
||||
},
|
||||
|
||||
@@ -300,12 +576,12 @@ export default {
|
||||
},
|
||||
|
||||
async saveMatchResult(match, result) {
|
||||
// wenn kein ':' dabei, ergänzen
|
||||
if (result.indexOf(':') === -1) {
|
||||
result = result.indexOf('-') > -1
|
||||
? '11:' + result
|
||||
: (result * -1) + ':11';
|
||||
if (!result || result.trim().length === 0) {
|
||||
return;
|
||||
}
|
||||
const normalized = this.normalizeResultInput(result);
|
||||
if (!normalized) return;
|
||||
result = normalized;
|
||||
await apiClient.post('/tournament/match/result', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate,
|
||||
@@ -313,7 +589,24 @@ export default {
|
||||
set: (match.tournamentResults?.length || 0) + 1,
|
||||
result
|
||||
});
|
||||
await this.loadTournamentData();
|
||||
const allRes = await apiClient.get(
|
||||
`/tournament/matches/${this.currentClub}/${this.selectedDate}`
|
||||
);
|
||||
const updated = allRes.data.find(m2 => m2.id === match.id);
|
||||
if (!updated) {
|
||||
console.error('Konnte aktualisiertes Match nicht finden');
|
||||
return;
|
||||
}
|
||||
match.tournamentResults = updated.tournamentResults || [];
|
||||
const resultString = match.tournamentResults.length
|
||||
? match.tournamentResults
|
||||
.sort((a, b) => a.set - b.set)
|
||||
.map(r => `${Math.abs(r.pointsPlayer1)}:${Math.abs(r.pointsPlayer2)}`)
|
||||
.join(', ')
|
||||
: null;
|
||||
|
||||
match.result = resultString;
|
||||
match.resultInput = '';
|
||||
},
|
||||
|
||||
async finishMatch(match) {
|
||||
@@ -331,6 +624,170 @@ export default {
|
||||
tournamentId: this.selectedDate
|
||||
});
|
||||
await this.loadTournamentData();
|
||||
},
|
||||
|
||||
formatResult(match) {
|
||||
if (!match.tournamentResults?.length) return '-';
|
||||
return match.tournamentResults
|
||||
.sort((a, b) => a.set - b.set)
|
||||
.map(r => `${Math.abs(r.pointsPlayer1)}:${Math.abs(r.pointsPlayer2)}`)
|
||||
.join(', ');
|
||||
},
|
||||
|
||||
async startMatches() {
|
||||
if (this.isGroupTournament) {
|
||||
if (!this.groups.length) {
|
||||
await this.createGroups();
|
||||
}
|
||||
await this.randomizeGroups();
|
||||
} else {
|
||||
await this.startKnockout();
|
||||
}
|
||||
await this.loadTournamentData();
|
||||
},
|
||||
|
||||
async onModusChange() {
|
||||
const type = this.isGroupTournament ? 'groups' : 'knockout';
|
||||
await apiClient.post('/tournament/modus', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate,
|
||||
type,
|
||||
numberOfGroups: this.numberOfGroups,
|
||||
advancingPerGroup: this.advancingPerGroup
|
||||
});
|
||||
await this.loadTournamentData();
|
||||
},
|
||||
|
||||
async resetGroups() {
|
||||
await apiClient.post('/tournament/groups/reset', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate
|
||||
});
|
||||
await this.loadTournamentData();
|
||||
},
|
||||
|
||||
async resetMatches() {
|
||||
await apiClient.post('/tournament/matches/reset', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate
|
||||
});
|
||||
await this.loadTournamentData();
|
||||
},
|
||||
|
||||
async removeParticipant(p) {
|
||||
await apiClient.delete('/tournament/participant', {
|
||||
data: {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate,
|
||||
participantId: p.id
|
||||
}
|
||||
});
|
||||
this.participants = this.participants.filter(x => x.id !== p.id);
|
||||
},
|
||||
|
||||
async onGroupCountChange() {
|
||||
await apiClient.post('/tournament/modus', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate,
|
||||
type: this.isGroupTournament ? 'groups' : 'knockout',
|
||||
numberOfGroups: this.numberOfGroups
|
||||
});
|
||||
await this.loadTournamentData();
|
||||
},
|
||||
|
||||
async reopenMatch(match) {
|
||||
await apiClient.post('/tournament/match/reopen', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate,
|
||||
matchId: match.id
|
||||
});
|
||||
await this.loadTournamentData();
|
||||
},
|
||||
|
||||
async deleteResult(match, set) {
|
||||
if (match.isFinished) {
|
||||
await this.reopenMatch(match);
|
||||
match.isFinished = false;
|
||||
}
|
||||
await apiClient.delete('/tournament/match/result', {
|
||||
data: {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate,
|
||||
matchId: match.id,
|
||||
set
|
||||
}
|
||||
});
|
||||
await this.loadTournamentData();
|
||||
},
|
||||
|
||||
startEditResult(match, result) {
|
||||
if (match.isFinished) {
|
||||
this.reopenMatch(match);
|
||||
match.isFinished = false;
|
||||
}
|
||||
this.editingResult.matchId = match.id;
|
||||
this.editingResult.set = result.set;
|
||||
this.editingResult.value = `${result.pointsPlayer1}:${result.pointsPlayer2}`;
|
||||
},
|
||||
|
||||
async saveEditedResult(match) {
|
||||
const { set, value } = this.editingResult;
|
||||
const normalized = this.normalizeResultInput(value);
|
||||
if (!normalized) return;
|
||||
let result = normalized;
|
||||
await apiClient.post('/tournament/match/result', {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate,
|
||||
matchId: match.id,
|
||||
set,
|
||||
result
|
||||
});
|
||||
this.editingResult.matchId = null;
|
||||
this.editingResult.set = null;
|
||||
this.editingResult.value = '';
|
||||
await this.loadTournamentData();
|
||||
},
|
||||
|
||||
isEditing(match, set) {
|
||||
return (
|
||||
this.editingResult.matchId === match.id &&
|
||||
this.editingResult.set === set
|
||||
);
|
||||
},
|
||||
|
||||
isLastResult(match, result) {
|
||||
const arr = match.tournamentResults || [];
|
||||
return arr.length > 0 && arr[arr.length - 1].set === result.set;
|
||||
},
|
||||
|
||||
getSetsString(match) {
|
||||
const results = match.tournamentResults || [];
|
||||
let win1 = 0, win2 = 0;
|
||||
for (const r of results) {
|
||||
if (r.pointsPlayer1 > r.pointsPlayer2) win1++;
|
||||
else if (r.pointsPlayer2 > r.pointsPlayer1) win2++;
|
||||
}
|
||||
return `${win1}:${win2}`;
|
||||
},
|
||||
|
||||
winnerIsPlayer1(match) {
|
||||
const [w1, w2] = this.getSetsString(match).split(':').map(Number);
|
||||
return w1 > w2;
|
||||
},
|
||||
|
||||
async resetKnockout() {
|
||||
try {
|
||||
await apiClient.delete('/tournament/matches/knockout', {
|
||||
data: {
|
||||
clubId: this.currentClub,
|
||||
tournamentId: this.selectedDate
|
||||
}
|
||||
});
|
||||
await this.loadTournamentData();
|
||||
} catch (err) {
|
||||
console.error('Reset KO failed:', err);
|
||||
alert(err.response?.data?.error || err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user