Files
harheimertc/pages/mannschaften/spielplaene.vue
Torsten Schulz (local) 8043916129
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 46s
Implement CSV fetching utility across components for improved data handling
This commit introduces a new utility function, fetchCsvText, to streamline the fetching of CSV data across multiple components. The function includes a cache-busting mechanism and retry logic to enhance reliability when retrieving data from the server. This change improves error handling and ensures consistent data retrieval in the Mannschaften overview, detail, and schedule pages, contributing to a more robust application.
2026-01-18 23:40:59 +01:00

777 lines
26 KiB
Vue

<template>
<div class="min-h-screen bg-gray-50">
<!-- Header -->
<div class="bg-white shadow-sm">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="flex items-center justify-between">
<div>
<h1 class="text-3xl font-bold text-gray-900">
Spielpläne
</h1>
<p class="mt-2 text-gray-600">
Alle Spielpläne der Mannschaften
</p>
</div>
</div>
</div>
</div>
<!-- Content -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Filter and Download Section -->
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<!-- Filter Selection -->
<div class="flex flex-col sm:flex-row sm:items-center gap-4">
<!-- Wettbewerbs-Filter -->
<div class="flex items-center space-x-2">
<label
for="wettbewerb-select"
class="text-sm font-medium text-gray-700"
>
Wettbewerb:
</label>
<select
id="wettbewerb-select"
v-model="selectedWettbewerb"
class="px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white text-sm"
@change="filterData"
>
<option value="punktrunde">
Punktrunde
</option>
<option value="pokal">
Pokal
</option>
<option value="alle">
Alle
</option>
</select>
</div>
<!-- Mannschafts-Filter -->
<div class="flex items-center space-x-2">
<label
for="filter-select"
class="text-sm font-medium text-gray-700"
>
Mannschaft:
</label>
<select
id="filter-select"
v-model="selectedFilter"
class="px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white text-sm"
@change="filterData"
>
<option value="all">
Gesamt
</option>
<option value="erwachsene">
Erwachsene
</option>
<option value="nachwuchs">
Nachwuchs
</option>
<option
v-for="mannschaft in mannschaften"
:key="mannschaft"
:value="mannschaft"
>
{{ mannschaft }}
</option>
</select>
</div>
</div>
<!-- Download Button -->
<button
:disabled="isLoading || !filteredData.length"
class="inline-flex items-center px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors disabled:bg-gray-400"
@click="downloadPDF"
>
<svg
class="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
PDF Download
</button>
</div>
<!-- Filter Info -->
<div class="mt-4 text-sm text-gray-600">
<span v-if="selectedFilter === 'all'">
{{ getWettbewerbText() }} - Alle Mannschaften ({{ filteredData.length }} von {{ spielplanData.length }} Einträgen)
</span>
<span v-else-if="selectedFilter === 'erwachsene'">
{{ getWettbewerbText() }} - Erwachsenen-Mannschaften ({{ filteredData.length }} von {{ spielplanData.length }} Einträgen)
</span>
<span v-else-if="selectedFilter === 'nachwuchs'">
{{ getWettbewerbText() }} - Nachwuchs-Mannschaften ({{ filteredData.length }} von {{ spielplanData.length }} Einträgen)
</span>
<span v-else>
{{ getWettbewerbText() }} - {{ selectedFilter }} ({{ filteredData.length }} von {{ spielplanData.length }} Einträgen)
</span>
</div>
</div>
<!-- Loading State -->
<div
v-if="isLoading"
class="text-center py-12"
>
<svg
class="w-8 h-8 text-gray-400 mx-auto mb-4 animate-spin"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
<p class="text-gray-600">
Spielpläne werden geladen...
</p>
</div>
<!-- Error State -->
<div
v-else-if="error"
class="bg-red-50 border border-red-200 rounded-lg p-6 text-center"
>
<svg
class="w-12 h-12 text-red-400 mx-auto mb-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
/>
</svg>
<h3 class="text-lg font-medium text-red-800 mb-2">
Fehler beim Laden
</h3>
<p class="text-red-600 mb-4">
{{ error }}
</p>
<button
class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
@click="loadData"
>
Erneut versuchen
</button>
</div>
<!-- Empty State -->
<div
v-else-if="!spielplanData || spielplanData.length === 0"
class="text-center py-12 bg-white rounded-xl shadow-lg"
>
<svg
class="w-12 h-12 text-gray-400 mx-auto mb-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
/>
</svg>
<h3 class="text-lg font-medium text-gray-900 mb-2">
Keine Spielpläne verfügbar
</h3>
<p class="text-gray-600">
Es wurden noch keine Spielplandaten hochgeladen.
</p>
</div>
<!-- Spielplan Table -->
<div
v-else
class="bg-white rounded-xl shadow-lg overflow-hidden"
>
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-xl font-semibold text-gray-900">
Spielplan
</h2>
<p class="text-sm text-gray-600 mt-1">
{{ getWettbewerbText() }} - {{ filteredData.length }} von {{ spielplanData.length }} Einträgen
</p>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th
v-for="header in headers"
:key="header"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{{ formatHeader(header) }}
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr
v-for="(row, index) in filteredData"
:key="index"
:class="getRowClass(row)"
>
<td
v-for="header in headers"
:key="header"
class="px-6 py-4 whitespace-nowrap text-sm text-gray-900"
>
<span
v-if="header.toLowerCase().includes('datum')"
class="font-mono"
>
{{ formatDate(row[getOriginalHeader(header)]) }}
</span>
<span
v-else-if="header.toLowerCase().includes('uhrzeit')"
class="font-mono"
>
{{ formatTime(row[getOriginalHeader(header)]) }}
</span>
<span
v-else-if="header.toLowerCase().includes('mannschaft')"
class="font-medium"
>
{{ row[getOriginalHeader(header)] || '-' }}
</span>
<span v-else-if="header.toLowerCase().includes('runde')">
{{ formatRunde(row[getOriginalHeader(header)]) }}
</span>
<span v-else>
{{ row[getOriginalHeader(header)] || '-' }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="px-6 py-4 bg-gray-50 border-t border-gray-200">
<p class="text-xs text-gray-500">
Letzte Aktualisierung: {{ lastUpdated }}
</p>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
useHead({
title: 'Spielpläne - Mannschaften - Harheimer TC'
})
const spielplanData = ref([])
const headers = ref([])
const isLoading = ref(false)
const error = ref(null)
const lastUpdated = ref('')
const selectedFilter = ref('all')
const selectedWettbewerb = ref('punktrunde')
const filteredData = ref([])
const mannschaften = ref([])
async function fetchCsvText(url) {
const attempt = async () => {
const withBuster = `${url}${url.includes('?') ? '&' : '?'}_t=${Date.now()}`
const res = await fetch(withBuster, { cache: 'no-store' })
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`)
return await res.text()
}
try {
return await attempt()
} catch (_e) {
// 1 Retry: hilft bei kurzen Restarts/Proxy-Resets (Firefox: NS_ERROR_NET_PARTIAL_TRANSFER)
await new Promise(resolve => setTimeout(resolve, 150))
return await attempt()
}
}
const loadData = async () => {
isLoading.value = true
error.value = null
try {
// Lade Spielplandaten und Mannschaften parallel
const [spielplanResponse, mannschaftenResponse] = await Promise.all([
fetch('/api/spielplan'),
fetchCsvText('/data/mannschaften.csv')
])
// Spielplandaten verarbeiten
const spielplanResult = await spielplanResponse.json()
if (spielplanResult.success) {
spielplanData.value = spielplanResult.data
// Nur die gewünschten Spalten anzeigen
const originalHeaders = spielplanResult.headers
const desiredHeaders = ['Termin', 'HeimMannschaft', 'GastMannschaft', 'Runde', 'Staffel']
// Finde die Indizes der gewünschten Spalten
const headerIndices = desiredHeaders.map(desiredHeader => {
return originalHeaders.findIndex(header =>
header.toLowerCase() === desiredHeader.toLowerCase()
)
}).filter(index => index !== -1)
// Filtere nur die gewünschten Spalten
const filteredHeaders = headerIndices.map(index => originalHeaders[index])
// Benenne "Staffel" in "Gruppe" um (nur für Anzeige)
headers.value = filteredHeaders.map(header => {
if (header.toLowerCase().includes('staffel')) {
return 'Gruppe'
}
return header
})
// Erstelle Mapping für Daten-Zugriff
const headerMapping = {}
headers.value.forEach((displayHeader, index) => {
headerMapping[displayHeader] = filteredHeaders[index]
})
// Speichere Mapping global für Zugriff in Template
window.spielplanHeaderMapping = headerMapping
lastUpdated.value = new Date().toLocaleString('de-DE')
} else {
error.value = spielplanResult.message
}
// Mannschaften aus CMS laden (manuell eingegebene Mannschaften)
if (mannschaftenResponse) {
const csvText = mannschaftenResponse
const lines = csvText.split('\n').filter(line => line.trim() !== '')
if (lines.length > 1) {
mannschaften.value = lines.slice(1).map(line => {
// Besserer CSV-Parser: Respektiert Anführungszeichen
const values = []
let current = ''
let inQuotes = false
for (let i = 0; i < line.length; i++) {
const char = line[i]
if (char === '"') {
inQuotes = !inQuotes
} else if (char === ',' && !inQuotes) {
values.push(current.trim())
current = ''
} else {
current += char
}
}
values.push(current.trim())
return values[0] // Erste Spalte ist der Mannschaftsname
}).filter(name => name && name !== '')
}
}
filterData() // Initial filter
} catch (err) {
console.error('Fehler beim Laden der Daten:', err)
error.value = 'Fehler beim Laden der Daten'
} finally {
isLoading.value = false
}
}
const filterData = () => {
if (!spielplanData.value || spielplanData.value.length === 0) {
filteredData.value = []
return
}
// Zuerst nach aktueller Saison filtern (immer aktiv)
// Da die Spiele bis 2026 gehen, nehmen wir die Saison 2025/26
// Saison läuft vom 01.07. bis 30.06. des Folgejahres
const saisonStartYear = 2025
const saisonEndYear = 2026
const saisonStart = new Date(saisonStartYear, 6, 1) // 01.07.2025
const saisonEnd = new Date(saisonEndYear, 5, 30) // 30.06.2026
let saisonFiltered = spielplanData.value.filter(row => {
const termin = row.Termin
if (!termin) return false
try {
// Parse deutsches Datumsformat: "27.10.2025 20:00"
let spielDatum
if (termin.includes(' ')) {
// Uhrzeit entfernen: "27.10.2025 20:00" -> "27.10.2025"
const datumTeil = termin.split(' ')[0]
// Deutsches Format parsen: "27.10.2025" -> Date
const [tag, monat, jahr] = datumTeil.split('.')
spielDatum = new Date(jahr, monat - 1, tag) // Monat ist 0-basiert
} else {
spielDatum = new Date(termin)
}
if (isNaN(spielDatum.getTime())) return false
// Prüfe ob das Spiel in der aktuellen Saison liegt
const inSaison = spielDatum >= saisonStart && spielDatum <= saisonEnd
return inSaison
} catch (_error) {
console.error('Fehler beim Parsen von Termin:', termin, error)
return false
}
})
// Dann nach Wettbewerb filtern
let wettbewerbFiltered = saisonFiltered
if (selectedWettbewerb.value === 'punktrunde') {
wettbewerbFiltered = saisonFiltered.filter(row => {
// Filtere nach Punktrunde-Spielen (VR = Vorrunde, RR = Rückrunde)
const runde = (row.Runde || '').toLowerCase()
const isMatch = runde === 'vr' || runde === 'rr' || runde.includes('vorrunde') || runde.includes('rückrunde')
return isMatch
})
} else if (selectedWettbewerb.value === 'pokal') {
wettbewerbFiltered = saisonFiltered.filter(row => {
// Filtere nach Pokal-Spielen
const runde = (row.Runde || '').toLowerCase()
return runde === 'pokal' || runde.includes('pokal')
})
}
// "alle" zeigt alle Spiele ohne weitere Filterung
// Dann nach Mannschaft filtern
if (selectedFilter.value === 'all') {
filteredData.value = wettbewerbFiltered
} else if (selectedFilter.value === 'erwachsene') {
filteredData.value = wettbewerbFiltered.filter(row => {
const heimMannschaft = (row.HeimMannschaft || '').toLowerCase()
const gastMannschaft = (row.GastMannschaft || '').toLowerCase()
const heimAltersklasse = (row.HeimMannschaftAltersklasse || '').toLowerCase()
const gastAltersklasse = (row.GastMannschaftAltersklasse || '').toLowerCase()
// Prüfe ob eine der Mannschaften Harheimer TC ist
const isHarheimerHeim = heimMannschaft.includes('harheimer tc')
const isHarheimerGast = gastMannschaft.includes('harheimer tc')
if (!isHarheimerHeim && !isHarheimerGast) {
return false // Kein Harheimer TC Spiel
}
// Filtere nach Erwachsenen-Mannschaften (NICHT Jugend)
const isErwachsenenHeim = isHarheimerHeim &&
heimAltersklasse.includes('erwachsene') &&
!heimAltersklasse.includes('jugend')
const isErwachsenenGast = isHarheimerGast &&
gastAltersklasse.includes('erwachsene') &&
!gastAltersklasse.includes('jugend')
return isErwachsenenHeim || isErwachsenenGast
})
} else if (selectedFilter.value === 'nachwuchs') {
filteredData.value = wettbewerbFiltered.filter(row => {
const heimMannschaft = (row.HeimMannschaft || '').toLowerCase()
const gastMannschaft = (row.GastMannschaft || '').toLowerCase()
const heimAltersklasse = (row.HeimMannschaftAltersklasse || '').toLowerCase()
const gastAltersklasse = (row.GastMannschaftAltersklasse || '').toLowerCase()
// Prüfe ob eine der Mannschaften Harheimer TC ist
const isHarheimerHeim = heimMannschaft.includes('harheimer tc')
const isHarheimerGast = gastMannschaft.includes('harheimer tc')
if (!isHarheimerHeim && !isHarheimerGast) {
return false // Kein Harheimer TC Spiel
}
// Filtere nach Jugend-Mannschaften (NUR Jugend)
const isJugendHeim = isHarheimerHeim &&
(heimAltersklasse.includes('jugend') || heimMannschaft.includes('jugend'))
const isJugendGast = isHarheimerGast &&
(gastAltersklasse.includes('jugend') || gastMannschaft.includes('jugend'))
return isJugendHeim || isJugendGast
})
} else {
// Spezifische Mannschaft - Mapping zwischen CMS-Mannschaften und CSV-Daten
filteredData.value = wettbewerbFiltered.filter(row => {
const heimMannschaft = (row.HeimMannschaft || '').toLowerCase()
const gastMannschaft = (row.GastMannschaft || '').toLowerCase()
const heimAltersklasse = (row.HeimMannschaftAltersklasse || '').toLowerCase()
const gastAltersklasse = (row.GastMannschaftAltersklasse || '').toLowerCase()
// Prüfe ob eine der Mannschaften Harheimer TC ist
const isHarheimerHeim = heimMannschaft.includes('harheimer tc')
const isHarheimerGast = gastMannschaft.includes('harheimer tc')
if (!isHarheimerHeim && !isHarheimerGast) {
return false // Kein Harheimer TC Spiel
}
const cmsMannschaft = selectedFilter.value
// Mapping zwischen CMS-Mannschaften und CSV-Daten
const mannschaftMapping = {
'Erwachsene 1': ['harheimer tc'], // Nur ohne römische Zahl
'Erwachsene 2': ['harheimer tc ii'],
'Erwachsene 3': ['harheimer tc iii'],
'Erwachsene 4': ['harheimer tc iv'],
'Erwachsene 5': ['harheimer tc v'],
'Jugendmannschaft': ['harheimer tc'] // Jugend hat keine römische Zahl
}
const csvVariants = mannschaftMapping[cmsMannschaft] || []
// Prüfe Mannschafts-Zuordnung UND Altersklasse
const mannschaftMatch = csvVariants.some(variant => {
// Strikte Übereinstimmung: Prüfe exakte Mannschaftsnamen
if (isHarheimerHeim) {
// Für "harheimer tc" (Erwachsene 1): Nur wenn KEINE römische Zahl folgt
if (variant === 'harheimer tc') {
return heimMannschaft === 'harheimer tc' ||
heimMannschaft.startsWith('harheimer tc ') &&
!heimMannschaft.match(/harheimer tc\s+[ivx]+/i)
}
// Für andere Mannschaften: Exakte Übereinstimmung
return heimMannschaft === variant || heimMannschaft.startsWith(variant + ' ')
}
if (isHarheimerGast) {
// Für "harheimer tc" (Erwachsene 1): Nur wenn KEINE römische Zahl folgt
if (variant === 'harheimer tc') {
return gastMannschaft === 'harheimer tc' ||
gastMannschaft.startsWith('harheimer tc ') &&
!gastMannschaft.match(/harheimer tc\s+[ivx]+/i)
}
// Für andere Mannschaften: Exakte Übereinstimmung
return gastMannschaft === variant || gastMannschaft.startsWith(variant + ' ')
}
return false
})
if (!mannschaftMatch) {
return false
}
// Zusätzliche Altersklassen-Prüfung für spezifische Mannschaften
if (cmsMannschaft.startsWith('Erwachsene')) {
// Erwachsenen-Mannschaften: MUSS Erwachsene sein, DARF NICHT Jugend sein
const isErwachsenenHeim = isHarheimerHeim &&
heimAltersklasse.includes('erwachsene') &&
!heimAltersklasse.includes('jugend')
const isErwachsenenGast = isHarheimerGast &&
gastAltersklasse.includes('erwachsene') &&
!gastAltersklasse.includes('jugend')
return isErwachsenenHeim || isErwachsenenGast
} else if (cmsMannschaft === 'Jugendmannschaft') {
// Jugend-Mannschaft: MUSS Jugend sein
const isJugendHeim = isHarheimerHeim &&
(heimAltersklasse.includes('jugend') || heimMannschaft.includes('jugend'))
const isJugendGast = isHarheimerGast &&
(gastAltersklasse.includes('jugend') || gastMannschaft.includes('jugend'))
return isJugendHeim || isJugendGast
}
return true // Fallback für unbekannte Mannschaften
})
}
}
const downloadPDF = () => {
if (!filteredData.value || filteredData.value.length === 0) return
// Bestimme den Team-Parameter basierend auf dem Filter
let teamParam = ''
if (selectedFilter.value === 'all') {
teamParam = 'all'
} else if (selectedFilter.value === 'erwachsene') {
teamParam = 'erwachsene'
} else if (selectedFilter.value === 'nachwuchs') {
teamParam = 'nachwuchs'
} else {
// Für einzelne Mannschaften: Konvertiere Namen
teamParam = selectedFilter.value.replace(/\s+/g, '_').toLowerCase()
}
// Erstelle Download-URL für dynamische PDF-Generierung
const params = new URLSearchParams({
team: teamParam,
wettbewerb: selectedWettbewerb.value
})
const downloadUrl = `/api/spielplan/pdf?${params.toString()}`
// Öffne Download in neuem Tab
window.open(downloadUrl, '_blank')
}
const formatHeader = (header) => {
const headerMap = {
'Datum': 'Datum',
'Mannschaft': 'Mannschaft',
'Gegner': 'Gegner',
'Ort': 'Ort',
'Uhrzeit': 'Uhrzeit',
'Runde': 'Runde',
'Gruppe': 'Gruppe',
'Ergebnis': 'Ergebnis',
'Bemerkung': 'Bemerkung',
'Status': 'Status'
}
return headerMap[header] || header
}
const formatDate = (dateString) => {
if (!dateString) return '-'
try {
const date = new Date(dateString)
if (isNaN(date.getTime())) {
return dateString
}
return date.toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
})
} catch {
return dateString
}
}
const formatTime = (timeString) => {
if (!timeString) return '-'
if (timeString.match(/^\d{1,2}:\d{2}$/)) {
return timeString
}
return timeString
}
const formatRunde = (rundeString) => {
if (!rundeString) return '-'
const runde = rundeString.toLowerCase()
// Ersetze "VR" durch "Vorrunde" und "RR" durch "Rückrunde"
if (runde === 'vr') {
return 'Vorrunde'
} else if (runde === 'rr') {
return 'Rückrunde'
} else if (runde === 'pokal') {
return 'Pokal'
}
return rundeString
}
const getOriginalHeader = (displayHeader) => {
// Verwende das Mapping um den ursprünglichen Header-Namen zu finden
if (window.spielplanHeaderMapping && window.spielplanHeaderMapping[displayHeader]) {
return window.spielplanHeaderMapping[displayHeader]
}
return displayHeader
}
const getRowClass = (row) => {
const termin = row[getOriginalHeader('Termin')] || row['Termin']
if (!termin) return 'bg-white'
try {
// Parse deutsches Datumsformat: "27.10.2025 20:00"
let spielDatum
if (termin.includes(' ')) {
// Uhrzeit entfernen: "27.10.2025 20:00" -> "27.10.2025"
const datumTeil = termin.split(' ')[0]
// Deutsches Format parsen: "27.10.2025" -> Date
const [tag, monat, jahr] = datumTeil.split('.')
spielDatum = new Date(jahr, monat - 1, tag) // Monat ist 0-basiert
} else {
// Fallback für andere Formate
spielDatum = new Date(termin)
}
if (isNaN(spielDatum.getTime())) return 'bg-white'
const heute = new Date()
heute.setHours(0, 0, 0, 0)
const in7Tagen = new Date(heute)
in7Tagen.setDate(in7Tagen.getDate() + 7)
spielDatum.setHours(0, 0, 0, 0)
// Heute: Hellgelb
if (spielDatum.getTime() === heute.getTime()) {
return 'bg-yellow-100'
}
// Nächste 7 Tage: Hellblau
if (spielDatum > heute && spielDatum <= in7Tagen) {
return 'bg-blue-100'
}
// Standard: Weiß
return 'bg-white'
} catch {
return 'bg-white'
}
}
const getWettbewerbText = () => {
switch (selectedWettbewerb.value) {
case 'punktrunde':
return 'Punktrunde (Vorrunde + Rückrunde)'
case 'pokal':
return 'Pokal'
case 'alle':
return 'Alle Wettbewerbe'
default:
return 'Punktrunde (Vorrunde + Rückrunde)'
}
}
onMounted(() => {
loadData()
})
</script>