diff --git a/backend/clients/myTischtennisClient.js b/backend/clients/myTischtennisClient.js index a7d9ad4..9b2206c 100644 --- a/backend/clients/myTischtennisClient.js +++ b/backend/clients/myTischtennisClient.js @@ -190,14 +190,14 @@ class MyTischtennisClient { * @param {string} fedNickname - Federation nickname (e.g., "HeTTV") * @returns {Promise} Rankings with player entries (all pages) */ - async getClubRankings(cookie, clubId, fedNickname) { + async getClubRankings(cookie, clubId, fedNickname, currentRanking = 'yes') { const allEntries = []; let currentPage = 0; let hasMorePages = true; while (hasMorePages) { - const endpoint = `/rankings/andro-rangliste?all-players=on&clubnr=${clubId}&fednickname=${fedNickname}&results-per-page=100&page=${currentPage}&_data=routes%2F%24`; + const endpoint = `/rankings/andro-rangliste?all-players=on&clubnr=${clubId}&fednickname=${fedNickname}¤t-ranking=${currentRanking}&results-per-page=100&page=${currentPage}&_data=routes%2F%24`; const result = await this.authenticatedRequest(endpoint, cookie, { diff --git a/backend/services/memberService.js b/backend/services/memberService.js index 10dab03..10af219 100644 --- a/backend/services/memberService.js +++ b/backend/services/memberService.js @@ -219,28 +219,42 @@ class MemberService { }; } - // 2. Rangliste vom Verein abrufen - const rankings = await myTischtennisClient.getClubRankings( + // 2. Ranglisten vom Verein abrufen + // TTR (aktuell) + const rankingsCurrent = await myTischtennisClient.getClubRankings( session.cookie, account.clubId, - account.fedNickname + account.fedNickname, + 'yes' + ); + + // QTTR (Quartalswert) + const rankingsQuarter = await myTischtennisClient.getClubRankings( + session.cookie, + account.clubId, + account.fedNickname, + 'no' ); - if (!rankings.success) { + if (!rankingsCurrent.success) { return { status: 500, response: { - message: rankings.error || 'Fehler beim Abrufen der Rangliste', + message: rankingsCurrent.error || 'Fehler beim Abrufen der Rangliste (aktuell)', updated: 0, errors: [], debug: { clubId: account.clubId, fedNickname: account.fedNickname, - rankingsError: rankings.error + rankingsError: rankingsCurrent.error } } }; } + if (!rankingsQuarter.success) { + // QTTR optional; nicht hart abbrechen, aber vermerken + console.warn('[updateRatingsFromMyTischtennis] - QTTR Abruf fehlgeschlagen:', rankingsQuarter.error); + } // 3. Alle Mitglieder des Clubs laden const members = await Member.findAll({ where: { clubId } }); @@ -250,29 +264,45 @@ class MemberService { const notFound = []; const matched = []; - // 4. Für jedes Mitglied TTR aktualisieren + // Maps für schnelleres Matching + const mapByName = (entries) => { + const m = new Map(); + for (const e of (entries || [])) { + const key = `${(e.firstname||'').toLowerCase()}|${(e.lastname||'').toLowerCase()}`; + if (!m.has(key)) m.set(key, e); + } + return m; + }; + + const currentMap = mapByName(rankingsCurrent.entries); + const quarterMap = rankingsQuarter.success ? mapByName(rankingsQuarter.entries) : new Map(); + + // 4. Für jedes Mitglied TTR und QTTR aktualisieren for (const member of members) { const firstName = member.firstName; const lastName = member.lastName; - - // Suche nach Match in rankings entries - const rankingEntry = rankings.entries.find(entry => - entry.firstname.toLowerCase() === firstName.toLowerCase() && - entry.lastname.toLowerCase() === lastName.toLowerCase() - ); - - if (rankingEntry) { + const key = `${(firstName||'').toLowerCase()}|${(lastName||'').toLowerCase()}`; + const rankingEntry = currentMap.get(key); + const rankingQuarterEntry = quarterMap.get(key); + + if (rankingEntry || rankingQuarterEntry) { try { - // fedRank ist der TTR-Wert const oldTtr = member.ttr; - member.ttr = rankingEntry.fedRank; - // TODO: QTTR muss von einem anderen Endpoint geholt werden + const oldQttr = member.qttr; + if (rankingEntry && typeof rankingEntry.fedRank === 'number') { + member.ttr = rankingEntry.fedRank; + } + if (rankingQuarterEntry && typeof rankingQuarterEntry.fedRank === 'number') { + member.qttr = rankingQuarterEntry.fedRank; + } await member.save(); updated++; matched.push({ name: `${firstName} ${lastName}`, oldTtr: oldTtr, - newTtr: rankingEntry.fedRank + newTtr: member.ttr, + oldQttr: oldQttr, + newQttr: member.qttr }); } catch (error) { console.error(`[updateRatingsFromMyTischtennis] - Error updating ${firstName} ${lastName}:`, error); @@ -304,7 +334,7 @@ class MemberService { matched: matched, notFound: notFound, errors: errors, - totalEntries: rankings.entries.length, + totalEntries: rankingsCurrent.entries.length, totalMembers: members.length } }; diff --git a/frontend/src/views/TeamManagementView.vue b/frontend/src/views/TeamManagementView.vue index 96c58d3..c141c5c 100644 --- a/frontend/src/views/TeamManagementView.vue +++ b/frontend/src/views/TeamManagementView.vue @@ -67,6 +67,7 @@ Spieler + (Q)TTR Saison {{ isSecondHalf ? 'Rückrunde' : 'Vorrunde' }} @@ -76,6 +77,12 @@ {{ stat.firstName }} {{ stat.lastName }} + + + {{ memberById[stat.memberId].qttr ?? memberById[stat.memberId].ttr ?? '–' }} + + + {{ stat.totalSeason }} {{ isSecondHalf ? stat.totalSecondHalf : stat.totalFirstHalf }} @@ -392,6 +399,7 @@ export default { // Player Stats const playerStats = ref([]); const loadingStats = ref(false); + const memberById = ref({}); // Computed const selectedClub = computed(() => store.state.currentClub); @@ -1012,6 +1020,18 @@ export default { loadingStats.value = true; try { + // Mitglieder mit (Q)TTR laden, um Werte anzuzeigen + try { + const membersResp = await apiClient.get(`/clubmembers/get/${selectedClub.value}/true`); + const map = {}; + for (const m of membersResp.data || []) { + map[m.id] = { ttr: m.ttr ?? null, qttr: m.qttr ?? null }; + } + memberById.value = map; + } catch (e) { + memberById.value = {}; + } + const response = await apiClient.get( `/matches/leagues/${selectedClub.value}/stats/${teamToEdit.value.leagueId}`, {