From e354d82969ce2e1de23a435410924f23d3b95937 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Fri, 3 Oct 2025 23:42:52 +0200 Subject: [PATCH] =?UTF-8?q?Erweitert=20den=20MatchReportApiDialog=20um=20u?= =?UTF-8?q?mfassende=20Validierungslogik=20f=C3=BCr=20die=20Eingabe=20von?= =?UTF-8?q?=20Spielberichten.=20Implementiert=20neue=20Warnungen=20f=C3=BC?= =?UTF-8?q?r=20unvollst=C3=A4ndige=20oder=20fehlerhafte=20Match-Ergebnisse?= =?UTF-8?q?=20sowie=20f=C3=BCr=20ung=C3=BCltige=20Start-=20und=20Endzeiten?= =?UTF-8?q?.=20Aktualisiert=20die=20Logik=20zur=20Aktivierung=20und=20Deak?= =?UTF-8?q?tivierung=20des=20Absende-Buttons=20basierend=20auf=20den=20Val?= =?UTF-8?q?idierungsbedingungen.=20Verbessert=20die=20Benutzeroberfl=C3=A4?= =?UTF-8?q?che=20mit=20neuen=20CSS-Stilen=20f=C3=BCr=20Validierungsbenachr?= =?UTF-8?q?ichtigungen=20und=20optimiert=20die=20Benutzererfahrung=20durch?= =?UTF-8?q?=20dynamische=20R=C3=BCckmeldungen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MatchReportApiDialog.vue | 305 ++++++++++++++++-- 1 file changed, 287 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/MatchReportApiDialog.vue b/frontend/src/components/MatchReportApiDialog.vue index 9895a32..e2e68dd 100644 --- a/frontend/src/components/MatchReportApiDialog.vue +++ b/frontend/src/components/MatchReportApiDialog.vue @@ -512,14 +512,34 @@ + +
⚠️ Dieser Spielbericht wurde bereits abgesendet. Keine weiteren Änderungen möglich.
+ + +
+ + ❌ Der Spielbericht kann nicht abgesendet werden. Es müssen alle Spiele abgeschlossen sein. +
Bitte schließen Sie alle {{ results.filter(match => !match.completed).length }} offenen Spiele ab! +
+ + ❌ Der Spielbericht kann nicht abgesendet werden. Es gibt noch Fehler in den Match-Ergebnissen. +
Bitte überprüfen Sie die Ergebniserfassung! +
+
+ + +
+ ❌ Der Spielbericht kann nicht abgesendet werden. Start- oder Endzeit sind nicht festgelegt. +
Bitte legen Sie beide Zeiten fest! +
@@ -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': [ - // Vierer–Vierer (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 {