From 1a7ba583d8b4d246aa8cba1ef224fd178d7af526 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 23 Sep 2026 16:00:06 +0200 Subject: [PATCH] =?UTF-8?q?Aktualisiere=20die=20Version=20auf=201.8.10=20u?= =?UTF-8?q?nd=20f=C3=BCge=20Vereinsrang=20(QTTR=20/=20TTR)=20zur=20QTTR-Ta?= =?UTF-8?q?belle=20hinzu.=20Implementiere=20die=20Logik=20zur=20Berechnung?= =?UTF-8?q?=20und=20Sortierung=20der=20Vereinsr=C3=A4nge=20in=20den=20API-?= =?UTF-8?q?=20und=20Import-Skripten.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- pages/mitgliederbereich/qttr.vue | 20 +++++++++++++++-- server/api/mitgliederbereich/qttr.get.js | 28 ++++++++++++++++++++---- server/utils/qttr-import.js | 24 +++++++++++++++++++- 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index dd657bc..d20fa95 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "harheimertc-website", - "version": "1.8.9", + "version": "1.8.10", "description": "Moderne Webseite für den Harheimer Tischtennis Club", "private": true, "type": "module", diff --git a/pages/mitgliederbereich/qttr.vue b/pages/mitgliederbereich/qttr.vue index ae3b5d0..d9e5f97 100755 --- a/pages/mitgliederbereich/qttr.vue +++ b/pages/mitgliederbereich/qttr.vue @@ -46,7 +46,7 @@ - Rang + Vereinsrang
(QTTR / TTR) Spieler @@ -69,7 +69,9 @@ :class="isOwnRow(row.playerName) ? 'bg-primary-100' : ''" > - {{ row.rank ?? '–' }} + + {{ formatClubRank(row) }} +
@@ -133,6 +135,20 @@ function isOwnRow(playerName) { return normalizeName(playerName) === current } +function formatClubRank(row) { + const qttrRank = row.clubQttrRank + const ttrRank = row.clubTtrRank + if (qttrRank == null) return '–' + return ttrRank == null ? String(qttrRank) : `${qttrRank} (${ttrRank})` +} + +function clubRankTitle(row) { + if (row.clubQttrRank == null) return 'Keine QTTR-Platzierung verfügbar' + return row.clubTtrRank == null + ? `QTTR-Platzierung im Verein: ${row.clubQttrRank}` + : `QTTR-Platzierung im Verein: ${row.clubQttrRank}; TTR-Platzierung im Verein: ${row.clubTtrRank}` +} + function getPlayerNameClass(row) { const minor = isMinor(row.birthdate) if (minor && isMaleGender(row.gender)) return 'text-blue-400' diff --git a/server/api/mitgliederbereich/qttr.get.js b/server/api/mitgliederbereich/qttr.get.js index 85ad60c..f9b4900 100755 --- a/server/api/mitgliederbereich/qttr.get.js +++ b/server/api/mitgliederbereich/qttr.get.js @@ -37,6 +37,26 @@ function buildBirthdateLookup(entries) { return lookup } +function addClubRanks(rows) { + const addRankFor = (valueKey, rankKey) => { + rows + .filter((row) => row[valueKey] != null) + .sort((a, b) => b[valueKey] - a[valueKey] || String(a.playerName || '').localeCompare(String(b.playerName || ''), 'de')) + .forEach((row, index) => { + row[rankKey] = index + 1 + }) + } + + addRankFor('currentQttr', 'clubQttrRank') + addRankFor('currentTtr', 'clubTtrRank') + + return rows.sort((a, b) => + (a.clubQttrRank ?? Number.POSITIVE_INFINITY) - (b.clubQttrRank ?? Number.POSITIVE_INFINITY) + || (a.clubTtrRank ?? Number.POSITIVE_INFINITY) - (b.clubTtrRank ?? Number.POSITIVE_INFINITY) + || String(a.playerName || '').localeCompare(String(b.playerName || ''), 'de') + ) +} + export default defineEventHandler(async (event) => { const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '') if (!token || !verifyToken(token)) { @@ -73,16 +93,16 @@ export default defineEventHandler(async (event) => { ].flatMap(entry => [entry?.name, `${entry?.firstName || ''} ${entry?.lastName || ''}`.trim()]).map(normalizeName).filter(Boolean)) const birthdateLookup = buildBirthdateLookup([...visibleManualMembers, ...visibleUsers]) + const rankedRows = addClubRanks(Array.isArray(payload.rows) ? payload.rows.map(row => ({ ...row })) : []) + return { ...payload, - rows: Array.isArray(payload.rows) - ? payload.rows + rows: rankedRows .filter(row => !hiddenNames.has(normalizeName(row.playerName))) .map((row) => ({ ...row, birthdate: birthdateLookup.get(normalizeName(row.playerName)) || row.birthdate || '' })) - : [] } } catch (error) { if (error?.code === 'ENOENT') { @@ -98,4 +118,4 @@ export default defineEventHandler(async (event) => { message: 'Fehler beim Laden der QTTR-Werte.' }) } -}) \ No newline at end of file +}) diff --git a/server/utils/qttr-import.js b/server/utils/qttr-import.js index 96ee9ab..d0ee057 100755 --- a/server/utils/qttr-import.js +++ b/server/utils/qttr-import.js @@ -67,6 +67,26 @@ function normalizeGender(value) { return normalized || null } +function addClubRanks(rows) { + const addRankFor = (valueKey, rankKey) => { + rows + .filter((row) => row[valueKey] != null) + .sort((a, b) => b[valueKey] - a[valueKey] || String(a.playerName || '').localeCompare(String(b.playerName || ''), 'de')) + .forEach((row, index) => { + row[rankKey] = index + 1 + }) + } + + addRankFor('currentQttr', 'clubQttrRank') + addRankFor('currentTtr', 'clubTtrRank') + + return rows.sort((a, b) => + (a.clubQttrRank ?? Number.POSITIVE_INFINITY) - (b.clubQttrRank ?? Number.POSITIVE_INFINITY) + || (a.clubTtrRank ?? Number.POSITIVE_INFINITY) - (b.clubTtrRank ?? Number.POSITIVE_INFINITY) + || String(a.playerName || '').localeCompare(String(b.playerName || ''), 'de') + ) +} + function extractTableBlocks(html) { return [...String(html || '').matchAll(/]*>[\s\S]*?<\/table>/gi)].map((match) => match[0]) } @@ -190,7 +210,7 @@ export async function importQttrValues(options = {}) { throw new Error('QTTR-Tabelle ist leer oder unvollständig') } - const parsedRows = rows.map((cells) => deriveQttrFields(headers, cells)) + const parsedRows = addClubRanks(rows.map((cells) => deriveQttrFields(headers, cells))) const payload = { format: 'harheimertc.qttr.v1', importedAt: new Date().toISOString(), @@ -243,6 +263,8 @@ async function importAuthenticatedTtrValues(connection) { }) }).filter(row => row.playerName && (row.currentTtr != null || row.currentQttr != null)) + addClubRanks(rows) + if (!rows.length) throw new Error('Keine verwertbaren TTR-Werte in der myTischtennis-Rangliste gefunden.') const previousQttrSignature = qttrListSignature(previousPayload.rows) const nextQttrSignature = qttrListSignature(rows)