| {{ m.groupRound }} |
{{ m.groupNumber }} |
@@ -301,6 +324,7 @@ export default {
groups: [],
matches: [],
showKnockout: false,
+ showParticipants: false, // Kollaps-Status für Teilnehmerliste
editingResult: {
matchId: null, // aktuell bearbeitetes Match
set: null, // aktuell bearbeitete Satz‑Nummer
@@ -369,6 +393,15 @@ export default {
return rankings;
},
+ // Mapping von groupId zu groupNumber für die Teilnehmer-Auswahl
+ groupIdToNumberMap() {
+ const map = {};
+ this.groups.forEach(g => {
+ map[g.groupId] = g.groupNumber;
+ });
+ return map;
+ },
+
rankingList() {
const finalMatch = this.knockoutMatches.find(
m => m.round.toLowerCase() === 'finale'
@@ -435,6 +468,20 @@ export default {
);
this.clubMembers = m.data;
},
+ mounted() {
+ // Event-Listener für das Entfernen des Highlights
+ document.addEventListener('click', (e) => {
+ // Entferne Highlight nur wenn nicht auf eine Matrix-Zelle geklickt wird
+ if (!e.target.closest('.match-cell')) {
+ this.clearHighlight();
+ }
+ });
+
+ // Event-Listener für Eingabefelder
+ document.addEventListener('input', () => {
+ this.clearHighlight();
+ });
+ },
methods: {
normalizeResultInput(raw) {
const s = raw.trim();
@@ -493,11 +540,27 @@ export default {
m[g.groupId] = g.groupNumber;
return m;
}, {});
- this.matches = mRes.data.map(m => ({
- ...m,
- groupNumber: grpMap[m.groupId] || 0,
- resultInput: ''
- }));
+
+ this.matches = mRes.data.map(m => {
+ // Bestimme groupId basierend auf den Spielern, da die Matches groupId: null haben
+ const player1GroupId = m.player1?.groupId;
+ const player2GroupId = m.player2?.groupId;
+ const matchGroupId = player1GroupId || player2GroupId;
+
+ return {
+ ...m,
+ groupId: matchGroupId, // Überschreibe null mit der korrekten groupId
+ groupNumber: grpMap[matchGroupId] || 0,
+ resultInput: ''
+ };
+ });
+
+ // Initialisiere groupNumber für Teilnehmer basierend auf groupId
+ this.initializeParticipantGroupNumbers();
+
+ // Setze Kollaps-Status: ausgeklappt wenn keine Spiele, eingeklappt wenn Spiele vorhanden
+ this.showParticipants = this.matches.length === 0;
+
this.showKnockout = this.matches.some(m => m.round !== 'group');
},
@@ -785,6 +848,207 @@ export default {
} catch (err) {
alert('Fehler beim Zurücksetzen der K.o.-Runde');
}
+ },
+
+ getMatchResult(player1Id, player2Id, groupId) {
+ const match = this.matches.find(m =>
+ m.round === 'group' &&
+ m.groupId === groupId &&
+ ((m.player1.id === player1Id && m.player2.id === player2Id) ||
+ (m.player1.id === player2Id && m.player2.id === player1Id)) &&
+ m.isFinished
+ );
+
+ if (!match) return null;
+
+ // Bestimme, wer gewonnen hat
+ const [sets1, sets2] = match.result.split(':').map(n => +n);
+ const player1Won = sets1 > sets2;
+
+ // Gib das Ergebnis in der Sicht des ersten Spielers zurück
+ if (match.player1.id === player1Id) {
+ return player1Won ? 'W' : 'L';
+ } else {
+ return player1Won ? 'L' : 'W';
+ }
+ },
+
+ getMatchLiveResult(player1Id, player2Id, groupId) {
+ const match = this.matches.find(m =>
+ m.round === 'group' &&
+ m.groupId === groupId &&
+ ((m.player1.id === player1Id && m.player2.id === player2Id) ||
+ (m.player1.id === player2Id && m.player2.id === player1Id))
+ );
+
+ if (!match) return null;
+
+ // Berechne aktuelle Sätze aus tournamentResults
+ let sets1 = 0, sets2 = 0;
+ if (match.tournamentResults && match.tournamentResults.length > 0) {
+ match.tournamentResults.forEach(result => {
+ if (result.pointsPlayer1 > result.pointsPlayer2) {
+ sets1++;
+ } else if (result.pointsPlayer2 > result.pointsPlayer1) {
+ sets2++;
+ }
+ });
+ }
+
+ // Bestimme die Anzeige basierend auf der Spieler-Reihenfolge
+ if (match.player1.id === player1Id) {
+ return {
+ sets1: sets1,
+ sets2: sets2,
+ isFinished: match.isFinished,
+ player1Won: sets1 > sets2,
+ player2Won: sets2 > sets1,
+ isTie: sets1 === sets2
+ };
+ } else {
+ return {
+ sets1: sets2,
+ sets2: sets1,
+ isFinished: match.isFinished,
+ player1Won: sets2 > sets1,
+ player2Won: sets1 > sets2,
+ isTie: sets1 === sets2
+ };
+ }
+ },
+
+ highlightMatch(player1Id, player2Id, groupId) {
+ console.log('highlightMatch called:', { player1Id, player2Id, groupId });
+
+ // Finde das entsprechende Match (auch unbeendete)
+ const match = this.matches.find(m =>
+ m.round === 'group' &&
+ m.groupId === groupId &&
+ ((m.player1.id === player1Id && m.player2.id === player2Id) ||
+ (m.player1.id === player2Id && m.player2.id === player1Id))
+ );
+
+ console.log('Found match:', match);
+
+ if (!match) {
+ console.log('No match found');
+ return;
+ }
+
+ // Setze Highlight-Klasse
+ this.$nextTick(() => {
+ const matchElement = document.querySelector(`tr[data-match-id="${match.id}"]`);
+ console.log('Match element:', matchElement);
+
+ if (matchElement) {
+ // Entferne vorherige Highlights
+ document.querySelectorAll('.match-highlight').forEach(el => {
+ el.classList.remove('match-highlight');
+ });
+
+ // Füge Highlight hinzu
+ matchElement.classList.add('match-highlight');
+
+ // Scrolle zum Element
+ matchElement.scrollIntoView({
+ behavior: 'smooth',
+ block: 'center'
+ });
+ } else {
+ console.log('Match element not found in DOM');
+ }
+ });
+ },
+
+ clearHighlight() {
+ // Entferne alle Highlights
+ document.querySelectorAll('.match-highlight').forEach(el => {
+ el.classList.remove('match-highlight');
+ });
+ },
+
+ async updateParticipantGroup(participant, event) {
+ const groupNumber = parseInt(event.target.value);
+
+ // Aktualisiere lokal
+ participant.groupNumber = groupNumber;
+
+ // Bereite alle Teilnehmer-Zuordnungen vor
+ const assignments = this.participants.map(p => ({
+ participantId: p.id,
+ groupNumber: p.groupNumber || null
+ }));
+
+ // Sende an Backend
+ try {
+ await apiClient.post('/tournament/groups/manual', {
+ clubId: this.currentClub,
+ tournamentId: this.selectedDate,
+ assignments: assignments,
+ numberOfGroups: this.numberOfGroups,
+ maxGroupSize: this.maxGroupSize
+ });
+
+ // Lade Daten neu, um die aktualisierten Gruppen zu erhalten
+ await this.loadTournamentData();
+ } catch (error) {
+ console.error('Fehler beim Aktualisieren der Gruppe:', error);
+ // Bei Fehler: Lade Daten neu
+ await this.loadTournamentData();
+ }
+ },
+
+ // Initialisiere groupNumber basierend auf groupId
+ initializeParticipantGroupNumbers() {
+ this.participants.forEach(participant => {
+ if (participant.groupId) {
+ const group = this.groups.find(g => g.groupId === participant.groupId);
+ if (group) {
+ participant.groupNumber = group.groupNumber;
+ }
+ }
+ });
+ },
+
+ toggleParticipants() {
+ this.showParticipants = !this.showParticipants;
+ },
+
+ getMatchDisplayText(player1Id, player2Id, groupId) {
+ const liveResult = this.getMatchLiveResult(player1Id, player2Id, groupId);
+ if (!liveResult) return '-';
+
+ // Zeige Satzergebnis (z.B. 1:0, 2:1, 0:2)
+ return `${liveResult.sets1}:${liveResult.sets2}`;
+ },
+
+ getMatchCellClasses(player1Id, player2Id, groupId) {
+ const liveResult = this.getMatchLiveResult(player1Id, player2Id, groupId);
+ if (!liveResult) return ['no-match'];
+
+ const classes = ['match-result'];
+
+ if (liveResult.isFinished) {
+ // Spiel beendet: Dunkle Farben
+ if (liveResult.player1Won) {
+ classes.push('match-finished-win');
+ } else if (liveResult.player2Won) {
+ classes.push('match-finished-loss');
+ } else {
+ classes.push('match-finished-tie');
+ }
+ } else {
+ // Spiel läuft: Helle Farben
+ if (liveResult.player1Won) {
+ classes.push('match-live-win');
+ } else if (liveResult.player2Won) {
+ classes.push('match-live-loss');
+ } else {
+ classes.push('match-live-tie');
+ }
+ }
+
+ return classes;
}
}
};
@@ -822,4 +1086,173 @@ td {
button {
margin-left: 0.5em;
}
+
+.diagonal {
+ background-color: #000;
+ color: #000;
+ display: block;
+ width: 100%;
+ height: 100%;
+ min-height: 20px;
+}
+
+.match-result {
+ font-weight: bold;
+ padding: 2px 4px;
+ border-radius: 3px;
+}
+
+.match-result.win {
+ background-color: #d4edda;
+ color: #155724;
+}
+
+.match-result.loss {
+ background-color: #f8d7da;
+ color: #721c24;
+}
+
+/* Live-Ergebnisse während des Spiels */
+.match-live-win {
+ background-color: #d1eca1; /* Hellgrün */
+ color: #0c5460;
+ font-weight: bold;
+}
+
+.match-live-loss {
+ background-color: #ffeaa7; /* Orange */
+ color: #d63031;
+ font-weight: bold;
+}
+
+.match-live-tie {
+ background-color: #f8f9fa; /* Neutral */
+ color: #6c757d;
+ font-weight: bold;
+}
+
+/* Beendete Spiele */
+.match-finished-win {
+ background-color: #28a745; /* Dunkelgrün */
+ color: white;
+ font-weight: bold;
+}
+
+.match-finished-loss {
+ background-color: #fd7e14; /* Dunkelorange */
+ color: white;
+ font-weight: bold;
+}
+
+.match-finished-tie {
+ background-color: #6c757d; /* Grau */
+ color: white;
+ font-weight: bold;
+}
+
+/* Kollabierbare Teilnehmerliste */
+.participants-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ cursor: pointer;
+ padding: 0.5rem 0;
+ border-bottom: 1px solid #dee2e6;
+ margin-bottom: 1rem;
+}
+
+.participants-header:hover {
+ background-color: #f8f9fa;
+ border-radius: 4px;
+}
+
+.participants-header h4 {
+ margin: 0;
+ color: #495057;
+}
+
+.collapse-icon {
+ font-size: 0.8em;
+ color: #6c757d;
+ transition: transform 0.3s ease;
+ user-select: none;
+}
+
+.collapse-icon.expanded {
+ transform: rotate(180deg);
+}
+
+.participants-content {
+ animation: slideDown 0.3s ease;
+}
+
+@keyframes slideDown {
+ from {
+ opacity: 0;
+ max-height: 0;
+ }
+ to {
+ opacity: 1;
+ max-height: 1000px;
+ }
+}
+
+.add-participant {
+ margin-top: 1rem;
+ padding-top: 1rem;
+ border-top: 1px solid #dee2e6;
+ display: flex;
+ gap: 0.5rem;
+ align-items: center;
+}
+
+.no-match {
+ color: #ccc;
+}
+
+.group-table table {
+ font-size: 0.9em;
+}
+
+.group-table th,
+.group-table td {
+ padding: 0.3em 0.5em;
+ text-align: center;
+}
+
+.group-table th:first-child,
+.group-table td:first-child {
+ text-align: left;
+ font-weight: bold;
+}
+
+.group-table th:nth-child(2),
+.group-table td:nth-child(2) {
+ text-align: left;
+ min-width: 120px;
+}
+
+.match-cell.clickable {
+ cursor: pointer;
+ transition: all 0.2s ease;
+ position: relative;
+}
+
+.match-cell.clickable:hover {
+ background-color: #f8f9fa;
+ transform: scale(1.02);
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.match-highlight {
+ background-color: #fff3cd !important;
+ border: 2px solid #ffc107 !important;
+ animation: highlight-pulse 0.5s ease-in-out;
+}
+
+@keyframes highlight-pulse {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(1.02); }
+ 100% { transform: scale(1); }
+}
\ No newline at end of file
|