651 lines
24 KiB
Vue
651 lines
24 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"
|
|
@change="filterData"
|
|
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"
|
|
>
|
|
<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"
|
|
@change="filterData"
|
|
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"
|
|
>
|
|
<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
|
|
@click="downloadPDF"
|
|
: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"
|
|
>
|
|
<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 @click="loadData" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors">
|
|
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, computed } 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([])
|
|
|
|
const loadData = async () => {
|
|
isLoading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
// Lade Spielplandaten und Mannschaften parallel
|
|
const [spielplanResponse, mannschaftenResponse] = await Promise.all([
|
|
fetch('/api/spielplan'),
|
|
fetch('/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.ok) {
|
|
const csvText = await mannschaftenResponse.text()
|
|
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)
|
|
const currentDate = new Date()
|
|
const currentYear = currentDate.getFullYear()
|
|
|
|
// 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 (error) {
|
|
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> |