Füge Sortierfunktion für QTTR und TTR zur Rangliste hinzu und aktualisiere die Anzeige der Vereinsränge
This commit is contained in:
@@ -13,13 +13,39 @@
|
||||
|
||||
<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>
|
||||
<h2 class="text-xl font-semibold text-gray-900">
|
||||
Harheimer TC Rangliste
|
||||
</h2>
|
||||
<p class="text-sm text-gray-600">
|
||||
{{ data?.title || 'Andro-Rangliste' }} · {{ data?.rowCount || 0 }} Einträge
|
||||
</p>
|
||||
<div class="flex flex-wrap items-end gap-x-4 gap-y-3">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-gray-900">
|
||||
Harheimer TC Rangliste
|
||||
</h2>
|
||||
<p class="text-sm text-gray-600">
|
||||
{{ 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 class="text-sm text-gray-500">
|
||||
Aktualisiert: {{ formatDate(data?.importedAt) }}
|
||||
@@ -46,7 +72,7 @@
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<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 class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-gray-500">
|
||||
Spieler
|
||||
@@ -64,8 +90,8 @@
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 bg-white">
|
||||
<tr
|
||||
v-for="row in data?.rows || []"
|
||||
:key="`${row.rank}-${row.playerNumber || row.playerName}`"
|
||||
v-for="row in sortedRows"
|
||||
:key="row.playerNumber || row.playerName"
|
||||
:class="isOwnRow(row.playerName) ? 'bg-primary-100' : ''"
|
||||
>
|
||||
<td class="px-4 py-3 text-sm text-gray-600">
|
||||
@@ -97,7 +123,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
@@ -111,6 +137,34 @@ await authStore.checkAuth()
|
||||
const { data, pending, error } = await useFetch('/api/mitgliederbereich/qttr')
|
||||
|
||||
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) {
|
||||
return String(value || '').trim().toLowerCase().replace(/\s+/g, ' ')
|
||||
@@ -136,17 +190,17 @@ function isOwnRow(playerName) {
|
||||
}
|
||||
|
||||
function formatClubRank(row) {
|
||||
const qttrRank = row.clubQttrRank
|
||||
const ttrRank = row.clubTtrRank
|
||||
if (qttrRank == null) return '–'
|
||||
return ttrRank == null ? String(qttrRank) : `${qttrRank} (${ttrRank})`
|
||||
const activeRank = sortBy.value === 'qttr' ? row.clubQttrRank : row.clubTtrRank
|
||||
const otherRank = sortBy.value === 'qttr' ? row.clubTtrRank : row.clubQttrRank
|
||||
return `${activeRank ?? '–'} (${otherRank ?? '–'})`
|
||||
}
|
||||
|
||||
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}`
|
||||
const activeLabel = sortBy.value === 'qttr' ? 'QTTR' : 'TTR'
|
||||
const otherLabel = sortBy.value === 'qttr' ? 'TTR' : 'QTTR'
|
||||
const activeRank = sortBy.value === 'qttr' ? row.clubQttrRank : 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) {
|
||||
|
||||
Reference in New Issue
Block a user