Erweitert den MatchReportApiDialog um umfassende Validierungslogik für die Eingabe von Spielberichten. Implementiert neue Warnungen für unvollständige oder fehlerhafte Match-Ergebnisse sowie für ungültige Start- und Endzeiten. Aktualisiert die Logik zur Aktivierung und Deaktivierung des Absende-Buttons basierend auf den Validierungsbedingungen. Verbessert die Benutzeroberfläche mit neuen CSS-Stilen für Validierungsbenachrichtigungen und optimiert die Benutzererfahrung durch dynamische Rückmeldungen.

This commit is contained in:
Torsten Schulz (local)
2025-10-03 23:42:52 +02:00
parent 049ee56571
commit e354d82969

View File

@@ -512,14 +512,34 @@
<button
@click="submitMatchReport"
class="btn-primary submit-btn"
:disabled="isMatchCompleted"
:class="{ 'disabled': isMatchCompleted }"
:disabled="isMatchCompleted || !areAllMatchResultsValid() || !areStartAndEndTimesValid()"
:class="{ 'disabled': isMatchCompleted || !areAllMatchResultsValid() || !areStartAndEndTimesValid() }"
>
{{ isMatchCompleted ? '✅ Spielbericht bereits abgesendet' : '📝 Spielbericht absenden' }}
{{ getSubmitButtonText() }}
</button>
<!-- Warnung wenn Match abgeschlossen -->
<div v-if="isMatchCompleted" class="completion-notice">
Dieser Spielbericht wurde bereits abgesendet. Keine weiteren Änderungen möglich.
</div>
<!-- Warnung wenn Match-Ergebnisse fehlerhaft sind -->
<div v-else-if="!areAllMatchResultsValid()" class="validation-notice">
<span v-if="getPlayThrough() && getPlayThrough().toUpperCase().includes('ALLGAMES')">
Der Spielbericht kann nicht abgesendet werden. Es müssen alle Spiele abgeschlossen sein.
<br><strong>Bitte schließen Sie alle {{ results.filter(match => !match.completed).length }} offenen Spiele ab!</strong>
</span>
<span v-else>
Der Spielbericht kann nicht abgesendet werden. Es gibt noch Fehler in den Match-Ergebnissen.
<br><strong>Bitte überprüfen Sie die Ergebniserfassung!</strong>
</span>
</div>
<!-- Warnung wenn Zeiten fehlerhaft sind -->
<div v-else-if="!areStartAndEndTimesValid()" class="validation-notice">
Der Spielbericht kann nicht abgesendet werden. Start- oder Endzeit sind nicht festgelegt.
<br><strong>Bitte legen Sie beide Zeiten fest!</strong>
</div>
</div>
</div>
</div>
@@ -642,10 +662,21 @@ export default {
'werner-scheffler-system': [
'DA1 DB1', 'DA2 DB2', 'A1 B2', 'A2 B1', 'A3 B4', 'A4 B3', 'A1 B1', 'A2 B2', 'A3 B3', 'A4 B4', 'A3 B1', 'A1 B3', 'A2 B4', 'A4 B2'
],
'braunschweiger system': [
// ViererVierer (häufigster Pfad)
'DA1 DB1', 'DA2 DB2', 'A1 B1', 'A2 B2', 'A3 B3', 'A4 B4', 'A1 B2', 'A2 B1', 'A3 B4', 'A4 B3'
],
'braunschweiger system': {
// Dynamische Auswahl je nach Spielerzahlen
matrices: {
// 4 vs 4 Spieler
'4-4': ['DA1 DB1', 'DA2 DB2', 'A1 B1', 'A2 B2', 'A3 B3', 'A4 B4', 'A1 B2', 'A2 B1', 'A3 B4', 'A4 B3'],
// 4 vs 3 Spieler
'4-3': ['DA1 DB1', 'A3 B3', 'A1 B2', 'A2 B1', 'A4 B2', 'A1 B1', 'A4 B3', 'A2 B2', 'A1 B3', 'A3 B1'],
// 3 vs 4 Spieler
'3-4': ['DA1 DB1', 'A3 B3', 'A2 B1', 'A1 B2', 'A2 B4', 'A1 B1', 'A3 B4', 'A2 B2', 'A3 B1', 'A1 B3'],
// 3 vs 3 Spieler
'3-3': ['DA1 DB1', 'A1 B2', 'A2 B1', 'A3 B2', 'A2 B3', 'A1 B1', 'A3 B3', 'A2 B2', 'A3 B1', 'A1 B3']
},
// Fallback für unbekannte Kombination
default: ['DA1 DB1', 'DA2 DB2', 'A1 B1', 'A2 B2', 'A3 B3', 'A4 B4', 'A1 B2', 'A2 B1', 'A3 B4', 'A4 B3']
},
'modifiziertes swaythling-cup-system': [
'A1 B2', 'A2 B1', 'A3 B3', 'DA1 DB1', 'A1 B1', 'A3 B2', 'A2 B3'
],
@@ -700,8 +731,29 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
},
currentPlayModeMatrix() {
const key = (this.currentPlayMode || '').toLowerCase();
return this.playModeMatrices[key] || [];
}
const matrixConfig = this.playModeMatrices[key];
// Spezielle Behandlung für Braunschweiger System
if (matrixConfig && typeof matrixConfig === 'object' && matrixConfig.matrices) {
const homePlayerCount = this.getSelectedPlayerCount('home');
const guestPlayerCount = this.getSelectedPlayerCount('guest');
// Erstelle Key basierend auf Spielerzahlen (z.B. "3-4", "4-4")
const playerCountKey = `${homePlayerCount}-${guestPlayerCount}`;
console.log(`🎯 Braunschweiger System: ${homePlayerCount} vs ${guestPlayerCount} → Key: ${playerCountKey}`);
// Verwende spezifische Matrix oder Fallback
return matrixConfig.matrices[playerCountKey] || matrixConfig.default || [];
}
// Standard-Verhalten für andere Systeme
return matrixConfig || [];
},
// Prüfe ob das Match bereits abgeschlossen ist
isMatchCompleted() {
return this.match && this.match.isCompleted === true;
},
},
async mounted() {
await this.loadData();
@@ -845,10 +897,151 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
this.finalGuestPin = this.match.guestPin || '';
},
// Text für den Absenden-Button basierend auf Zustand
getSubmitButtonText() {
if (this.isMatchCompleted) {
return '✅ Spielbericht bereits abgesendet';
} else if (!this.areAllMatchResultsValid()) {
const playThrough = this.getPlayThrough();
if (playThrough && playThrough.toUpperCase().includes('ALLGAMES')) {
const unfinishedMatches = this.results.filter(match => !match.completed);
return `❌ Spielbericht absenden (${unfinishedMatches.length} Spiel/e noch offen)`;
}
return '❌ Spielbericht absenden (Ergebnisse fehlerhaft)';
} else if (!this.areStartAndEndTimesValid()) {
return '❌ Spielbericht absenden (Zeiten fehlerhaft)';
} else {
return '📝 Spielbericht absenden';
}
},
// Prüfe ob Start- und Endzeiten korrekt festgelegt sind
areStartAndEndTimesValid() {
// Prüfe ob Startzeit festgelegt wurde
if (!this.match.startDate) {
console.log('❌ Startzeit ist nicht festgelegt');
return false;
}
// Prüfe ob Endzeit festgelegt wurde
if (!this.match.endDate) {
console.log('❌ Endzeit ist nicht festgelegt');
return false;
}
// Prüfe ob Endzeit nach Startzeit liegt
const startTime = new Date(this.match.startDate).getTime();
const endTime = new Date(this.match.endDate).getTime();
if (endTime <= startTime) {
console.log('⚠️ Endzeit liegt vor/vor Startzeit - korrigiere auf nächsten Tag...');
// Setze Endzeit auf nächsten Tag um dieselbe Uhrzeit
const nextDay = new Date(endTime);
nextDay.setDate(nextDay.getDate() + 1);
this.match.endDate = nextDay;
console.log('✅ Endzeit wurde auf nächsten Tag verschoben:', nextDay.toISOString());
}
console.log('✅ Start- und Endzeiten sind korrekt');
return true;
},
// Prüfe ob alle Matchergebnisse korrekt sind (für Spielbericht-Absendung)
areAllMatchResultsValid() {
if (!this.results || this.results.length === 0) {
return false;
}
// Spezielle Regel für ALLGAMES Spielmodus
const playThrough = this.getPlayThrough();
if (playThrough && playThrough.toUpperCase().includes('ALLGAMES')) {
// Alle Spiele müssen abgeschlossen sein
const unfinishedMatches = this.results.filter(match => !match.completed);
if (unfinishedMatches.length > 0) {
console.log(`❌ ALLGAMES Modus: ${unfinishedMatches.length} Spiel(e) noch nicht abgeschlossen`);
return false;
}
console.log('✅ ALLGAMES Modus: Alle Spiele sind abgeschlossen');
}
for (let i = 0; i < this.results.length; i++) {
const match = this.results[i];
// Prüfe auf Lücken in der Sätze-Reihenfolge
const gapIndices = this.hasGapInSets(match);
if (gapIndices !== false && Array.isArray(gapIndices) && gapIndices.length > 0) {
console.log(`❌ Match ${i + 1}: Lücken gefunden bei Satz ${gapIndices.map(idx => idx + 1).join(', ')}`);
return false;
}
// Prüfe jeden Satz auf Validität (nur ausgefüllte Sätze)
for (let j = 0; j < match.sets.length; j++) {
const set = match.sets[j];
if (set && set.trim().length > 0 && set.includes(':')) {
// Parse die Satzergebnisse
const [homeScoreStr, guestScoreStr] = set.split(':');
const homeScore = parseInt(homeScoreStr) || 0;
const guestScore = parseInt(guestScoreStr) || 0;
const validationResult = this.validateSetScore(homeScore, guestScore, set);
if (!validationResult.isValid) {
console.log(`❌ Match ${i + 1}, Satz ${j + 1}: ${validationResult.error}`);
return false;
}
}
}
// Prüfe Match-Abschluss-Regeln wenn Match als abgeschlossen markiert ist
if (match.completed) {
const completionValidation = this.validateMatchCompletion(i);
if (!completionValidation.isValid) {
console.log(`❌ Match ${i + 1}: ${completionValidation.error}`);
return false;
}
}
}
console.log('✅ Alle Match-Ergebnisse sind korrekt');
return true;
},
async submitMatchReport() {
try {
console.log('🚀 Starte Spielbericht-Absendung...');
// Validiere zuerst alle Match-Ergebnisse
if (!this.areAllMatchResultsValid()) {
const playThrough = this.getPlayThrough();
if (playThrough && playThrough.toUpperCase().includes('ALLGAMES')) {
const unfinishedMatches = this.results.filter(match => !match.completed);
alert(`❌ Der Spielbericht kann nicht abgesendet werden. Es müssen alle Spiele abgeschlossen sein.\n\n` +
`📊 Aktueller Status:\n` +
`${this.results.length} Spiele insgesamt\n` +
`${this.results.length - unfinishedMatches.length} Spiele abgeschlossen\n` +
`${unfinishedMatches.length} Spiele noch offen\n\n` +
`Bitte schließen Sie alle offenen Spiele ab, bevor Sie den Spielbericht absenden.`);
return;
} else {
alert('❌ Der Spielbericht kann nicht abgesendet werden, da noch Fehler in den Match-Ergebnissen vorhanden sind. Bitte korrigieren Sie:\n\n' +
'• Lücken in der Satze-Reihenfolge\n' +
'• Ungültige Satzergebnisse\n' +
'• Ungenerische Match-Abschlüsse\n\n' +
'Überprüfen Sie die Ergebniserfassung.');
return;
}
}
console.log('✅ Alle Match-Ergebnisse sind gültig - fahre fort...');
// Validiere Start- und Endzeiten
if (!this.areStartAndEndTimesValid()) {
alert('❌ Der Spielbericht kann nicht abgesendet werden, da Start- oder Endzeit nicht festgelegt wurden.\n\n' +
'Bitte legen Sie sowohl Startzeit als auch Endzeit fest.');
return;
}
console.log('✅ Alle Zeiten sind gültig - fahre fort...');
// Validiere PINs (für Test: Gast-PIN "1234" akzeptieren)
if (!this.finalHomePin || this.finalHomePin.trim() === '') {
alert('Bitte geben Sie die PIN des Heimvereins ein.');
@@ -1163,10 +1356,6 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
console.log('✅ Player-Positionen aktualisiert');
},
// Prüfe ob das Match bereits abgeschlossen ist
get isMatchCompleted() {
return this.match && this.match.isCompleted === true;
},
// Zähle die ausgewählten Spieler für ein Team
getSelectedPlayerCount(team) {
@@ -1874,6 +2063,21 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
const now = new Date();
const timeString = now.toTimeString().slice(0, 5); // HH:MM format
this.match.endDate = this.createDateTimeFromTimeString(timeString);
// Prüfe ob Endzeit vor Startzeit liegt und korrigiere automatisch
if (this.match.startDate && this.match.endDate) {
const startTime = new Date(this.match.startDate).getTime();
const endTime = new Date(this.match.endDate).getTime();
if (endTime <= startTime) {
console.log('⚠️ Endzeit liegt vor/auf Startzeit - korrigiere auf nächsten Tag...');
// Setze Endzeit auf nächsten Tag um dieselbe Uhrzeit
const nextDay = new Date(endTime);
nextDay.setDate(nextDay.getDate() + 1);
this.match.endDate = nextDay;
console.log('✅ Endzeit wurde auf nächsten Tag verschoben:', nextDay.toISOString());
}
}
},
setStartTime(timeString) {
@@ -1882,6 +2086,21 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
setEndTime(timeString) {
this.match.endDate = this.createDateTimeFromTimeString(timeString);
// Prüfe ob Endzeit vor Startzeit liegt und korrigiere automatisch
if (this.match.startDate && this.match.endDate) {
const startTime = new Date(this.match.startDate).getTime();
const endTime = new Date(this.match.endDate).getTime();
if (endTime <= startTime) {
console.log('⚠️ Endzeit liegt vor/auf Startzeit - korrigiere auf nächsten Tag...');
// Setze Endzeit auf nächsten Tag um dieselbe Uhrzeit
const nextDay = new Date(endTime);
nextDay.setDate(nextDay.getDate() + 1);
this.match.endDate = nextDay;
console.log('✅ Endzeit wurde auf nächsten Tag verschoben:', nextDay.toISOString());
}
}
},
getFormattedTime(dateValue) {
@@ -2494,16 +2713,50 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
},
// Hole aktuellen Spielmodus
// Hole aktuellen Spielmodus (für Validierungen)
getPlayThrough() {
// Suche nach playThrough für ALLGAMES-Validierung
const meetingPlayThrough = this.meetingDetails && this.meetingDetails.playThrough;
const dataPlayThrough = this.meetingData && this.meetingData.playThrough;
const playThrough = meetingPlayThrough || dataPlayThrough || '';
console.log(`🔍 getPlayThrough: meetingPlayThrough=${meetingPlayThrough}, dataPlayThrough=${dataPlayThrough}, result=${playThrough}`);
return playThrough;
},
// Hole aktuellen Spielmodus (für matrix/logic)
getPlayMode() {
return (this.meetingDetails && this.meetingDetails.playMode) ||
(this.meetingData && (this.meetingData.playMode || this.meetingData.matchSystem || this.meetingData.system)) ||
'';
const meetingPlayModeObj = this.meetingDetails && this.meetingDetails.playMode;
const dataMode = this.meetingData && (this.meetingData.playMode || this.meetingData.matchSystem || this.meetingData.system);
// Falls meetingPlayMode ein Objekt ist, extrahiere die richtige Eigenschaft
let meetingPlayMode = '';
if (meetingPlayModeObj) {
if (typeof meetingPlayModeObj === 'string') {
meetingPlayMode = meetingPlayModeObj;
} else if (meetingPlayModeObj.name) {
meetingPlayMode = meetingPlayModeObj.name;
} else if (meetingPlayModeObj.playMode) {
meetingPlayMode = meetingPlayModeObj.playMode;
} else {
// Debug: Objekt-Struktur analysieren
console.log('🔍 meetingPlayMode Objekt-Struktur:', Object.keys(meetingPlayModeObj));
meetingPlayMode = meetingPlayModeObj.toString();
}
}
const playMode = meetingPlayMode || dataMode || '';
console.log(`🔍 getPlayMode: meetingPlayModeObj=${meetingPlayModeObj}, extracted=${meetingPlayMode}, dataMode=${dataMode}, result=${playMode}`);
return playMode;
},
// Bestimme erforderliche Mindestspielerzahl für Spielmodus
getRequiredPlayerCount(playMode) {
const mode = (playMode || '').toLowerCase();
if (!playMode) {
console.log('⚠️ getRequiredPlayerCount: playMode ist undefined oder leer');
return 3; // Fallback-Wert
}
const mode = playMode.toLowerCase();
// 6er System: 4 Spieler erforderlich
if (mode.includes('6')) {
@@ -3897,6 +4150,22 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
text-align: center;
}
.validation-notice {
margin-top: 8px;
padding: 8px 12px;
background-color: #f8d7da;
border: 1px solid #f5c6cb;
border-radius: 4px;
color: #721c24;
font-size: 12px;
text-align: center;
font-weight: 500;
}
.validation-notice strong {
color: #721c24;
}
.time-input:disabled,
.protest-input:disabled,
.pin-input:disabled {