Initial commit: Harheimer TC Website
- Vue 3 + Nuxt 3 Framework - Tailwind CSS Styling - Responsive Design mit schwarz-roten Vereinsfarben - Dynamische Galerie mit Lightbox - Event-Management über CSV-Dateien - Mannschaftsübersicht mit dynamischen Seiten - SMTP-Kontaktformular - Google Maps Integration - Mobile-optimierte Navigation mit Submenus - Trainer-Übersicht - Vereinsmeisterschaften, Spielsysteme, TT-Regeln - Impressum mit Datenschutzerklärung
This commit is contained in:
277
pages/vereinsmeisterschaften.vue
Normal file
277
pages/vereinsmeisterschaften.vue
Normal file
@@ -0,0 +1,277 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h1 class="text-4xl sm:text-5xl font-display font-bold text-gray-900 mb-6">
|
||||
Vereinsmeisterschaften
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<p class="text-xl text-gray-600 mb-12">
|
||||
Die Ergebnisse unserer Vereinsmeisterschaften der letzten Jahre
|
||||
</p>
|
||||
|
||||
<!-- Filter -->
|
||||
<div class="mb-8 flex flex-wrap gap-4">
|
||||
<button
|
||||
v-for="jahr in verfuegbareJahre"
|
||||
:key="jahr"
|
||||
@click="selectedYear = jahr"
|
||||
:class="[
|
||||
'px-4 py-2 rounded-lg font-medium transition-colors',
|
||||
selectedYear === jahr
|
||||
? 'bg-primary-600 text-white'
|
||||
: 'bg-white text-gray-700 hover:bg-gray-100 border border-gray-300'
|
||||
]"
|
||||
>
|
||||
{{ jahr }}
|
||||
</button>
|
||||
<button
|
||||
@click="selectedYear = 'alle'"
|
||||
:class="[
|
||||
'px-4 py-2 rounded-lg font-medium transition-colors',
|
||||
selectedYear === 'alle'
|
||||
? 'bg-primary-600 text-white'
|
||||
: 'bg-white text-gray-700 hover:bg-gray-100 border border-gray-300'
|
||||
]"
|
||||
>
|
||||
Alle Jahre
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Ergebnisse -->
|
||||
<div v-if="filteredResults.length > 0" class="space-y-8">
|
||||
<div
|
||||
v-for="jahr in sortedJahre"
|
||||
:key="jahr"
|
||||
class="bg-white rounded-xl shadow-lg p-6"
|
||||
>
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6 flex items-center">
|
||||
<Trophy :size="28" class="text-primary-600 mr-3" />
|
||||
{{ jahr }}
|
||||
</h2>
|
||||
|
||||
<!-- Besondere Bemerkungen -->
|
||||
<div v-if="sortedGroupedResults[jahr]?.bemerkungen" class="mb-6 p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
|
||||
<p class="text-yellow-800 font-medium">{{ sortedGroupedResults[jahr].bemerkungen }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Kategorien -->
|
||||
<div v-if="sortedGroupedResults[jahr]?.kategorien" class="space-y-6">
|
||||
<div
|
||||
v-for="(kategorieData, kategorie) in sortedGroupedResults[jahr].kategorien"
|
||||
:key="kategorie"
|
||||
class="border-l-4 border-primary-600 pl-4"
|
||||
>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-4">{{ kategorie }}</h3>
|
||||
|
||||
<div class="grid gap-3">
|
||||
<div
|
||||
v-for="(ergebnis, index) in kategorieData"
|
||||
:key="index"
|
||||
:class="[
|
||||
'flex items-center justify-between p-3 rounded-lg',
|
||||
ergebnis.platz === '1' ? 'bg-yellow-50 border border-yellow-200' :
|
||||
ergebnis.platz === '2' ? 'bg-gray-50 border border-gray-200' :
|
||||
ergebnis.platz === '3' ? 'bg-orange-50 border border-orange-200' :
|
||||
'bg-gray-100'
|
||||
]"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div
|
||||
:class="[
|
||||
'w-8 h-8 rounded-full flex items-center justify-center text-sm font-bold mr-3',
|
||||
ergebnis.platz === '1' ? 'bg-yellow-500 text-white' :
|
||||
ergebnis.platz === '2' ? 'bg-gray-400 text-white' :
|
||||
ergebnis.platz === '3' ? 'bg-orange-500 text-white' :
|
||||
'bg-gray-300 text-gray-700'
|
||||
]"
|
||||
>
|
||||
{{ ergebnis.platz }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-semibold text-gray-900">
|
||||
{{ ergebnis.spieler1 }}
|
||||
<span v-if="ergebnis.spieler2" class="text-gray-600">
|
||||
/ {{ ergebnis.spieler2 }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
{{ ergebnis.platz === '1' ? 'Vereinsmeister' : ergebnis.platz + '. Platz' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center py-12 bg-white rounded-xl shadow-lg">
|
||||
<Trophy :size="48" class="text-gray-400 mx-auto mb-4" />
|
||||
<p class="text-gray-600">Keine Ergebnisse für das ausgewählte Jahr gefunden.</p>
|
||||
</div>
|
||||
|
||||
<!-- Statistik -->
|
||||
<div class="mt-12 bg-gradient-to-r from-primary-600 to-primary-700 rounded-xl p-8 text-white">
|
||||
<h3 class="text-2xl font-display font-bold mb-6">Statistik</h3>
|
||||
<div class="grid md:grid-cols-3 gap-6">
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold mb-2">{{ verfuegbareJahre.length }}</div>
|
||||
<div class="text-primary-100">Jahre mit Meisterschaften</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold mb-2">{{ totalWinners }}</div>
|
||||
<div class="text-primary-100">Einzelgewinner</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold mb-2">{{ totalDoubles }}</div>
|
||||
<div class="text-primary-100">Doppelgewinner</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gratulation -->
|
||||
<div class="mt-8 text-center">
|
||||
<div class="bg-white rounded-xl shadow-lg p-8 border-l-4 border-primary-600">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4 flex items-center justify-center">
|
||||
<Trophy :size="32" class="text-primary-600 mr-3" />
|
||||
Herzlichen Glückwunsch!
|
||||
</h3>
|
||||
<p class="text-lg text-gray-700 leading-relaxed">
|
||||
Wir gratulieren allen Teilnehmern und Gewinnern der Vereinsmeisterschaften zu ihren großartigen Leistungen!
|
||||
</p>
|
||||
<p class="text-lg text-gray-700 leading-relaxed mt-4">
|
||||
Besonders stolz sind wir auf die kontinuierliche Teilnahme und den fairen Wettkampfgeist unserer Mitglieder.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { Trophy } from 'lucide-vue-next'
|
||||
|
||||
const results = ref([])
|
||||
const selectedYear = ref('alle')
|
||||
|
||||
const loadResults = async () => {
|
||||
try {
|
||||
const response = await fetch('/data/vereinsmeisterschaften.csv')
|
||||
if (!response.ok) return
|
||||
|
||||
const csv = await response.text()
|
||||
const lines = csv.split('\n').filter(line => line.trim() !== '')
|
||||
|
||||
if (lines.length < 2) return
|
||||
|
||||
results.value = lines.slice(1).map(line => {
|
||||
// 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 < 6) return null
|
||||
|
||||
return {
|
||||
jahr: values[0].trim(),
|
||||
kategorie: values[1].trim(),
|
||||
platz: values[2].trim(),
|
||||
spieler1: values[3].trim(),
|
||||
spieler2: values[4].trim(),
|
||||
bemerkung: values[5].trim()
|
||||
}
|
||||
}).filter(result => result !== null)
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Vereinsmeisterschaften:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const verfuegbareJahre = computed(() => {
|
||||
const jahre = [...new Set(results.value.map(r => r.jahr).filter(j => j !== ''))]
|
||||
return jahre.sort((a, b) => b - a) // Neueste zuerst
|
||||
})
|
||||
|
||||
const filteredResults = computed(() => {
|
||||
if (selectedYear.value === 'alle') {
|
||||
return results.value
|
||||
}
|
||||
return results.value.filter(r => r.jahr === selectedYear.value)
|
||||
})
|
||||
|
||||
const groupedResults = computed(() => {
|
||||
const grouped = {}
|
||||
|
||||
filteredResults.value.forEach(result => {
|
||||
if (!grouped[result.jahr]) {
|
||||
grouped[result.jahr] = {
|
||||
kategorien: {},
|
||||
bemerkungen: null
|
||||
}
|
||||
}
|
||||
|
||||
// Besondere Bemerkungen (z.B. coronabedingter Ausfall)
|
||||
if (result.bemerkung && result.bemerkung !== '') {
|
||||
grouped[result.jahr].bemerkungen = result.bemerkung
|
||||
return
|
||||
}
|
||||
|
||||
// Normale Ergebnisse
|
||||
if (result.kategorie && result.kategorie !== '') {
|
||||
if (!grouped[result.jahr].kategorien[result.kategorie]) {
|
||||
grouped[result.jahr].kategorien[result.kategorie] = []
|
||||
}
|
||||
grouped[result.jahr].kategorien[result.kategorie].push(result)
|
||||
}
|
||||
})
|
||||
|
||||
return grouped
|
||||
})
|
||||
|
||||
const sortedGroupedResults = computed(() => {
|
||||
const sorted = {}
|
||||
const jahre = Object.keys(groupedResults.value).sort((a, b) => b - a) // Neueste zuerst
|
||||
|
||||
jahre.forEach(jahr => {
|
||||
sorted[jahr] = groupedResults.value[jahr]
|
||||
})
|
||||
|
||||
return sorted
|
||||
})
|
||||
|
||||
const sortedJahre = computed(() => {
|
||||
return Object.keys(groupedResults.value).sort((a, b) => b - a) // Neueste zuerst
|
||||
})
|
||||
|
||||
const totalWinners = computed(() => {
|
||||
return results.value.filter(r => r.kategorie === 'Einzel' && r.platz === '1').length
|
||||
})
|
||||
|
||||
const totalDoubles = computed(() => {
|
||||
return results.value.filter(r => r.kategorie === 'Doppel' && r.platz === '1').length
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
loadResults()
|
||||
})
|
||||
|
||||
useHead({
|
||||
title: 'Vereinsmeisterschaften - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user