Füge Sortierfunktion für QTTR und TTR zur Rangliste hinzu und aktualisiere die Anzeige der Vereinsränge
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 5m37s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 3m5s

This commit is contained in:
Torsten Schulz (local)
2026-09-24 11:26:49 +02:00
parent a9ce55699e
commit d3e6fbf49b

View File

@@ -13,13 +13,39 @@
<div class="bg-white rounded-xl shadow-lg border border-gray-100 p-6"> <div class="bg-white rounded-xl shadow-lg border border-gray-100 p-6">
<div class="flex flex-wrap items-center gap-4 justify-between mb-4"> <div class="flex flex-wrap items-center gap-4 justify-between mb-4">
<div> <div class="flex flex-wrap items-end gap-x-4 gap-y-3">
<h2 class="text-xl font-semibold text-gray-900"> <div>
Harheimer TC Rangliste <h2 class="text-xl font-semibold text-gray-900">
</h2> Harheimer TC Rangliste
<p class="text-sm text-gray-600"> </h2>
{{ data?.title || 'Andro-Rangliste' }} · {{ data?.rowCount || 0 }} Einträge <p class="text-sm text-gray-600">
</p> {{ data?.title || 'Andro-Rangliste' }} · {{ data?.rowCount || 0 }} Einträge
</p>
</div>
<div
class="inline-flex rounded-md border border-gray-200 bg-gray-50 p-0.5 shadow-sm"
role="group"
aria-label="Rangliste sortieren nach"
>
<button
type="button"
class="rounded px-3 py-1.5 text-sm font-semibold transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600 focus-visible:ring-offset-2"
:class="sortBy === 'qttr' ? 'bg-primary-600 text-white shadow-sm' : 'text-gray-700 hover:bg-white hover:text-primary-700'"
:aria-pressed="sortBy === 'qttr'"
@click="sortBy = 'qttr'"
>
QTTR
</button>
<button
type="button"
class="rounded px-3 py-1.5 text-sm font-semibold transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600 focus-visible:ring-offset-2"
:class="sortBy === 'ttr' ? 'bg-primary-600 text-white shadow-sm' : 'text-gray-700 hover:bg-white hover:text-primary-700'"
:aria-pressed="sortBy === 'ttr'"
@click="sortBy = 'ttr'"
>
TTR
</button>
</div>
</div> </div>
<div class="text-sm text-gray-500"> <div class="text-sm text-gray-500">
Aktualisiert: {{ formatDate(data?.importedAt) }} Aktualisiert: {{ formatDate(data?.importedAt) }}
@@ -46,7 +72,7 @@
<thead class="bg-gray-50"> <thead class="bg-gray-50">
<tr> <tr>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-gray-500"> <th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-gray-500">
Vereinsrang<br>(QTTR / TTR) Vereinsrang<br>{{ sortBy === 'qttr' ? '(QTTR / TTR)' : '(TTR / QTTR)' }}
</th> </th>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-gray-500"> <th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-gray-500">
Spieler Spieler
@@ -64,8 +90,8 @@
</thead> </thead>
<tbody class="divide-y divide-gray-100 bg-white"> <tbody class="divide-y divide-gray-100 bg-white">
<tr <tr
v-for="row in data?.rows || []" v-for="row in sortedRows"
:key="`${row.rank}-${row.playerNumber || row.playerName}`" :key="row.playerNumber || row.playerName"
:class="isOwnRow(row.playerName) ? 'bg-primary-100' : ''" :class="isOwnRow(row.playerName) ? 'bg-primary-100' : ''"
> >
<td class="px-4 py-3 text-sm text-gray-600"> <td class="px-4 py-3 text-sm text-gray-600">
@@ -97,7 +123,7 @@
</template> </template>
<script setup> <script setup>
import { computed } from 'vue' import { computed, ref } from 'vue'
const authStore = useAuthStore() const authStore = useAuthStore()
@@ -111,6 +137,34 @@ await authStore.checkAuth()
const { data, pending, error } = await useFetch('/api/mitgliederbereich/qttr') const { data, pending, error } = await useFetch('/api/mitgliederbereich/qttr')
const currentUserName = computed(() => authStore.user?.name?.trim() || '') const currentUserName = computed(() => authStore.user?.name?.trim() || '')
const sortBy = ref('qttr')
const sortedRows = computed(() => {
const rows = [...(data.value?.rows || [])]
const byName = (a, b) => String(a.playerName || '').localeCompare(String(b.playerName || ''), 'de')
const byRating = (key) => (a, b) => b[key] - a[key] || byName(a, b)
if (sortBy.value === 'ttr') {
return rows.sort((a, b) => {
const aMissing = a.currentTtr == null
const bMissing = b.currentTtr == null
if (aMissing !== bMissing) return aMissing ? 1 : -1
return aMissing ? byName(a, b) : byRating('currentTtr')(a, b)
})
}
return rows.sort((a, b) => {
const aMissing = a.currentQttr == null
const bMissing = b.currentQttr == null
if (aMissing !== bMissing) return aMissing ? 1 : -1
if (!aMissing) return byRating('currentQttr')(a, b)
const aMissingTtr = a.currentTtr == null
const bMissingTtr = b.currentTtr == null
if (aMissingTtr !== bMissingTtr) return aMissingTtr ? 1 : -1
return aMissingTtr ? byName(a, b) : byRating('currentTtr')(a, b)
})
})
function normalizeName(value) { function normalizeName(value) {
return String(value || '').trim().toLowerCase().replace(/\s+/g, ' ') return String(value || '').trim().toLowerCase().replace(/\s+/g, ' ')
@@ -136,17 +190,17 @@ function isOwnRow(playerName) {
} }
function formatClubRank(row) { function formatClubRank(row) {
const qttrRank = row.clubQttrRank const activeRank = sortBy.value === 'qttr' ? row.clubQttrRank : row.clubTtrRank
const ttrRank = row.clubTtrRank const otherRank = sortBy.value === 'qttr' ? row.clubTtrRank : row.clubQttrRank
if (qttrRank == null) return '–' return `${activeRank ?? '–'} (${otherRank ?? '–'})`
return ttrRank == null ? String(qttrRank) : `${qttrRank} (${ttrRank})`
} }
function clubRankTitle(row) { function clubRankTitle(row) {
if (row.clubQttrRank == null) return 'Keine QTTR-Platzierung verfügbar' const activeLabel = sortBy.value === 'qttr' ? 'QTTR' : 'TTR'
return row.clubTtrRank == null const otherLabel = sortBy.value === 'qttr' ? 'TTR' : 'QTTR'
? `QTTR-Platzierung im Verein: ${row.clubQttrRank}` const activeRank = sortBy.value === 'qttr' ? row.clubQttrRank : row.clubTtrRank
: `QTTR-Platzierung im Verein: ${row.clubQttrRank}; TTR-Platzierung im Verein: ${row.clubTtrRank}` const otherRank = sortBy.value === 'qttr' ? row.clubTtrRank : row.clubQttrRank
return `${activeLabel}-Platzierung im Verein: ${activeRank ?? 'nicht verfügbar'}; ${otherLabel}-Platzierung im Verein: ${otherRank ?? 'nicht verfügbar'}`
} }
function getPlayerNameClass(row) { function getPlayerNameClass(row) {