feat(TTRPreview): add standalone TTR preview endpoint and enhance loading logic in MatchReportApiDialog
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 58s

This commit is contained in:
Torsten Schulz (local)
2026-10-28 10:14:33 +01:00
parent 6f4f865b4a
commit 9f349ed7b5
4 changed files with 72 additions and 30 deletions

View File

@@ -3337,33 +3337,65 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
await this.loadTtrPreview();
},
async loadTtrPreview() {
if (!this.canOpenTtrPreview || !this.match?.id || !this.meetingDetails) return;
if (!this.canOpenTtrPreview || !this.meetingDetails) return;
this.ttrLoading = true;
this.ttrError = '';
try {
if (this.standaloneTest) {
const response = await apiClient.post('/matches/ttr-preview/standalone', {
clubId: this.$store.getters.currentClub,
homePlayers: this.ttrLineupPlayers('home'),
guestPlayers: this.ttrLineupPlayers('guest'),
homeTeamName: this.meetingData?.homeTeamname,
guestTeamName: this.meetingData?.guestTeamname,
});
const resolvedByKey = new Map((response.data?.players || []).map((player) => [`${player.side}:${this.normalizeTtrName(player.name)}`, player]));
const players = ['home', 'guest'].flatMap((side) => this.ttrLineupPlayers(side).map((player, index) => ({
side,
name: [player.firstname || player.firstName, player.lastname || player.lastName].filter(Boolean).join(' '),
nuLigaPersonId: player.nuLigaPersonId || null,
...resolvedByKey.get(`${side}:${this.normalizeTtrName([player.firstname || player.firstName, player.lastname || player.lastName].filter(Boolean).join(' '))}`),
// Test-Aufstellungen sind Platzhalternamen. Nur nicht
// über die API auflösbare Spieler erhalten Testwerte.
ttr: Number.isFinite(Number(resolvedByKey.get(`${side}:${this.normalizeTtrName([player.firstname || player.firstName, player.lastname || player.lastName].filter(Boolean).join(' '))}`)?.ttr))
? Number(resolvedByKey.get(`${side}:${this.normalizeTtrName([player.firstname || player.firstName, player.lastname || player.lastName].filter(Boolean).join(' '))}`)?.ttr)
: (side === 'home' ? 1320 - index * 23 : 1345 - index * 19),
adjustmentConstant: resolvedByKey.get(`${side}:${this.normalizeTtrName([player.firstname || player.firstName, player.lastname || player.lastName].filter(Boolean).join(' '))}`)?.adjustmentConstant || 16,
source: resolvedByKey.get(`${side}:${this.normalizeTtrName([player.firstname || player.firstName, player.lastname || player.lastName].filter(Boolean).join(' '))}`)?.source || 'test',
})));
this.applyTtrPreviewPlayers(players);
return;
}
if (!this.match?.id) {
this.ttrError = 'Für diesen Spielbericht fehlt die gespeicherte Spiel-ID.';
return;
}
const response = await apiClient.post(`/matches/${this.match.id}/ttr-preview`, {
homePlayers: this.ttrLineupPlayers('home'),
guestPlayers: this.ttrLineupPlayers('guest'),
homeTeamName: this.meetingData?.homeTeamname,
guestTeamName: this.meetingData?.guestTeamname,
});
this.ttrPlayers = Array.isArray(response.data?.players) ? response.data.players : [];
const index = new Map(this.ttrPlayers.map((player) => [`${player.side}:${this.normalizeTtrName(player.name)}`, player]));
const snapshots = {};
this.results.forEach((match, resultIndex) => {
if (this.isTtrDouble(match)) return;
const home = index.get(`home:${this.normalizeTtrName(match.homeName)}`);
const guest = index.get(`guest:${this.normalizeTtrName(match.guestName)}`);
if (!Number.isFinite(Number(home?.ttr)) || !Number.isFinite(Number(guest?.ttr))) return;
snapshots[resultIndex] = { homeTtr: Number(home.ttr), guestTtr: Number(guest.ttr), homeAdjustmentConstant: Number(home.adjustmentConstant || 16), guestAdjustmentConstant: Number(guest.adjustmentConstant || 16) };
});
this.ttrSnapshots = snapshots;
this.applyTtrPreviewPlayers(Array.isArray(response.data?.players) ? response.data.players : []);
} catch (error) {
this.ttrError = error.response?.data?.error || 'TTR-Werte konnten nicht geladen werden.';
} finally {
this.ttrLoading = false;
}
},
applyTtrPreviewPlayers(players) {
this.ttrPlayers = players;
const index = new Map(this.ttrPlayers.map((player) => [`${player.side}:${this.normalizeTtrName(player.name)}`, player]));
const snapshots = {};
this.results.forEach((match, resultIndex) => {
if (this.isTtrDouble(match)) return;
const home = index.get(`home:${this.normalizeTtrName(match.homeName)}`);
const guest = index.get(`guest:${this.normalizeTtrName(match.guestName)}`);
if (!Number.isFinite(Number(home?.ttr)) || !Number.isFinite(Number(guest?.ttr))) return;
snapshots[resultIndex] = { homeTtr: Number(home.ttr), guestTtr: Number(guest.ttr), homeAdjustmentConstant: Number(home.adjustmentConstant || 16), guestAdjustmentConstant: Number(guest.adjustmentConstant || 16) };
});
this.ttrSnapshots = snapshots;
},
isTtrDouble(match) {
const label = String(match?.label || '').toUpperCase();
return label.includes('D') || String(match?.homeName || '').includes(' / ') || String(match?.guestName || '').includes(' / ');

View File

@@ -2999,7 +2999,10 @@ export default {
this.fetchingTeamData = true;
try {
const response = await apiClient.post('/scheduler/match_results', {
// A manual click must not depend on the scheduler's
// autoUpdateRatings/savePassword flags. This endpoint fetches
// precisely the selected ClubTeam and persists its results.
const response = await apiClient.post('/mytischtennis/fetch-team-data', {
clubTeamId: this.selectedTeam.id
});
const result = response?.data;
@@ -3014,6 +3017,7 @@ export default {
}
const fetchedCount =
result?.data?.fetchedCount ??
result?.result?.totalFetched ??
result?.result?.fetchedCount ??
result?.result?.data?.fetchedCount ??