Erweitert den MatchReportApiDialog mit neuen Funktionen zur Abschlussbearbeitung. Fügt die Anzeige von Aufstellungen, Endergebnis, Zeitangaben, Protest-Eingabe und PIN-Eingaben hinzu. Implementiert Methoden zur Extraktion von Einzel- und Doppelspielern sowie zur Berechnung des Gesamtergebnisses. Optimiert die Benutzeroberfläche mit neuen CSS-Stilen für eine verbesserte Darstellung.

This commit is contained in:
Torsten Schulz (local)
2025-10-03 20:13:49 +02:00
parent ac727c6c5b
commit d23a9f086c

View File

@@ -67,12 +67,12 @@
<div class="team-stats">
<div class="home-team">
<h3>{{ meetingData.homeClub }}</h3>
<div class="points">{{ getOverallScore().split(':')[0] }}</div>
<div class="points">{{ getOverallMatchScore().split(':')[0] }}</div>
</div>
<div class="vs">vs</div>
<div class="guest-team">
<h3>{{ meetingData.guestClub }}</h3>
<div class="points">{{ getOverallScore().split(':')[1] }}</div>
<div class="points">{{ getOverallMatchScore().split(':')[1] }}</div>
</div>
</div>
</div>
@@ -376,9 +376,115 @@
</div>
<div v-else-if="activeSection === 'completion'" class="completion-content">
<h3>Abschluss</h3>
<p>Hier können Sie den Abschluss des Spielberichts vornehmen.</p>
<button class="btn-primary">Spielbericht abschließen</button>
<!-- Aufstellungen -->
<div class="lineups-summary">
<div class="lineup-team">
<h4>{{ meetingData.homeTeamname }}</h4>
<div class="einzel-section">
<h5>Einzel</h5>
<div v-for="(player, idx) in getHomeSinglePlayers()" :key="idx" class="player-row">
{{ idx + 1 }}: {{ player }}
</div>
</div>
<div class="doppel-section">
<h5>Doppel</h5>
<div v-for="(pair, idx) in getHomeDoublePairs()" :key="idx" class="pair-row">
D{{ idx + 1 }}: {{ pair }}
</div>
</div>
</div>
<div class="team-divider">vs</div>
<div class="lineup-team">
<h4>{{ meetingData.guestTeamname }}</h4>
<div class="einzel-section">
<h5>Einzel</h5>
<div v-for="(player, idx) in getGuestSinglePlayers()" :key="idx" class="player-row">
{{ idx + 1 }}: {{ player }}
</div>
</div>
<div class="doppel-section">
<h5>Doppel</h5>
<div v-for="(pair, idx) in getGuestDoublePairs()" :key="idx" class="pair-row">
D{{ idx + 1 }}: {{ pair }}
</div>
</div>
</div>
</div>
<!-- Endergebnis -->
<div class="final-score">
<h3>Endergebnis</h3>
<div class="score-summary large">
<div class="score-display">
<span class="score-label">Spielstand:</span>
<span class="score-value final">{{ getOverallScore() }}</span>
</div>
</div>
</div>
<!-- Zeitangaben -->
<div class="time-summary">
<div class="time-display">
<label>Startzeit:</label>
<span class="time-value">{{ getFormattedTime(match.startDate) || 'nicht gesetzt' }}</span>
</div>
<div class="time-input-group">
<label>Endzeit:</label>
<input
type="time"
:value="getFormattedTime(match.endDate)"
@change="setEndTime($event.target.value)"
class="time-input"
/>
<button @click="setCurrentEndTime" class="time-btn" title="Aktuelle Zeit setzen">🕐</button>
</div>
</div>
<!-- Protest-Eingabe -->
<div class="protest-section">
<label for="protestInput">Protest:</label>
<textarea
id="protestInput"
v-model="protestText"
placeholder="Protestgrund eingeben..."
class="protest-input"
></textarea>
</div>
<!-- PIN-Eingaben -->
<div class="final-pins">
<div class="pin-group">
<label for="finalHomePin">PIN Heimverein:</label>
<input
id="finalHomePin"
v-model="finalHomePin"
type="password"
class="pin-input"
:placeholder="match.homePin || 'PIN eingeben'"
/>
</div>
<div class="pin-group">
<label for="finalGuestPin">PIN Gastverein:</label>
<input
id="finalGuestPin"
v-model="finalGuestPin"
type="password"
class="pin-input"
:placeholder="match.guestPin || 'PIN eingeben'"
/>
</div>
</div>
<!-- Absenden-Button -->
<div class="submit-section">
<button @click="submitMatchReport" class="btn-primary submit-btn">
📝 Spielbericht absenden
</button>
</div>
</div>
</div>
@@ -482,6 +588,10 @@ export default {
results: [],
// Fehlermeldungen für Satzeingaben
errors: [],
// Abschluss-Felder
protestText: '',
finalHomePin: '',
finalGuestPin: '',
// Definitionen der Spielsysteme als Matrizen (Reihenfolge der Begegnungen)
// A = Heim (A1..), B = Gast (B1..), D = Doppel (DAx, DBx)
playModeMatrices: {
@@ -556,8 +666,162 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
async mounted() {
await this.loadData();
this.initializeResults();
this.initializeFinalPins();
},
methods: {
// Methoden für Abschluss-Seite
getHomeDoublePairs() {
if (!this.results || this.results.length === 0) return [];
// Extrahiere Doppelpaare aus den tatsächlichen Match-Ergebnissen
const pairs = [];
this.results.forEach(match => {
// Doppelpaare enthalten "/" im Namen
if (match.homeName && match.homeName.includes(' / ')) {
if (!pairs.includes(match.homeName)) {
pairs.push(match.homeName);
}
}
});
return pairs;
},
getHomeSinglePlayers() {
if (!this.results || this.results.length === 0) return [];
// Extrahiere Einzelspieler in der Reihenfolge ihres Auftretens
const players = [];
const seen = new Set();
this.results.forEach(match => {
if (match.homeName && match.homeName !== '' && !match.homeName.includes(' / ')) {
if (!seen.has(match.homeName)) {
seen.add(match.homeName);
players.push(match.homeName);
}
}
});
return players;
},
getGuestDoublePairs() {
if (!this.results || this.results.length === 0) return [];
// Extrahiere Doppelpaare aus den tatsächlichen Match-Ergebnissen
const pairs = [];
this.results.forEach(match => {
// Doppelpaare enthalten "/" im Namen
if (match.guestName && match.guestName.includes(' / ')) {
if (!pairs.includes(match.guestName)) {
pairs.push(match.guestName);
}
}
});
return pairs;
},
getGuestSinglePlayers() {
if (!this.results || this.results.length === 0) return [];
// Extrahiere Einzelspieler in der Reihenfolge ihres Auftretens
const players = [];
const seen = new Set();
this.results.forEach(match => {
if (match.guestName && match.guestName !== '' && !match.guestName.includes(' / ')) {
if (!seen.has(match.guestName)) {
seen.add(match.guestName);
players.push(match.guestName);
}
}
});
return players;
},
getOverallMatchScore() {
let homeWins = 0;
let guestWins = 0;
this.results.forEach(match => {
if (match.completed) {
let matchHomeWins = 0;
let matchGuestWins = 0;
match.sets.forEach(set => {
if (set && set.includes(':')) {
const [home, guest] = set.split(':').map(s => parseInt(s) || 0);
if (home > guest) {
matchHomeWins++;
} else if (guest > home) {
matchGuestWins++;
}
}
});
if (matchHomeWins > matchGuestWins) {
homeWins++;
} else if (matchGuestWins > matchHomeWins) {
guestWins++;
}
}
});
return `${homeWins}:${guestWins}`;
},
getOverallSetScore() {
let totalHomeSets = 0;
let totalGuestSets = 0;
this.results.forEach(match => {
if (match.completed) {
match.sets.forEach(set => {
if (set && set.includes(':')) {
const [home, guest] = set.split(':').map(s => parseInt(s) || 0);
totalHomeSets += home;
totalGuestSets += guest;
}
});
}
});
return `(${totalHomeSets}:${totalGuestSets})`;
},
initializeFinalPins() {
// Initialisiere PIN-Felder mit den Werten aus dem Match-Objekt
this.finalHomePin = this.match.homePin || '';
this.finalGuestPin = this.match.guestPin || '';
},
async submitMatchReport() {
try {
const reportData = {
matchId: this.match.id,
startTime: this.match.startDate,
endTime: this.match.endDate,
protestText: this.protestText,
homePin: this.finalHomePin,
guestPin: this.finalGuestPin,
results: this.results,
finalScore: this.getOverallScore()
};
console.log('Spielbericht-Daten:', reportData);
alert('Spielbericht erfolgreich abgesendet!');
} catch (error) {
console.error('Fehler beim Absenden:', error);
alert('Fehler beim Absenden des Spielberichts');
}
},
// Verwende die Matches aus dem Match-Objekt oder erstelle defaults
initializeResults() {
if (this.match.matches && Array.isArray(this.match.matches)) {
@@ -1192,16 +1456,21 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
getOverallScore() {
let homeWins = 0;
let guestWins = 0;
let totalHomeSets = 0;
let totalGuestSets = 0;
this.results.forEach(match => {
if (match.completed) {
// Zähle gewonnene Sätze für dieses Match
// Source gewonnene Sätze für dieses Match und Gesamtsätze
let matchHomeWins = 0;
let matchGuestWins = 0;
match.sets.forEach(set => {
if (set && set.includes(':')) {
const [home, guest] = set.split(':').map(s => parseInt(s) || 0);
totalHomeSets += home;
totalGuestSets += guest;
if (home > guest) {
matchHomeWins++;
} else if (guest > home) {
@@ -1219,7 +1488,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
}
});
return `${homeWins}:${guestWins}`;
return `${homeWins}:${guestWins} (${totalHomeSets}:${totalGuestSets} Sätze)`;
},
getMatchResult(match) {
@@ -2117,6 +2386,174 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
border-color: var(--primary-color);
}
/* Abschluss-Seite */
.lineups-summary {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 15px;
margin-bottom: 20px;
padding: 12px;
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
}
.lineup-team {
background: #fff;
padding: 10px;
border-radius: 4px;
border: 1px solid #dee2e6;
}
.lineup-team h4 {
margin: 0 0 8px 0;
color: var(--primary-color);
text-align: center;
font-size: 16px;
}
.doppel-section, .einzel-section {
margin-bottom: 8px;
}
.doppel-section h5, .einzel-section h5 {
margin: 0 0 4px 0;
color: #495057;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.3px;
}
.pair-row, .player-row {
padding: 2px 0;
font-size: 13px;
color: #495057;
}
.team-divider {
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: var(--primary-color);
font-size: 16px;
}
.final-score {
text-align: center;
margin: 16px 0;
}
.final-score h3 {
margin: 0 0 8px 0;
color: var(--primary-color);
font-size: 18px;
}
.score-summary.large {
padding: 10px;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border: 2px solid var(--primary-color);
border-radius: 6px;
}
.score-summary.large .score-value {
font-size: 20px;
font-weight: bold;
}
.time-summary {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 16px;
padding: 10px;
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
}
.time-display {
display: flex;
align-items: center;
gap: 8px;
}
.time-display label {
font-weight: 500;
color: #495057;
}
.time-value {
font-family: 'Courier New', monospace;
font-weight: 600;
color: var(--primary-color);
}
.protest-section {
margin-bottom: 16px;
}
.protest-section label {
display: block;
margin-bottom: 4px;
font-weight: 500;
color: #495057;
font-size: 14px;
}
.protest-input {
width: 100%;
min-height: 50px;
padding: 8px;
border: 1px solid #dee2e6;
border-radius: 4px;
font-family: inherit;
font-size: 13px;
resize: vertical;
}
.protest-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(var(--primary-rgb), 0.2);
}
.final-pins {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 16px;
padding: 10px;
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
}
.pin-group {
display: flex;
flex-direction: column;
gap: 4px;
}
.pin-group label {
font-weight: 500;
color: #495057;
font-size: 14px;
}
.submit-section {
text-align: center;
padding: 16px;
}
.submit-btn {
font-size: 16px;
padding: 12px 24px;
font-weight: 600;
}
/* Fehlermeldungen */
.error-row {
background-color: #fff5f5;