+
+ Originalbericht zum Vergleich
+
+ Status: {{ getOriginalReportStatus() }}
+ Spielstand: {{ getOriginalReportScore() }}
+
+
+
+
+
+ | Spiel |
+ Heim |
+ Gast |
+ Sätze |
+ Ergebnis |
+ |
+
+
+
+
+ | {{ row.matchNumber }} |
+ {{ row.homeName || '—' }} |
+ {{ row.guestName || '—' }} |
+ {{ row.setsText || '—' }} |
+ {{ row.result }} |
+
+
+ |
+
+
+
+
+ Originaldaten anzeigen (technisch)
+ {{ formatTestData(originalTestReport || originalTestInput) }}
+
+
Aktuellen Zustand anzeigen
{{ formatTestData(buildStandaloneTestPayload()) }}
@@ -805,6 +841,9 @@ export default {
isTestMode: this.standaloneTest,
testVerification: null,
originalTestInput: null,
+ // Unveränderliche Referenz auf den offiziellen Bericht, bevor der
+ // Testmodus seinen vollständig leeren lokalen Zustand erzeugt.
+ originalTestReport: null,
standaloneTestSystem: 'braunschweiger system',
standaloneTestSystems: [
{ value: 'paarkreuz-system', label: 'Paarkreuz-System (6 gegen 6)' },
@@ -1231,7 +1270,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
});
},
async pollLatestMeetingDetails() {
- if (this.loading || this.isMeetingDetailsPolling) {
+ if (this.isTestMode || this.loading || this.isMeetingDetailsPolling) {
return;
}
@@ -2571,6 +2610,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
this.submitSucceeded = false;
this.initialCompletionState = false;
this.prepareResults();
+ this.captureOriginalTestReport();
},
resetStandaloneTest() {
@@ -2595,6 +2635,93 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
return JSON.stringify(data || {}, null, 2);
},
+ captureOriginalTestReport() {
+ // JSON-Kopie schützt die Vergleichsansicht vor allen lokalen
+ // Änderungen, die der Testmodus anschließend am Bericht vornimmt.
+ this.originalTestReport = JSON.parse(JSON.stringify({
+ meetingData: this.meetingData || {},
+ meetingDetails: this.meetingDetails || {},
+ results: this.results || []
+ }));
+ },
+
+ getOriginalReportRows() {
+ const details = this.originalTestReport?.meetingDetails || {};
+ const matches = Array.isArray(details.matches) ? details.matches : [];
+
+ return matches.map((match, index) => {
+ const sets = Array.from({ length: 5 }, (_, setIndex) => {
+ const home = Number(match?.[`set${setIndex + 1}A`] || 0);
+ const guest = Number(match?.[`set${setIndex + 1}B`] || 0);
+ return home > 0 || guest > 0 ? `${home}:${guest}` : '';
+ });
+ const homeWins = sets.filter(set => {
+ const [home, guest] = set.split(':').map(Number);
+ return home > guest;
+ }).length;
+ const guestWins = sets.filter(set => {
+ const [home, guest] = set.split(':').map(Number);
+ return guest > home;
+ }).length;
+ const homeName = this.formatMeetingMatchSide(match, 'home') || match?.homePlayer || '';
+ const guestName = this.formatMeetingMatchSide(match, 'guest') || match?.guestPlayer || '';
+
+ return {
+ matchNumber: match?.matchNr || index + 1,
+ index,
+ homeName,
+ guestName,
+ sets,
+ setsText: sets.filter(Boolean).join(', '),
+ hasSets: sets.some(Boolean),
+ result: homeWins || guestWins ? `${homeWins}:${guestWins}` : '—',
+ completed: match?.isCompleted === true || match?.matchesA === 1 || match?.matchesB === 1 || homeWins >= 3 || guestWins >= 3
+ };
+ });
+ },
+
+ getOriginalReportStatus() {
+ return this.originalTestReport?.meetingDetails?.isCompleted || this.originalTestReport?.meetingData?.isCompleted
+ ? 'Abgeschlossen'
+ : 'Offen';
+ },
+
+ getOriginalReportScore() {
+ const details = this.originalTestReport?.meetingDetails || {};
+ const home = details.homeMatches ?? this.originalTestReport?.meetingData?.homeMatches;
+ const guest = details.guestMatches ?? this.originalTestReport?.meetingData?.guestMatches;
+ if (Number.isFinite(Number(home)) && Number.isFinite(Number(guest))) {
+ return `${home}:${guest}`;
+ }
+
+ const calculatedScore = this.getOriginalReportRows().reduce((score, row) => {
+ if (!row.completed) return score;
+ const [home, guest] = row.result.split(':').map(Number);
+ if (home > guest) score.home += 1;
+ if (guest > home) score.guest += 1;
+ return score;
+ }, { home: 0, guest: 0 });
+ return `${calculatedScore.home}:${calculatedScore.guest}`;
+ },
+
+ copyOriginalPairingResults(row) {
+ if (!row?.hasSets) return;
+
+ const targetIndex = this.results.findIndex((_, index) => {
+ const formation = this.meetingDetails?.meetingPlayMode?.matchFormations?.[index];
+ return Number(formation?.matchNr || index + 1) === Number(row.matchNumber);
+ });
+ const localMatch = this.results[targetIndex >= 0 ? targetIndex : row.index];
+ if (!localMatch) return;
+
+ localMatch.sets = [...row.sets];
+ localMatch.completed = row.completed;
+ localMatch.result = row.result === '—' ? '' : (row.result === '3:0' || row.result === '3:1' || row.result === '3:2' ? '1:0' : '0:1');
+ this.calculateMatchResult(targetIndex >= 0 ? targetIndex : row.index);
+ this.errors[targetIndex >= 0 ? targetIndex : row.index] = null;
+ this.$forceUpdate();
+ },
+
async loadData() {
this.loading = true;
this.error = null;
@@ -3204,12 +3331,42 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
this.isDraftSyncing = false;
this.lastDraftSyncError = '';
- // Ein Test beginnt mit dem aktuellen offiziellen Stand, damit ein
- // alter lokaler Entwurf nicht versehentlich getestet wird.
+ // Den offiziellen Stand nur als Quelle für Teams, Aufstellung und
+ // Spielsystem laden. Ergebnisse, Abschluss und Signaturen dürfen
+ // im Testmodus nie übernommen werden.
await this.loadData();
- this.prepareResults();
- this.populateResultsFromMeetingDetails();
+ this.resetTestModeState();
+ },
+
+ async resetTestModeState() {
+ if (!this.meetingDetails || !this.meetingData) {
+ return;
+ }
+
+ this.captureOriginalTestReport();
+
+ [this.meetingDetails, this.meetingData].forEach((data) => {
+ data.isCompleted = false;
+ data.wo = null;
+ data.endDate = null;
+ data.signature = {};
+ });
+
+ // Die vom Server gespeicherten Ergebnisse komplett verwerfen. Die
+ // leeren Zeilen werden anschließend aus der Spielmatrix erzeugt.
+ this.meetingDetails.matches = [];
+ this.results = [];
+ this.teamNotAppeared = null;
+ this.isHomeLineupCertified = false;
+ this.isGuestLineupCertified = false;
+ this.isGreetingCompleted = false;
+ this.submitSucceeded = false;
+ this.initialCompletionState = false;
this.testVerification = null;
+ this.activeSection = 'general';
+
+ await this.$nextTick();
+ this.prepareResults();
},
/**
@@ -5024,6 +5181,84 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
font-size: 11px;
}
+.original-report-comparison {
+ border-color: rgba(33, 110, 57, 0.38) !important;
+ background: #f7fbf8 !important;
+}
+
+.comparison-summary {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px 18px;
+ margin: 10px 0;
+ color: #355044;
+ font-size: 13px;
+}
+
+.comparison-table-wrap {
+ overflow-x: auto;
+ border: 1px solid #dce8df;
+ border-radius: 6px;
+ background: #fff;
+}
+
+.comparison-table {
+ width: 100%;
+ min-width: 620px;
+ border-collapse: collapse;
+ font-size: 12px;
+}
+
+.comparison-table th,
+.comparison-table td {
+ padding: 7px 8px;
+ border-bottom: 1px solid #edf2ee;
+ text-align: left;
+ vertical-align: middle;
+}
+
+.comparison-table th {
+ color: #496052;
+ background: #edf6ef;
+ font-size: 11px;
+ letter-spacing: 0.02em;
+ text-transform: uppercase;
+}
+
+.comparison-table tbody tr:last-child td {
+ border-bottom: 0;
+}
+
+.comparison-sets {
+ color: #355044;
+ font-family: monospace;
+ white-space: nowrap;
+}
+
+.copy-original-btn {
+ padding: 5px 8px;
+ border: 1px solid #4f8d66;
+ border-radius: 5px;
+ background: #fff;
+ color: #216e39;
+ cursor: pointer;
+ font: inherit;
+ font-weight: 600;
+}
+
+.copy-original-btn:hover:not(:disabled),
+.copy-original-btn:focus-visible:not(:disabled) {
+ border-color: #216e39;
+ background: #e8f4eb;
+ outline: none;
+}
+
+.copy-original-btn:disabled {
+ border-color: #d5ddd7;
+ color: #8a958d;
+ cursor: not-allowed;
+}
+
.test-verification {
margin: -8px 0 16px;
padding: 12px 14px;