feat(tournament): add player details dialog and enhance player name interactions
- Implemented clickable player names in the TournamentPlacementsTab for improved user experience. - Added a PlayerDetailsDialog component to display detailed player information when names are clicked. - Updated localization files to include new strings for player details. - Enhanced data handling for internal and external participants in player dialog logic.
This commit is contained in:
366
frontend/src/components/tournament/PlayerDetailsDialog.vue
Normal file
366
frontend/src/components/tournament/PlayerDetailsDialog.vue
Normal file
@@ -0,0 +1,366 @@
|
||||
<template>
|
||||
<BaseDialog
|
||||
:model-value="modelValue"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
:title="playerName"
|
||||
:is-modal="true"
|
||||
size="medium"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div v-if="!loading && playerData" class="player-details-content">
|
||||
<table class="player-details-table">
|
||||
<tbody>
|
||||
<tr v-if="playerData.name">
|
||||
<td class="label-cell">{{ $t('members.firstName') }} / {{ $t('members.lastName') }}</td>
|
||||
<td class="value-cell">
|
||||
<button class="copy-button" @click="copyToClipboard(playerData.name)" title="Kopieren">📋</button>
|
||||
{{ playerData.name }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="playerData.birthDate">
|
||||
<td class="label-cell">{{ $t('members.birthdate') }}</td>
|
||||
<td class="value-cell">
|
||||
<button class="copy-button" @click="copyToClipboard(formatDate(playerData.birthDate))" title="Kopieren">📋</button>
|
||||
{{ formatDate(playerData.birthDate) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="playerData.address">
|
||||
<td class="label-cell">{{ $t('tournaments.address') }}</td>
|
||||
<td class="value-cell">
|
||||
<button class="copy-button" @click="copyToClipboard(playerData.address)" title="Kopieren">📋</button>
|
||||
{{ playerData.address }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="playerData.gender && playerData.gender !== 'unknown'">
|
||||
<td class="label-cell">{{ $t('members.gender') }}</td>
|
||||
<td class="value-cell">
|
||||
<button class="copy-button" @click="copyToClipboard(formatGender(playerData.gender))" title="Kopieren">📋</button>
|
||||
{{ formatGender(playerData.gender) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="playerData.email">
|
||||
<td class="label-cell">{{ $t('members.emailAddress') }}</td>
|
||||
<td class="value-cell">
|
||||
<button class="copy-button" @click="copyToClipboard(playerData.email)" title="Kopieren">📋</button>
|
||||
{{ playerData.email }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="playerData.phone">
|
||||
<td class="label-cell">{{ $t('members.phoneNumber') }}</td>
|
||||
<td class="value-cell">
|
||||
<button class="copy-button" @click="copyToClipboard(playerData.phone)" title="Kopieren">📋</button>
|
||||
{{ playerData.phone }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-if="!hasAnyData" class="no-data">
|
||||
{{ $t('tournaments.noPlayerDataAvailable') }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="loading" class="loading">
|
||||
{{ $t('messages.loading') }}...
|
||||
</div>
|
||||
<div v-else class="loading">
|
||||
{{ $t('tournaments.noPlayerDataAvailable') }}
|
||||
</div>
|
||||
</BaseDialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseDialog from '../BaseDialog.vue';
|
||||
import apiClient from '../../apiClient.js';
|
||||
|
||||
export default {
|
||||
name: 'PlayerDetailsDialog',
|
||||
components: {
|
||||
BaseDialog
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
playerId: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
isExternal: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
tournamentId: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
clubId: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
playerName: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
data() {
|
||||
return {
|
||||
playerData: null,
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasAnyData() {
|
||||
if (!this.playerData) return false;
|
||||
return !!(this.playerData.name || this.playerData.birthDate || this.playerData.address ||
|
||||
this.playerData.gender || this.playerData.email || this.playerData.phone);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue(newVal) {
|
||||
console.log('[PlayerDetailsDialog] modelValue changed:', newVal, 'playerId:', this.playerId);
|
||||
if (newVal && this.playerId) {
|
||||
// Lade Daten erst, wenn der Dialog geöffnet wird
|
||||
this.loadPlayerData();
|
||||
} else {
|
||||
this.playerData = null;
|
||||
}
|
||||
},
|
||||
playerId(newVal) {
|
||||
console.log('[PlayerDetailsDialog] playerId changed:', newVal, 'modelValue:', this.modelValue);
|
||||
// Wenn der Dialog bereits geöffnet ist und die playerId sich ändert, lade neue Daten
|
||||
if (this.modelValue && newVal) {
|
||||
this.loadPlayerData();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadPlayerData() {
|
||||
console.log('[PlayerDetailsDialog] loadPlayerData called, playerId:', this.playerId, 'isExternal:', this.isExternal, 'clubId:', this.clubId);
|
||||
if (!this.playerId) {
|
||||
console.warn('[PlayerDetailsDialog] Keine playerId');
|
||||
return;
|
||||
}
|
||||
if (!this.clubId || isNaN(Number(this.clubId))) {
|
||||
console.error('[PlayerDetailsDialog] Invalid clubId:', this.clubId);
|
||||
this.playerData = {
|
||||
name: this.playerName,
|
||||
error: true
|
||||
};
|
||||
this.loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.playerData = null;
|
||||
|
||||
try {
|
||||
if (this.isExternal) {
|
||||
// Lade externe Teilnehmer-Daten
|
||||
// Lade alle externen Teilnehmer für dieses Turnier (ohne classId Filter = alle Klassen)
|
||||
const response = await apiClient.post('/tournament/external-participants', {
|
||||
clubId: Number(this.clubId),
|
||||
tournamentId: this.tournamentId,
|
||||
classId: null // null = alle externen Teilnehmer des Turniers
|
||||
});
|
||||
|
||||
const externalParticipant = Array.isArray(response.data)
|
||||
? response.data.find(p => p.id === this.playerId)
|
||||
: null;
|
||||
|
||||
if (externalParticipant) {
|
||||
this.playerData = {
|
||||
name: `${externalParticipant.firstName || ''} ${externalParticipant.lastName || ''}`.trim(),
|
||||
birthDate: externalParticipant.birthDate || null,
|
||||
address: null, // Externe Teilnehmer haben keine Adresse
|
||||
gender: externalParticipant.gender || null,
|
||||
email: null, // Externe Teilnehmer haben keine E-Mail
|
||||
phone: null // Externe Teilnehmer haben keine Telefonnummer
|
||||
};
|
||||
} else {
|
||||
this.playerData = {
|
||||
name: this.playerName,
|
||||
error: true
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Lade interne Member-Daten
|
||||
const response = await apiClient.get(`/clubmembers/get/${Number(this.clubId)}/true`);
|
||||
const member = response.data.find(m => m.id === this.playerId);
|
||||
|
||||
if (member) {
|
||||
// Formatiere Adresse
|
||||
let address = '';
|
||||
const parts = [];
|
||||
if (member.street) parts.push(member.street);
|
||||
if (member.postalCode) parts.push(member.postalCode);
|
||||
if (member.city) parts.push(member.city);
|
||||
address = parts.join(', ');
|
||||
|
||||
// Formatiere Telefonnummer
|
||||
let phone = '';
|
||||
if (member.contacts && Array.isArray(member.contacts)) {
|
||||
const phoneContacts = member.contacts
|
||||
.filter(c => c.type === 'phone')
|
||||
.map(c => c.value);
|
||||
phone = phoneContacts.join(', ');
|
||||
} else if (member.phone) {
|
||||
phone = member.phone;
|
||||
}
|
||||
|
||||
// Formatiere E-Mail
|
||||
let email = '';
|
||||
if (member.contacts && Array.isArray(member.contacts)) {
|
||||
const emailContacts = member.contacts
|
||||
.filter(c => c.type === 'email')
|
||||
.map(c => c.value);
|
||||
email = emailContacts.join(', ');
|
||||
} else if (member.email) {
|
||||
email = member.email;
|
||||
}
|
||||
|
||||
this.playerData = {
|
||||
name: `${member.firstName || ''} ${member.lastName || ''}`.trim(),
|
||||
birthDate: member.birthDate || null,
|
||||
address: address || null,
|
||||
gender: member.gender || null,
|
||||
email: email || null,
|
||||
phone: phone || null
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Spielerdaten:', error);
|
||||
this.playerData = {
|
||||
name: this.playerName,
|
||||
error: true
|
||||
};
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
formatDate(dateString) {
|
||||
if (!dateString) return '';
|
||||
try {
|
||||
const date = new Date(dateString);
|
||||
if (isNaN(date.getTime())) {
|
||||
// Versuche, verschiedene Datumsformate zu parsen
|
||||
const ddmmyyyy = dateString.match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/);
|
||||
if (ddmmyyyy) {
|
||||
return dateString; // Bereits im richtigen Format
|
||||
}
|
||||
return dateString;
|
||||
}
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const year = date.getFullYear();
|
||||
return `${day}.${month}.${year}`;
|
||||
} catch (e) {
|
||||
return dateString;
|
||||
}
|
||||
},
|
||||
formatGender(gender) {
|
||||
if (!gender || gender === 'unknown') return '';
|
||||
const genderMap = {
|
||||
'male': this.$t('members.genderMale'),
|
||||
'female': this.$t('members.genderFemale'),
|
||||
'diverse': this.$t('members.genderDiverse')
|
||||
};
|
||||
return genderMap[gender] || gender;
|
||||
},
|
||||
handleClose() {
|
||||
this.$emit('update:modelValue', false);
|
||||
},
|
||||
async copyToClipboard(text) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
// Optional: Feedback anzeigen (z.B. kurz "Kopiert!" anzeigen)
|
||||
} catch (err) {
|
||||
console.error('Fehler beim Kopieren:', err);
|
||||
// Fallback für ältere Browser
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.opacity = '0';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
} catch (fallbackErr) {
|
||||
console.error('Fallback-Kopieren fehlgeschlagen:', fallbackErr);
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.player-details-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.player-details-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.player-details-table tbody tr {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.player-details-table tbody tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.player-details-table .label-cell {
|
||||
padding: 0.75rem 1rem 0.75rem 0;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
vertical-align: top;
|
||||
width: 40%;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.player-details-table .value-cell {
|
||||
padding: 0.75rem 0;
|
||||
color: #666;
|
||||
vertical-align: top;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 1.1em;
|
||||
padding: 0.25rem 0.5rem;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.2s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.copy-button:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.copy-button:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
@@ -27,7 +27,15 @@
|
||||
<tbody>
|
||||
<tr v-for="(entry, entryIdx) in classPlacements" :key="`final-${classId}-${entryIdx}`">
|
||||
<td class="col-place">{{ entry.position }}.</td>
|
||||
<td>{{ getEntryPlayerName(entry) }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="player-name-clickable"
|
||||
@click="openPlayerDialog(entry)"
|
||||
:title="$t('tournaments.showPlayerDetails')"
|
||||
>
|
||||
{{ getEntryPlayerName(entry) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -59,7 +67,15 @@
|
||||
<tbody>
|
||||
<tr v-for="(r, ri) in g.rankings" :key="`r-${g.groupId}-${ri}`">
|
||||
<td class="col-place">{{ r.position }}.</td>
|
||||
<td>{{ r.name }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="player-name-clickable"
|
||||
@click="openPlayerDialogFromRanking(r)"
|
||||
:title="$t('tournaments.showPlayerDetails')"
|
||||
>
|
||||
{{ r.name }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ r.points }}</td>
|
||||
<td>{{ r.setsWon }}:{{ r.setsLost }}</td>
|
||||
<td>{{ r.setDiff >= 0 ? '+' + r.setDiff : r.setDiff }}</td>
|
||||
@@ -73,15 +89,29 @@
|
||||
<div v-if="Object.keys(finalPlacementsByClass).length === 0 && groupPlacements.length === 0" class="no-placements">
|
||||
<p>{{ $t('tournaments.noPlacementsYet') }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Player Details Dialog -->
|
||||
<PlayerDetailsDialog
|
||||
v-model="showPlayerDialog"
|
||||
:player-id="selectedPlayerId"
|
||||
:is-external="selectedPlayerIsExternal"
|
||||
:tournament-id="selectedDate"
|
||||
:club-id="clubId"
|
||||
:player-name="selectedPlayerName"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TournamentClassSelector from './TournamentClassSelector.vue';
|
||||
import PlayerDetailsDialog from './PlayerDetailsDialog.vue';
|
||||
|
||||
export default {
|
||||
name: 'TournamentPlacementsTab',
|
||||
components: { TournamentClassSelector },
|
||||
components: {
|
||||
TournamentClassSelector,
|
||||
PlayerDetailsDialog
|
||||
},
|
||||
props: {
|
||||
selectedDate: { type: [String, Number], default: null },
|
||||
selectedViewClass: { type: [Number, String, null], default: null },
|
||||
@@ -91,9 +121,26 @@ export default {
|
||||
pairings: { type: Array, required: true },
|
||||
groups: { type: Array, required: true },
|
||||
groupRankings: { type: Object, required: true },
|
||||
knockoutMatches: { type: Array, required: true }
|
||||
knockoutMatches: { type: Array, required: true },
|
||||
clubId: { type: [Number, String], required: true }
|
||||
},
|
||||
emits: ['update:selectedViewClass'],
|
||||
data() {
|
||||
return {
|
||||
showPlayerDialog: false,
|
||||
selectedPlayerId: null,
|
||||
selectedPlayerIsExternal: false,
|
||||
selectedPlayerName: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
showPlayerDialog(newVal) {
|
||||
console.log('[TournamentPlacementsTab] showPlayerDialog changed:', newVal);
|
||||
},
|
||||
selectedPlayerId(newVal) {
|
||||
console.log('[TournamentPlacementsTab] selectedPlayerId changed:', newVal);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// Flag für 'Alle Klassen'
|
||||
isAllSelected() {
|
||||
@@ -379,7 +426,8 @@ export default {
|
||||
points: r.points,
|
||||
setsWon: r.setsWon,
|
||||
setsLost: r.setsLost,
|
||||
setDiff: r.setDiff
|
||||
setDiff: r.setDiff,
|
||||
isExternal: r.isExternal || false
|
||||
}))
|
||||
});
|
||||
}
|
||||
@@ -402,7 +450,8 @@ export default {
|
||||
points: r.points,
|
||||
setsWon: r.setsWon,
|
||||
setsLost: r.setsLost,
|
||||
setDiff: r.setDiff
|
||||
setDiff: r.setDiff,
|
||||
isExternal: r.isExternal || false
|
||||
}))
|
||||
});
|
||||
});
|
||||
@@ -464,6 +513,126 @@ export default {
|
||||
if (cid == null) return false;
|
||||
const c = (this.tournamentClasses || []).find(x => x.id === cid);
|
||||
return Boolean(c && c.isDoubles);
|
||||
},
|
||||
openPlayerDialog(entry) {
|
||||
console.log('[openPlayerDialog] entry:', entry);
|
||||
// Für Doppel-Paarungen können wir keine Details anzeigen
|
||||
if (entry.displayName) {
|
||||
console.log('[openPlayerDialog] Doppel-Paarung, keine Details');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.clubId) {
|
||||
console.warn('[openPlayerDialog] clubId fehlt');
|
||||
return;
|
||||
}
|
||||
|
||||
const memberId = entry.member?.id;
|
||||
if (!memberId) {
|
||||
console.warn('[openPlayerDialog] Keine Member-ID:', {
|
||||
hasMember: !!entry.member,
|
||||
memberId: entry.member?.id
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Prüfe zuerst in externalParticipants, ob es ein externer Teilnehmer ist
|
||||
const externalParticipant = this.externalParticipants.find(p => p.id === memberId);
|
||||
if (externalParticipant) {
|
||||
// Externer Teilnehmer gefunden
|
||||
console.log('[openPlayerDialog] Externer Teilnehmer, ID:', memberId);
|
||||
this.selectedPlayerId = memberId;
|
||||
this.selectedPlayerIsExternal = true;
|
||||
this.selectedPlayerName = this.getEntryPlayerName(entry);
|
||||
this.showPlayerDialog = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Prüfe in participants, ob es ein interner Teilnehmer ist
|
||||
const participant = this.participants.find(p => {
|
||||
// Prüfe verschiedene mögliche ID-Felder
|
||||
return (p.member && p.member.id === memberId) ||
|
||||
p.id === memberId ||
|
||||
p.clubMemberId === memberId;
|
||||
});
|
||||
|
||||
if (participant) {
|
||||
// Interner Teilnehmer gefunden
|
||||
const actualMemberId = participant.member?.id || participant.clubMemberId || memberId;
|
||||
console.log('[openPlayerDialog] Interner Teilnehmer, Member ID:', actualMemberId);
|
||||
this.selectedPlayerId = actualMemberId;
|
||||
this.selectedPlayerIsExternal = false;
|
||||
this.selectedPlayerName = this.getEntryPlayerName(entry);
|
||||
this.showPlayerDialog = true;
|
||||
} else {
|
||||
// Weder in participants noch in externalParticipants gefunden
|
||||
// Fallback: Versuche nochmal in externalParticipants mit firstName/lastName
|
||||
const entryName = this.getEntryPlayerName(entry);
|
||||
const nameParts = entryName.split(' ').filter(p => p);
|
||||
if (nameParts.length >= 2) {
|
||||
const firstName = nameParts[0];
|
||||
const lastName = nameParts.slice(1).join(' ');
|
||||
const extByName = this.externalParticipants.find(p =>
|
||||
p.firstName === firstName && p.lastName === lastName
|
||||
);
|
||||
if (extByName) {
|
||||
console.log('[openPlayerDialog] Externer Teilnehmer (nach Name), ID:', extByName.id);
|
||||
this.selectedPlayerId = extByName.id;
|
||||
this.selectedPlayerIsExternal = true;
|
||||
this.selectedPlayerName = entryName;
|
||||
this.showPlayerDialog = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.warn('[openPlayerDialog] Teilnehmer nicht gefunden, versuche als intern:', {
|
||||
memberId,
|
||||
entryName
|
||||
});
|
||||
// Letzter Fallback: Annahme, dass es ein interner Member ist
|
||||
this.selectedPlayerId = memberId;
|
||||
this.selectedPlayerIsExternal = false;
|
||||
this.selectedPlayerName = this.getEntryPlayerName(entry);
|
||||
this.showPlayerDialog = true;
|
||||
}
|
||||
},
|
||||
openPlayerDialogFromRanking(ranking) {
|
||||
console.log('[openPlayerDialogFromRanking] ranking:', ranking);
|
||||
if (!this.clubId) {
|
||||
console.warn('[openPlayerDialogFromRanking] clubId nicht verfügbar');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prüfe, ob es ein interner oder externer Teilnehmer ist
|
||||
// ranking.id ist die TournamentMember.id oder ExternalTournamentParticipant.id
|
||||
if (ranking.isExternal === true) {
|
||||
// Externer Teilnehmer: ranking.id ist die ExternalTournamentParticipant.id
|
||||
console.log('[openPlayerDialogFromRanking] Externer Teilnehmer, ID:', ranking.id);
|
||||
this.selectedPlayerId = ranking.id;
|
||||
this.selectedPlayerIsExternal = true;
|
||||
this.selectedPlayerName = ranking.name;
|
||||
this.showPlayerDialog = true;
|
||||
} else {
|
||||
// Interner Teilnehmer: Finde den Participant, um die clubMemberId zu bekommen
|
||||
const participant = this.participants.find(p => p.id === ranking.id);
|
||||
console.log('[openPlayerDialogFromRanking] Interner Teilnehmer, participant:', participant);
|
||||
if (participant && participant.member && participant.member.id) {
|
||||
console.log('[openPlayerDialogFromRanking] Öffne Dialog für Member ID:', participant.member.id);
|
||||
this.selectedPlayerId = participant.member.id;
|
||||
this.selectedPlayerIsExternal = false;
|
||||
this.selectedPlayerName = ranking.name;
|
||||
this.showPlayerDialog = true;
|
||||
} else if (participant && participant.clubMemberId) {
|
||||
// Fallback: Verwende clubMemberId direkt
|
||||
console.log('[openPlayerDialogFromRanking] Öffne Dialog für clubMemberId:', participant.clubMemberId);
|
||||
this.selectedPlayerId = participant.clubMemberId;
|
||||
this.selectedPlayerIsExternal = false;
|
||||
this.selectedPlayerName = ranking.name;
|
||||
this.showPlayerDialog = true;
|
||||
} else {
|
||||
console.warn('[openPlayerDialogFromRanking] Teilnehmer nicht gefunden für ranking.id:', ranking.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -549,6 +718,16 @@ th {
|
||||
padding: 2rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.player-name-clickable {
|
||||
cursor: pointer;
|
||||
color: #1976d2;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.player-name-clickable:hover {
|
||||
color: #1565c0;
|
||||
}
|
||||
</style>
|
||||
/* Spaltenbreite für Platz: 4em */
|
||||
table thead th:first-child,
|
||||
|
||||
@@ -649,6 +649,9 @@
|
||||
"createMatches": "Spiele erstellen",
|
||||
"startKORound": "K.o.-Runde starten",
|
||||
"deleteKORound": "K.o.-Runde",
|
||||
"address": "Adresse",
|
||||
"showPlayerDetails": "Spielerdetails anzeigen",
|
||||
"noPlayerDataAvailable": "Keine Spielerdaten verfügbar",
|
||||
"koRound": "K.-o.-Runde",
|
||||
"errorUpdatingTournament": "Fehler beim Aktualisieren des Turniers.",
|
||||
"pleaseEnterDate": "Bitte geben Sie ein Datum ein!",
|
||||
|
||||
@@ -225,6 +225,7 @@
|
||||
:participants="participants"
|
||||
:external-participants="externalParticipants"
|
||||
:pairings="pairings"
|
||||
:club-id="currentClub"
|
||||
@update:selectedViewClass="selectedViewClass = $event"
|
||||
/>
|
||||
</div>
|
||||
@@ -491,7 +492,8 @@ export default {
|
||||
pointsLost: Math.abs(p.pointsLost || 0),
|
||||
pointRatio: p.pointRatio || 0,
|
||||
matchesWon: p.matchesWon || 0,
|
||||
matchesLost: p.matchesLost || 0
|
||||
matchesLost: p.matchesLost || 0,
|
||||
isExternal: p.isExternal || false
|
||||
}));
|
||||
});
|
||||
return rankings;
|
||||
|
||||
Reference in New Issue
Block a user