Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 46s
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.
211 lines
6.4 KiB
Vue
211 lines
6.4 KiB
Vue
<template>
|
|
<div>
|
|
<div
|
|
v-if="mannschaften.length > 0"
|
|
class="space-y-8"
|
|
>
|
|
<div
|
|
v-for="(mannschaft, index) in mannschaften"
|
|
:key="index"
|
|
class="bg-white rounded-xl shadow-lg border border-gray-100 overflow-hidden"
|
|
>
|
|
<!-- Header -->
|
|
<div class="bg-gradient-to-r from-primary-600 to-primary-700 p-6">
|
|
<h2 class="text-2xl font-display font-bold text-white mb-2">
|
|
{{ mannschaft.mannschaft }}
|
|
</h2>
|
|
<p class="text-primary-100 text-lg">
|
|
{{ mannschaft.liga }}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Content -->
|
|
<div class="p-6">
|
|
<!-- Liga-Info -->
|
|
<div class="grid md:grid-cols-2 gap-6 mb-6">
|
|
<div class="space-y-3">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-2 h-2 bg-primary-600 rounded-full" />
|
|
<span class="text-gray-600">Staffelleiter:</span>
|
|
<span class="font-semibold text-gray-900">{{ mannschaft.staffelleiter }}</span>
|
|
</div>
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-2 h-2 bg-primary-600 rounded-full" />
|
|
<span class="text-gray-600">Telefon:</span>
|
|
<span class="font-semibold text-gray-900">{{ mannschaft.telefon }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="space-y-3">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-2 h-2 bg-primary-600 rounded-full" />
|
|
<span class="text-gray-600">Heimspieltag:</span>
|
|
<span class="font-semibold text-gray-900">{{ mannschaft.heimspieltag }}</span>
|
|
</div>
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-2 h-2 bg-primary-600 rounded-full" />
|
|
<span class="text-gray-600">Spielsystem:</span>
|
|
<span class="font-semibold text-gray-900">{{ mannschaft.spielsystem }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mannschaftsaufstellung -->
|
|
<div class="border-t border-gray-200 pt-6">
|
|
<h3 class="text-xl font-semibold text-gray-900 mb-4">
|
|
Mannschaftsaufstellung Saison 2025/26 (Hinrunde)
|
|
</h3>
|
|
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
<div
|
|
v-for="(spieler, spielerIndex) in getSpielerListe(mannschaft)"
|
|
:key="spielerIndex"
|
|
class="bg-gray-50 rounded-lg p-4 text-center"
|
|
:class="spieler === mannschaft.mannschaftsfuehrer ? 'ring-2 ring-primary-500 bg-primary-50' : ''"
|
|
>
|
|
<div class="font-semibold text-gray-900">
|
|
{{ spieler }}
|
|
</div>
|
|
<div
|
|
v-if="spieler === mannschaft.mannschaftsfuehrer"
|
|
class="text-xs text-primary-600 font-medium mt-1"
|
|
>
|
|
Mannschaftsführer
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Letzte Aktualisierung -->
|
|
<div class="border-t border-gray-200 pt-4 mt-6">
|
|
<p class="text-sm text-gray-500 text-center">
|
|
Zuletzt aktualisiert am: {{ formatDate(mannschaft.letzte_aktualisierung) }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-else
|
|
class="text-center py-12 bg-gray-50 rounded-xl"
|
|
>
|
|
<Users
|
|
:size="48"
|
|
class="text-gray-400 mx-auto mb-4"
|
|
/>
|
|
<p class="text-gray-600">
|
|
Keine Mannschaftsdaten geladen
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import { Users } from 'lucide-vue-next'
|
|
|
|
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) {
|
|
await new Promise(resolve => setTimeout(resolve, 150))
|
|
return await attempt()
|
|
}
|
|
}
|
|
|
|
const loadMannschaften = async () => {
|
|
try {
|
|
const csv = await fetchCsvText('/data/mannschaften.csv')
|
|
|
|
// Vereinfachter CSV-Parser
|
|
const lines = csv.split('\n').filter(line => line.trim() !== '')
|
|
|
|
if (lines.length < 2) {
|
|
return
|
|
}
|
|
|
|
mannschaften.value = lines.slice(1).map((line, _index) => {
|
|
// 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())
|
|
|
|
if (values.length < 10) {
|
|
return null
|
|
}
|
|
|
|
const mannschaft = {
|
|
mannschaft: values[0].trim(),
|
|
liga: values[1].trim(),
|
|
staffelleiter: values[2].trim(),
|
|
telefon: values[3].trim(),
|
|
heimspieltag: values[4].trim(),
|
|
spielsystem: values[5].trim(),
|
|
mannschaftsfuehrer: values[6].trim(),
|
|
spieler: values[7].trim(),
|
|
weitere_informationen_link: values[8].trim(),
|
|
letzte_aktualisierung: values[9] ? values[9].trim() : ''
|
|
}
|
|
|
|
return mannschaft
|
|
}).filter(mannschaft => mannschaft !== null)
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der Mannschaften:', error)
|
|
}
|
|
}
|
|
|
|
const getSpielerListe = (mannschaft) => {
|
|
if (!mannschaft.spieler) return []
|
|
return mannschaft.spieler.split(';').map(s => s.trim()).filter(s => s !== '')
|
|
}
|
|
|
|
const formatDate = (dateString) => {
|
|
if (!dateString) return ''
|
|
|
|
// Wenn bereits im Format DD.MM.YYYY, direkt zurückgeben
|
|
if (/^\d{2}\.\d{2}\.\d{4}$/.test(dateString)) {
|
|
return dateString
|
|
}
|
|
|
|
// Versuche, das Datum zu parsen
|
|
const date = new Date(dateString)
|
|
if (isNaN(date.getTime())) {
|
|
return dateString // Falls ungültig, Original zurückgeben
|
|
}
|
|
|
|
return date.toLocaleDateString('de-DE', {
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
year: 'numeric'
|
|
})
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadMannschaften()
|
|
})
|
|
</script>
|