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:
14
pages/anlagen.vue
Normal file
14
pages/anlagen.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="min-h-screen">
|
||||
<Facilities />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Facilities from '~/components/Facilities.vue'
|
||||
|
||||
useHead({
|
||||
title: 'Anlagen - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
14
pages/galerie.vue
Normal file
14
pages/galerie.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="min-h-screen">
|
||||
<Gallery />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Gallery from '~/components/Gallery.vue'
|
||||
|
||||
useHead({
|
||||
title: 'Galerie - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
96
pages/geschichte.vue
Normal file
96
pages/geschichte.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-7xl 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">
|
||||
Vereinsgeschichte
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<div class="prose prose-lg max-w-none">
|
||||
<p class="text-xl text-gray-600 mb-8">
|
||||
Die bewegte Geschichte des Harheimer Tischtennis Clubs seit 1954.
|
||||
</p>
|
||||
|
||||
<div class="space-y-6 mb-8">
|
||||
<p class="text-lg text-gray-700">
|
||||
Nach dem zweiten Weltkrieg entwickelte sich sprunghaft der Tischtennissport in der Bundesrepublik.
|
||||
Auch in der damaligen Gemeinde Harheim gab es junge Menschen, die an diesem neuen Sport Gefallen fanden,
|
||||
so dass am <strong>10.05.1950</strong> durch deren Initiative eine Tischtennisabteilung innerhalb der
|
||||
Sportgemeinschaft Harheim (SGH) gegründet wurde.
|
||||
</p>
|
||||
|
||||
<p class="text-lg text-gray-700">
|
||||
Zu Anfang waren es nur wenige TT-Begeisterte und nur durch deren Idealismus, Opfer und Gemeinschaftssinn
|
||||
wurden die Anfangsschwierigkeiten überwunden. Im Laufe der Zeit kamen auch die Kritiker innerhalb der SGH
|
||||
nicht umhin, die damaligen Tischtennisspieler mit ihrer neuen Sportart anzuerkennen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">10.06.1954 - Gründung des HTC</h3>
|
||||
<p class="text-gray-600 mb-3">
|
||||
Bei der am 20.05.1954 stattgefundenen Sitzung der SGH wurde die Trennung der einzelnen Abteilungen beschlossen.
|
||||
Somit sah sich die TT-Abteilung veranlasst, ihren Sportbetrieb in eigener Regie weiterzuführen.
|
||||
</p>
|
||||
<p class="text-gray-600">
|
||||
Am <strong>10.06.1954</strong> trafen sich 6 Damen und 22 Herren zur Gründungsversammlung in der Gaststätte „Zum Löwen".
|
||||
Der neu gegründete Verein wurde unter dem Namen "Harheimer Tischtennis-Club" Mitglied des Landessportbundes Hessen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">1964 - Neue Trainingsstätte</h3>
|
||||
<p class="text-gray-600">
|
||||
Mit der Erbauung der Schulturnhalle im Jahre 1964 stand eine für die damaligen Verhältnisse recht moderne
|
||||
Übungsstätte zur Verfügung, die dem HTC für einen Tag in der Woche überlassen wurde. Damit waren viele
|
||||
Probleme gelöst und es gab einen Aufschwung, der sich in einer steigenden Spielerzahl bemerkbar machte.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">1974 - Bürgerhaus</h3>
|
||||
<p class="text-gray-600">
|
||||
Mit der Erstellung des Bürgerhauses wurde wiederum neuer Trainingsraum geschaffen, der besonders für den
|
||||
Tischtennissport geeignet ist. Der HTC nahm die Gelegenheit war und hielt ab Mai 1974 seine Übungsabende
|
||||
im großen Saal des Bürgerhauses ab.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">1976 - Eintragung ins Vereinsregister</h3>
|
||||
<p class="text-gray-600">
|
||||
Die Eintragung in das Vereinsregister (e. V.) erfolgte im Jahre 1976 und gleichzeitig wurde dem Verein
|
||||
die Gemeinnützigkeit zuerkannt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">1978/79 - Sportlicher Höhepunkt</h3>
|
||||
<p class="text-gray-600">
|
||||
Ein besonderes Geschenk machten die Spieler des HTC im Jubiläumsjahr ihrem Verein: Die 1. Herrenmannschaft
|
||||
wurde Meister der Bezirksklasse Ffm.-Ost und die 2. Herrenmannschaft Meister der Kreisklasse-A Ffm.-Nord.
|
||||
Nachdem auch die Schülermannschaft Meister ihrer Klasse wurde, ist die Saison 78/79 als absolut sportlicher
|
||||
Höhepunkt in der Vereinsgeschichte zu werten.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">Heute</h3>
|
||||
<p class="text-gray-600">
|
||||
Der HTC hat sich auch in Zukunft zur Aufgabe gemacht, allen interessierten Bürgern und Jugendlichen im
|
||||
Rahmen seiner Möglichkeiten das Tischtennisspielen als Leistungssport oder zur Freizeitgestaltung zu ermöglichen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
useHead({
|
||||
title: 'Geschichte - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
124
pages/impressum.vue
Normal file
124
pages/impressum.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 px-4 sm:px-6 lg:px-8 bg-gray-50">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<h1 class="text-4xl sm:text-5xl font-display font-bold text-gray-900 mb-6">
|
||||
Impressum
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg space-y-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-display font-bold text-gray-900 mb-2">Angaben gemäß § 5 TMG</h2>
|
||||
<p class="text-gray-700">
|
||||
Harheimer Tischtennis-Club 1954 e. V. (HTC)<br />
|
||||
In der Fuchskaut 4<br />
|
||||
60437 Frankfurt am Main
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-xl font-display font-bold text-gray-900 mb-2">Kontakt</h2>
|
||||
<p class="text-gray-700">
|
||||
Telefon: 06101-4992227<br />
|
||||
E-Mail: j.dichmann@gmx.de<br />
|
||||
Internet: www.harheimertc.de
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-xl font-display font-bold text-gray-900 mb-2">Vertretungsberechtigter Vorstand</h2>
|
||||
<p class="text-gray-700">
|
||||
Roger Dichmann, Vorsitzender<br />
|
||||
Jürgen Kratz, Stellvertreter des Vorsitzenden<br />
|
||||
Olaf Nüßlein, Kassenwart<br />
|
||||
Jürgen Dichmann, Schriftführer
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-xl font-display font-bold text-gray-900 mb-2">Registereintrag</h2>
|
||||
<p class="text-gray-700">
|
||||
lsb h-Vereinsnummer: 24091<br />
|
||||
Registereintrag: Amtsgericht Frankfurt am Main, Registergericht<br />
|
||||
Registernummer: VR 6835
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-xl font-display font-bold text-gray-900 mb-2">Vereinsatzung</h2>
|
||||
<p class="text-gray-700 mb-4">
|
||||
Unsere aktuelle Vereinsatzung können Sie hier herunterladen oder online einsehen:
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row gap-3">
|
||||
<a
|
||||
href="/documents/satzung.pdf"
|
||||
target="_blank"
|
||||
class="inline-flex items-center px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white font-medium rounded-lg transition-colors"
|
||||
>
|
||||
<FileText :size="16" class="mr-2" />
|
||||
Satzung herunterladen (PDF)
|
||||
</a>
|
||||
<NuxtLink
|
||||
to="/satzung"
|
||||
class="inline-flex items-center px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-900 font-medium rounded-lg transition-colors"
|
||||
>
|
||||
<Eye :size="16" class="mr-2" />
|
||||
Online ansehen
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-xl font-display font-bold text-gray-900 mb-2">Verantwortlich für den Inhalt</h2>
|
||||
<p class="text-gray-700">
|
||||
Roger Dichmann<br />
|
||||
Reginastr. 46<br />
|
||||
60437 Frankfurt
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-xl font-display font-bold text-gray-900 mb-2">Haftungsausschluss</h2>
|
||||
|
||||
<h3 class="font-semibold text-gray-900 mt-4 mb-2">Haftung für Inhalte</h3>
|
||||
<p class="text-gray-700 mb-4">
|
||||
Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen. Grundsätzlich sind alle unsere Informationen ohne Gewähr. Auch für den Fall das unzutreffende oder falsche Informationen enthalten sind, wird vom HTC jegliche Haftung ausgeschlossen.
|
||||
</p>
|
||||
|
||||
<h3 class="font-semibold text-gray-900 mt-4 mb-2">Haftung für Links</h3>
|
||||
<p class="text-gray-700 mb-4">
|
||||
Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Eine Haftung für Schäden, die ggf. durch das Aufrufen dieser Seiten, bzw. deren Inhalte entstehen, wird vom HTC nicht übernommen. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.
|
||||
</p>
|
||||
|
||||
<h3 class="font-semibold text-gray-900 mt-4 mb-2">Urheberrecht</h3>
|
||||
<p class="text-gray-700 mb-4">
|
||||
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet. Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-xl font-display font-bold text-gray-900 mb-2">Datenschutzerklärung</h2>
|
||||
|
||||
<h3 class="font-semibold text-gray-900 mt-4 mb-2">Datenschutz</h3>
|
||||
<p class="text-gray-700 mb-4">
|
||||
Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend der gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerklärung. Die Nutzung unserer Website ist in der Regel ohne Angabe personenbezogener Daten möglich. Soweit auf unseren Seiten personenbezogene Daten (beispielsweise Name, Anschrift oder E-Mail-Adressen) erhoben werden, erfolgt dies, soweit möglich, stets auf freiwilliger Basis. Diese Daten werden ohne Ihre ausdrückliche Zustimmung nicht an Dritte weitergegeben. Wir weisen darauf hin, dass die Datenübertragung im Internet (z.B. bei der Kommunikation per E-Mail) Sicherheitslücken aufweisen kann. Ein lückenloser Schutz der Daten vor dem Zugriff durch Dritte ist nicht möglich.
|
||||
</p>
|
||||
|
||||
<h3 class="font-semibold text-gray-900 mt-4 mb-2">Widerspruch Werbe-Mails</h3>
|
||||
<p class="text-gray-700">
|
||||
Der Nutzung von im Rahmen der Impressumspflicht veröffentlichten Kontaktdaten zur Übersendung von nicht ausdrücklich angeforderter Werbung und Informationsmaterialien wird hiermit widersprochen. Die Betreiber der Seiten behalten sich ausdrücklich rechtliche Schritte im Falle der unverlangten Zusendung von Werbeinformationen, etwa durch Spam-E-Mails, vor.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { FileText, Eye } from 'lucide-vue-next'
|
||||
|
||||
useHead({
|
||||
title: 'Impressum - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
9
pages/index.vue
Normal file
9
pages/index.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div class="min-h-full">
|
||||
<Hero />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Hero from '~/components/Hero.vue'
|
||||
</script>
|
||||
14
pages/kontakt.vue
Normal file
14
pages/kontakt.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="min-h-screen">
|
||||
<Contact />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Contact from '~/components/Contact.vue'
|
||||
|
||||
useHead({
|
||||
title: 'Kontakt - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
209
pages/mannschaften/[slug].vue
Normal file
209
pages/mannschaften/[slug].vue
Normal file
@@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div v-if="mannschaft" class="space-y-8">
|
||||
<!-- Header -->
|
||||
<div class="bg-gradient-to-r from-primary-600 to-primary-700 rounded-xl p-8 text-white">
|
||||
<h1 class="text-4xl font-display font-bold mb-2">
|
||||
{{ mannschaft.mannschaft }}
|
||||
</h1>
|
||||
<p class="text-primary-100 text-xl">{{ mannschaft.liga }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Liga-Info -->
|
||||
<div class="bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 class="text-2xl font-semibold text-gray-900 mb-6">Liga-Informationen</h2>
|
||||
<div class="grid md:grid-cols-2 gap-6">
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-2 h-2 bg-primary-600 rounded-full"></div>
|
||||
<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"></div>
|
||||
<span class="text-gray-600">Telefon:</span>
|
||||
<span class="font-semibold text-gray-900">{{ mannschaft.telefon }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-2 h-2 bg-primary-600 rounded-full"></div>
|
||||
<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"></div>
|
||||
<span class="text-gray-600">Spielsystem:</span>
|
||||
<span class="font-semibold text-gray-900">{{ mannschaft.spielsystem }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mannschaftsaufstellung -->
|
||||
<div class="bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 class="text-2xl font-semibold text-gray-900 mb-6">
|
||||
Mannschaftsaufstellung Saison 2025/26 (Hinrunde)
|
||||
</h2>
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div
|
||||
v-for="(spieler, index) in getSpielerListe(mannschaft)"
|
||||
:key="index"
|
||||
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>
|
||||
|
||||
<!-- Links -->
|
||||
<div class="bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 class="text-2xl font-semibold text-gray-900 mb-6">Weitere Informationen</h2>
|
||||
<div class="text-center">
|
||||
<a
|
||||
v-if="mannschaft.weitere_informationen_link && mannschaft.weitere_informationen_link !== ''"
|
||||
:href="mannschaft.weitere_informationen_link"
|
||||
target="_blank"
|
||||
class="inline-flex items-center px-8 py-4 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
<BarChart :size="24" class="mr-3" />
|
||||
Weitere Informationen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Letzte Aktualisierung -->
|
||||
<div class="bg-white rounded-xl shadow-lg p-6">
|
||||
<p class="text-sm text-gray-500 text-center">
|
||||
Zuletzt aktualisiert am: {{ formatDate(mannschaft.letzte_aktualisierung) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Zurück-Button -->
|
||||
<div class="text-center">
|
||||
<NuxtLink
|
||||
to="/mannschaften"
|
||||
class="inline-flex items-center px-6 py-3 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
← Zurück zur Übersicht
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center py-16">
|
||||
<h1 class="text-4xl font-display font-bold text-gray-900 mb-4">Mannschaft nicht gefunden</h1>
|
||||
<p class="text-gray-600 mb-8">Die angeforderte Mannschaft konnte nicht gefunden werden.</p>
|
||||
<NuxtLink
|
||||
to="/mannschaften"
|
||||
class="inline-flex items-center px-6 py-3 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
Zur Mannschaftsübersicht
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { Calendar, Users, BarChart } from 'lucide-vue-next'
|
||||
|
||||
const route = useRoute()
|
||||
const mannschaft = ref(null)
|
||||
|
||||
const loadMannschaften = async () => {
|
||||
try {
|
||||
const response = await fetch('/data/mannschaften.csv')
|
||||
if (!response.ok) return
|
||||
|
||||
const csv = await response.text()
|
||||
const lines = csv.split('\n').filter(line => line.trim() !== '')
|
||||
|
||||
if (lines.length < 2) return
|
||||
|
||||
const mannschaften = 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())
|
||||
|
||||
if (values.length < 10) return null
|
||||
|
||||
return {
|
||||
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].trim(),
|
||||
slug: values[0].trim().toLowerCase().replace(/\s+/g, '-')
|
||||
}
|
||||
}).filter(mannschaft => mannschaft !== null)
|
||||
|
||||
// Finde die Mannschaft basierend auf dem Slug
|
||||
const currentSlug = route.params.slug
|
||||
mannschaft.value = mannschaften.find(m => m.slug === currentSlug) || null
|
||||
|
||||
if (mannschaft.value) {
|
||||
useHead({
|
||||
title: `${mannschaft.value.mannschaft} - Harheimer TC`,
|
||||
})
|
||||
}
|
||||
} 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>
|
||||
36
pages/mannschaften/damen.vue
Normal file
36
pages/mannschaften/damen.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-7xl 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">
|
||||
Damenmannschaft
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">1. Damen</h3>
|
||||
<p class="text-gray-600 mb-4">Liga: Bezirksliga</p>
|
||||
<p class="text-gray-600 mb-6">Mannschaftsführerin: Name folgt</p>
|
||||
|
||||
<div class="mt-8">
|
||||
<h4 class="text-lg font-semibold text-gray-900 mb-4">Wir suchen Verstärkung!</h4>
|
||||
<p class="text-gray-600 mb-4">
|
||||
Unsere Damenmannschaft freut sich über neue Spielerinnen. Interessiert? Dann melde dich bei uns!
|
||||
</p>
|
||||
<NuxtLink
|
||||
to="/kontakt"
|
||||
class="inline-flex items-center px-6 py-3 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
Kontakt aufnehmen
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
useHead({
|
||||
title: 'Damenmannschaft - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
37
pages/mannschaften/herren.vue
Normal file
37
pages/mannschaften/herren.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-7xl 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">
|
||||
Herrenmannschaften
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<div class="space-y-8">
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">1. Herren</h3>
|
||||
<p class="text-gray-600 mb-4">Liga: Bezirksoberliga</p>
|
||||
<p class="text-gray-600">Mannschaftsführer: Name folgt</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">2. Herren</h3>
|
||||
<p class="text-gray-600 mb-4">Liga: Bezirksliga</p>
|
||||
<p class="text-gray-600">Mannschaftsführer: Name folgt</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">3. Herren</h3>
|
||||
<p class="text-gray-600 mb-4">Liga: Kreisliga</p>
|
||||
<p class="text-gray-600">Mannschaftsführer: Name folgt</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
useHead({
|
||||
title: 'Herrenmannschaften - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
93
pages/mannschaften/index.vue
Normal file
93
pages/mannschaften/index.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-7xl 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">
|
||||
Unsere Mannschaften
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<p class="text-xl text-gray-600 mb-12">
|
||||
Unsere aktiven Mannschaften in der Saison 2025/26
|
||||
</p>
|
||||
|
||||
<MannschaftenUebersicht />
|
||||
|
||||
<div class="mt-16">
|
||||
<h2 class="text-3xl font-display font-bold text-gray-900 mb-8 text-center">
|
||||
Weitere Informationen
|
||||
</h2>
|
||||
<div class="grid md:grid-cols-3 gap-8">
|
||||
<NuxtLink
|
||||
to="/mannschaften/herren"
|
||||
class="group bg-white p-8 rounded-xl shadow-lg hover:shadow-2xl transition-all border border-gray-100 hover:border-primary-600"
|
||||
>
|
||||
<div class="w-16 h-16 bg-gradient-to-br from-primary-500 to-primary-700 rounded-xl flex items-center justify-center mb-4 group-hover:scale-110 transition-transform">
|
||||
<Users :size="32" class="text-white" />
|
||||
</div>
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-2 group-hover:text-primary-600 transition-colors">
|
||||
Herren
|
||||
</h3>
|
||||
<p class="text-gray-600">
|
||||
3 Mannschaften in verschiedenen Ligen
|
||||
</p>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink
|
||||
to="/mannschaften/damen"
|
||||
class="group bg-white p-8 rounded-xl shadow-lg hover:shadow-2xl transition-all border border-gray-100 hover:border-primary-600"
|
||||
>
|
||||
<div class="w-16 h-16 bg-gradient-to-br from-primary-500 to-primary-700 rounded-xl flex items-center justify-center mb-4 group-hover:scale-110 transition-transform">
|
||||
<Users :size="32" class="text-white" />
|
||||
</div>
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-2 group-hover:text-primary-600 transition-colors">
|
||||
Damen
|
||||
</h3>
|
||||
<p class="text-gray-600">
|
||||
1 Mannschaft in der Bezirksliga
|
||||
</p>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink
|
||||
to="/mannschaften/jugend"
|
||||
class="group bg-white p-8 rounded-xl shadow-lg hover:shadow-2xl transition-all border border-gray-100 hover:border-primary-600"
|
||||
>
|
||||
<div class="w-16 h-16 bg-gradient-to-br from-primary-500 to-primary-700 rounded-xl flex items-center justify-center mb-4 group-hover:scale-110 transition-transform">
|
||||
<Users :size="32" class="text-white" />
|
||||
</div>
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-2 group-hover:text-primary-600 transition-colors">
|
||||
Jugend
|
||||
</h3>
|
||||
<p class="text-gray-600">
|
||||
2 Jugendmannschaften
|
||||
</p>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div class="mt-12 bg-primary-50 p-8 rounded-xl border border-primary-100">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">
|
||||
Spielpläne & Ergebnisse
|
||||
</h3>
|
||||
<p class="text-gray-600 mb-6">
|
||||
Alle aktuellen Spielpläne und Ergebnisse unserer Mannschaften finden Sie hier.
|
||||
</p>
|
||||
<NuxtLink
|
||||
to="/mannschaften/spielplaene"
|
||||
class="inline-flex items-center px-6 py-3 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
Zu den Spielplänen
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Users } from 'lucide-vue-next'
|
||||
import MannschaftenUebersicht from '~/components/MannschaftenUebersicht.vue'
|
||||
|
||||
useHead({
|
||||
title: 'Mannschaften - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
47
pages/mannschaften/jugend.vue
Normal file
47
pages/mannschaften/jugend.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-7xl 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">
|
||||
Jugendmannschaften
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<div class="space-y-8">
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">Jugend 1 (U18)</h3>
|
||||
<p class="text-gray-600 mb-4">Liga: Bezirksliga</p>
|
||||
<p class="text-gray-600">Betreuer: Name folgt</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">Jugend 2 (U15)</h3>
|
||||
<p class="text-gray-600 mb-4">Liga: Kreisliga</p>
|
||||
<p class="text-gray-600">Betreuer: Name folgt</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-primary-50 p-8 rounded-xl border border-primary-100">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">
|
||||
Jugendtraining
|
||||
</h3>
|
||||
<p class="text-gray-600 mb-6">
|
||||
<strong>Dienstag & Donnerstag:</strong> 17:00 - 19:00 Uhr<br />
|
||||
Für Kinder und Jugendliche von 8-18 Jahren
|
||||
</p>
|
||||
<NuxtLink
|
||||
to="/training"
|
||||
class="inline-flex items-center px-6 py-3 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
Mehr zum Training
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
useHead({
|
||||
title: 'Jugendmannschaften - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
268
pages/mannschaften/spielplaene.vue
Normal file
268
pages/mannschaften/spielplaene.vue
Normal file
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center mb-12">
|
||||
<h1 class="text-4xl sm:text-5xl font-display font-bold text-gray-900 mb-4">
|
||||
Spielpläne
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mx-auto mb-6" />
|
||||
<p class="text-xl text-gray-600 max-w-3xl mx-auto">
|
||||
Aktuelle Spielpläne der Saison {{ aktuellesSaisonLabel }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Spielpläne -->
|
||||
<div v-if="spielplaene.length > 0" class="space-y-4 max-w-4xl mx-auto">
|
||||
<div
|
||||
v-for="(plan, index) in spielplaene"
|
||||
:key="index"
|
||||
class="bg-white rounded-xl shadow-lg border border-gray-100 p-6 hover:shadow-xl transition-shadow"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center">
|
||||
<FileText :size="24" class="text-primary-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-900">{{ plan.titel }}</h3>
|
||||
<p class="text-sm text-gray-500">Saison {{ plan.saison }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
:href="plan.url"
|
||||
download
|
||||
class="inline-flex items-center px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white font-medium rounded-lg transition-colors"
|
||||
>
|
||||
<Download :size="18" class="mr-2" />
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Keine Spielpläne -->
|
||||
<div v-else class="text-center py-16 bg-white rounded-xl shadow-lg max-w-4xl mx-auto">
|
||||
<FileText :size="48" class="text-gray-400 mx-auto mb-4" />
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">Keine Spielpläne verfügbar</h3>
|
||||
<p class="text-gray-600">
|
||||
Für die aktuelle Saison {{ aktuellesSaisonLabel }} sind noch keine Spielpläne verfügbar.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Online Spielpläne und Tabellen -->
|
||||
<div class="mt-12 max-w-4xl mx-auto">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6 text-center">
|
||||
Online Spielpläne & Tabellen
|
||||
</h2>
|
||||
|
||||
<div v-if="mannschaftenMitLinks.length > 0" class="space-y-3">
|
||||
<div
|
||||
v-for="(mannschaft, index) in mannschaftenMitLinks"
|
||||
:key="index"
|
||||
class="bg-white rounded-lg shadow border border-gray-100 p-4 hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-900">{{ mannschaft.mannschaft }}</h3>
|
||||
<p class="text-sm text-gray-500">{{ mannschaft.liga }}</p>
|
||||
</div>
|
||||
<a
|
||||
:href="mannschaft.weitere_informationen_link"
|
||||
target="_blank"
|
||||
class="inline-flex items-center px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors text-sm"
|
||||
>
|
||||
<ExternalLink :size="16" class="mr-2" />
|
||||
Online ansehen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info-Box -->
|
||||
<div class="mt-12 max-w-4xl mx-auto bg-primary-50 border border-primary-100 rounded-xl p-6">
|
||||
<h3 class="text-lg font-semibold text-primary-900 mb-2">
|
||||
Hinweis
|
||||
</h3>
|
||||
<p class="text-primary-800">
|
||||
Die Spielpläne werden automatisch für die aktuelle Saison angezeigt.
|
||||
Ältere Spielpläne können auf Anfrage bereitgestellt werden.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { FileText, Download, ExternalLink } from 'lucide-vue-next'
|
||||
|
||||
const spielplaene = ref([])
|
||||
const mannschaftenMitLinks = ref([])
|
||||
|
||||
// Berechne die aktuelle Saison
|
||||
const aktuellesSaison = computed(() => {
|
||||
const jetzt = new Date()
|
||||
const monat = jetzt.getMonth() + 1 // 1-12
|
||||
const jahr = jetzt.getFullYear()
|
||||
|
||||
// Saison wechselt im Juli/August
|
||||
if (monat >= 7) {
|
||||
return { start: jahr, ende: jahr + 1 }
|
||||
} else {
|
||||
return { start: jahr - 1, ende: jahr }
|
||||
}
|
||||
})
|
||||
|
||||
const aktuellesSaisonLabel = computed(() => {
|
||||
return `${aktuellesSaison.value.start}/${aktuellesSaison.value.ende}`
|
||||
})
|
||||
|
||||
// Funktion zum Extrahieren der Saison aus dem Dateinamen
|
||||
const extractSaison = (filename) => {
|
||||
console.log('extractSaison für:', filename)
|
||||
|
||||
// Normalisiere alle möglichen Trennzeichen zu einem einzigen Zeichen
|
||||
// Suche nach 4 Ziffern, gefolgt von irgendeinem Nicht-Ziffer-Zeichen, gefolgt von 4 Ziffern
|
||||
let match = filename.match(/(\d{4})[^0-9](\d{4})/)
|
||||
if (match) {
|
||||
const start = parseInt(match[1])
|
||||
const ende = parseInt(match[2])
|
||||
console.log(' Gefunden (4-stellig):', start, ende)
|
||||
return { start, ende, label: `${start}/${ende}` }
|
||||
}
|
||||
|
||||
// Suche nach 2 Ziffern, gefolgt von irgendeinem Nicht-Ziffer-Zeichen, gefolgt von 2 Ziffern
|
||||
match = filename.match(/(\d{2})[^0-9](\d{2})/)
|
||||
if (match) {
|
||||
let start = parseInt(match[1])
|
||||
let ende = parseInt(match[2])
|
||||
|
||||
// Wenn Kurzform (25-26), zu Langform konvertieren
|
||||
if (start < 100) {
|
||||
start = 2000 + start
|
||||
ende = 2000 + ende
|
||||
}
|
||||
console.log(' Gefunden (2-stellig):', start, ende)
|
||||
return { start, ende, label: `${start}/${ende}` }
|
||||
}
|
||||
|
||||
console.log(' Keine Saison gefunden')
|
||||
return null
|
||||
}
|
||||
|
||||
// Prüfe, ob eine Saison zur aktuellen Saison passt
|
||||
const istAktuellesSaison = (saison) => {
|
||||
if (!saison) return false
|
||||
return saison.start === aktuellesSaison.value.start &&
|
||||
saison.ende === aktuellesSaison.value.ende
|
||||
}
|
||||
|
||||
// Lade Spielpläne
|
||||
const loadSpielplaene = async () => {
|
||||
try {
|
||||
console.log('=== SPIELPLÄNE LADEN ===')
|
||||
console.log('Aktuelle Saison:', aktuellesSaison.value)
|
||||
console.log('Saison Label:', aktuellesSaisonLabel.value)
|
||||
|
||||
// Lade Dateien vom Server
|
||||
const response = await fetch('/api/spielplaene')
|
||||
if (!response.ok) {
|
||||
console.error('Fehler beim Laden der Spielpläne:', response.status)
|
||||
return
|
||||
}
|
||||
|
||||
const dateien = await response.json()
|
||||
console.log('Geladene Dateien:', dateien)
|
||||
|
||||
const gefiltert = dateien
|
||||
.map(filename => {
|
||||
console.log('Verarbeite Datei:', filename)
|
||||
const saison = extractSaison(filename)
|
||||
console.log(' Extrahierte Saison:', saison)
|
||||
console.log(' Ist aktuelle Saison?', saison ? istAktuellesSaison(saison) : false)
|
||||
|
||||
if (!saison || !istAktuellesSaison(saison)) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Extrahiere Titel aus Dateiname
|
||||
const titel = filename
|
||||
.replace(/\.(pdf|PDF|xlsx|XLSX|xls|XLS)$/, '')
|
||||
.replace(/[-_]/g, ' ')
|
||||
.replace(/\d{2,4}[-_\/⁄]\d{2,4}/, '')
|
||||
.trim()
|
||||
|
||||
return {
|
||||
filename,
|
||||
titel: titel || filename,
|
||||
saison: saison.label,
|
||||
url: `/spielplaene/${filename}`
|
||||
}
|
||||
})
|
||||
.filter(item => item !== null)
|
||||
|
||||
spielplaene.value = gefiltert
|
||||
|
||||
console.log('Aktuelle Saison:', aktuellesSaisonLabel.value)
|
||||
console.log('Gefundene Spielpläne:', spielplaene.value)
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Spielpläne:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Lade Mannschaften aus CSV
|
||||
const loadMannschaften = async () => {
|
||||
try {
|
||||
const response = await fetch('/data/mannschaften.csv')
|
||||
if (!response.ok) return
|
||||
|
||||
const csv = await response.text()
|
||||
const lines = csv.split('\n').filter(line => line.trim() !== '')
|
||||
|
||||
if (lines.length < 2) return
|
||||
|
||||
mannschaftenMitLinks.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())
|
||||
|
||||
if (values.length < 10) return null
|
||||
|
||||
return {
|
||||
mannschaft: values[0].trim(),
|
||||
liga: values[1].trim(),
|
||||
weitere_informationen_link: values[8].trim()
|
||||
}
|
||||
}).filter(mannschaft => mannschaft !== null && mannschaft.weitere_informationen_link !== '')
|
||||
|
||||
console.log('Mannschaften mit Links:', mannschaftenMitLinks.value)
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Mannschaften:', error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadSpielplaene()
|
||||
loadMannschaften()
|
||||
})
|
||||
|
||||
useHead({
|
||||
title: 'Spielpläne - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
14
pages/mitgliedschaft.vue
Normal file
14
pages/mitgliedschaft.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="min-h-screen">
|
||||
<Membership />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Membership from '~/components/Membership.vue'
|
||||
|
||||
useHead({
|
||||
title: 'Mitgliedschaft - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
149
pages/satzung.vue
Normal file
149
pages/satzung.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-4xl 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">
|
||||
Vereinssatzung
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg">
|
||||
<p class="text-lg text-gray-600 mb-8">
|
||||
Die Satzung des Harheimer Tischtennis Clubs regelt die Grundlagen unseres Vereins.
|
||||
</p>
|
||||
|
||||
<div class="prose prose-lg max-w-none">
|
||||
<div class="space-y-8">
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 1 Name, Sitz und Geschäftsjahr</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p><strong>(1)</strong> Der Verein führt den Namen "Harheimer Tischtennis-Club 1954 e.V." (HTC).</p>
|
||||
<p><strong>(2)</strong> Der Verein hat seinen Sitz in Frankfurt am Main.</p>
|
||||
<p><strong>(3)</strong> Das Geschäftsjahr ist das Kalenderjahr.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 2 Zweck des Vereins</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p><strong>(1)</strong> Der Verein bezweckt die Förderung des Tischtennissports und die Pflege der Geselligkeit seiner Mitglieder.</p>
|
||||
<p><strong>(2)</strong> Der Verein ist selbstlos tätig; er verfolgt nicht in erster Linie eigenwirtschaftliche Zwecke.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 3 Mitgliedschaft</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p><strong>(1)</strong> Mitglied des Vereins kann jede natürliche Person werden, die die Ziele des Vereins unterstützt.</p>
|
||||
<p><strong>(2)</strong> Der Antrag auf Mitgliedschaft ist schriftlich an den Vorstand zu richten.</p>
|
||||
<p><strong>(3)</strong> Über die Aufnahme entscheidet der Vorstand.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 4 Rechte und Pflichten der Mitglieder</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p><strong>(1)</strong> Die Mitglieder haben das Recht, an den Veranstaltungen des Vereins teilzunehmen und die Einrichtungen des Vereins zu benutzen.</p>
|
||||
<p><strong>(2)</strong> Die Mitglieder sind verpflichtet, die Satzung und die Beschlüsse der Vereinsorgane zu beachten und den Mitgliedsbeitrag zu entrichten.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 5 Mitgliedsbeiträge</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p><strong>(1)</strong> Die Höhe der Mitgliedsbeiträge wird von der Mitgliederversammlung festgesetzt.</p>
|
||||
<p><strong>(2)</strong> Die Mitgliedsbeiträge sind im Voraus zu entrichten.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 6 Beendigung der Mitgliedschaft</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p><strong>(1)</strong> Die Mitgliedschaft endet durch Austritt, Ausschluss oder Tod.</p>
|
||||
<p><strong>(2)</strong> Der Austritt erfolgt durch schriftliche Erklärung gegenüber dem Vorstand.</p>
|
||||
<p><strong>(3)</strong> Ein Mitglied kann aus wichtigem Grund ausgeschlossen werden.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 7 Organe des Vereins</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p>Organe des Vereins sind:</p>
|
||||
<ul class="list-disc list-inside ml-4 space-y-1">
|
||||
<li>die Mitgliederversammlung</li>
|
||||
<li>der Vorstand</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 8 Mitgliederversammlung</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p><strong>(1)</strong> Die Mitgliederversammlung ist das oberste Organ des Vereins.</p>
|
||||
<p><strong>(2)</strong> Sie wird vom Vorsitzenden mindestens einmal im Jahr einberufen.</p>
|
||||
<p><strong>(3)</strong> Die Mitgliederversammlung beschließt über alle wichtigen Angelegenheiten des Vereins.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 9 Vorstand</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p><strong>(1)</strong> Der Vorstand besteht aus:</p>
|
||||
<ul class="list-disc list-inside ml-4 space-y-1">
|
||||
<li>dem Vorsitzenden</li>
|
||||
<li>dem stellvertretenden Vorsitzenden</li>
|
||||
<li>dem Kassenwart</li>
|
||||
<li>dem Schriftführer</li>
|
||||
</ul>
|
||||
<p><strong>(2)</strong> Der Vorstand wird von der Mitgliederversammlung gewählt.</p>
|
||||
<p><strong>(3)</strong> Der Vorstand führt die Geschäfte des Vereins.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 10 Satzungsänderungen</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p>Satzungsänderungen können nur in einer Mitgliederversammlung mit einer Mehrheit von zwei Dritteln der anwesenden Mitglieder beschlossen werden.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">§ 11 Auflösung des Vereins</h3>
|
||||
<div class="space-y-2 text-gray-700">
|
||||
<p><strong>(1)</strong> Die Auflösung des Vereins kann nur in einer Mitgliederversammlung mit einer Mehrheit von drei Vierteln der anwesenden Mitglieder beschlossen werden.</p>
|
||||
<p><strong>(2)</strong> Bei Auflösung des Vereins fällt das Vereinsvermögen an eine gemeinnützige Organisation.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-12 p-6 bg-primary-50 rounded-lg border border-primary-200">
|
||||
<div class="flex flex-col sm:flex-row gap-4 items-center justify-between">
|
||||
<div>
|
||||
<h4 class="text-lg font-semibold text-primary-800 mb-2">Satzung als PDF herunterladen</h4>
|
||||
<p class="text-primary-700 text-sm">
|
||||
Laden Sie die vollständige Satzung als PDF-Dokument herunter.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/documents/satzung.pdf"
|
||||
target="_blank"
|
||||
class="inline-flex items-center px-6 py-3 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
<FileText :size="20" class="mr-2" />
|
||||
PDF herunterladen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { FileText } from 'lucide-vue-next'
|
||||
|
||||
useHead({
|
||||
title: 'Satzung - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
214
pages/spielsysteme.vue
Normal file
214
pages/spielsysteme.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<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">
|
||||
Spielsysteme
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<p class="text-xl text-gray-600 mb-12">
|
||||
Übersicht der verschiedenen Mannschafts-Spielsysteme im Tischtennis
|
||||
</p>
|
||||
|
||||
<!-- Filter -->
|
||||
<div class="mb-8 flex flex-wrap gap-4">
|
||||
<button
|
||||
v-for="kategorie in verfuegbareKategorien"
|
||||
:key="kategorie"
|
||||
@click="selectedCategory = kategorie"
|
||||
:class="[
|
||||
'px-4 py-2 rounded-lg font-medium transition-colors',
|
||||
selectedCategory === kategorie
|
||||
? 'bg-primary-600 text-white'
|
||||
: 'bg-white text-gray-700 hover:bg-gray-100 border border-gray-300'
|
||||
]"
|
||||
>
|
||||
{{ kategorie }}
|
||||
</button>
|
||||
<button
|
||||
@click="selectedCategory = 'alle'"
|
||||
:class="[
|
||||
'px-4 py-2 rounded-lg font-medium transition-colors',
|
||||
selectedCategory === 'alle'
|
||||
? 'bg-primary-600 text-white'
|
||||
: 'bg-white text-gray-700 hover:bg-gray-100 border border-gray-300'
|
||||
]"
|
||||
>
|
||||
Alle Kategorien
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Spielsysteme -->
|
||||
<div v-if="filteredSystems.length > 0" class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div
|
||||
v-for="system in filteredSystems"
|
||||
:key="system.name"
|
||||
class="bg-white rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow border border-gray-100"
|
||||
>
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div class="flex-1">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-2">
|
||||
{{ system.name }}
|
||||
</h3>
|
||||
<div class="flex items-center mb-3">
|
||||
<Users :size="16" class="text-primary-600 mr-2" />
|
||||
<span class="text-sm font-medium text-gray-600">{{ system.mannschaftsgroesse }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
:class="[
|
||||
'px-3 py-1 rounded-full text-xs font-medium',
|
||||
getCategoryColor(system.kategorie)
|
||||
]"
|
||||
>
|
||||
{{ system.kategorie }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-gray-700 mb-4 leading-relaxed">
|
||||
{{ system.description }}
|
||||
</p>
|
||||
|
||||
<div class="space-y-2 text-sm">
|
||||
<div v-if="system.spielabfolge" class="flex items-center">
|
||||
<Calendar :size="14" class="text-primary-600 mr-2 flex-shrink-0" />
|
||||
<span class="text-gray-600"><strong>Spielabfolge:</strong> {{ system.spielabfolge }}</span>
|
||||
</div>
|
||||
<div v-if="system.anzahl_spiele" class="flex items-center">
|
||||
<Hash :size="14" class="text-primary-600 mr-2 flex-shrink-0" />
|
||||
<span class="text-gray-600"><strong>Anzahl Spiele:</strong> {{ system.anzahl_spiele }}</span>
|
||||
</div>
|
||||
<div v-if="system.besonderheiten" class="flex items-center">
|
||||
<Star :size="14" class="text-primary-600 mr-2 flex-shrink-0" />
|
||||
<span class="text-gray-600"><strong>Besonderheiten:</strong> {{ system.besonderheiten }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center py-12 bg-white rounded-xl shadow-lg">
|
||||
<Settings :size="48" class="text-gray-400 mx-auto mb-4" />
|
||||
<p class="text-gray-600">Keine Spielsysteme für die ausgewählte Kategorie gefunden.</p>
|
||||
</div>
|
||||
|
||||
<!-- Zusätzliche Informationen -->
|
||||
<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 flex items-center">
|
||||
<BookOpen :size="28" class="mr-3" />
|
||||
Weitere Informationen
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
<p class="text-primary-100 leading-relaxed">
|
||||
Die Spielsysteme werden je nach Liga und Verband unterschiedlich eingesetzt.
|
||||
Die meisten regionalen Ligen verwenden das Bundessystem oder das Braunschweiger System.
|
||||
</p>
|
||||
<p class="text-primary-100 leading-relaxed">
|
||||
Internationale Wettkämpfe folgen meist den FIT-Systemen (Corbillon-Cup für Damen,
|
||||
Swaythling-Cup für Herren).
|
||||
</p>
|
||||
<div class="mt-6">
|
||||
<a
|
||||
href="https://www.wikiwand.com/de/Tischtennis#Spielsysteme"
|
||||
target="_blank"
|
||||
class="inline-flex items-center px-6 py-3 bg-white text-primary-600 font-semibold rounded-lg hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<ExternalLink :size="20" class="mr-2" />
|
||||
Detaillierte Erklärungen auf Wikiwand
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { Users, Settings, BookOpen, ExternalLink, Calendar, Hash, Star } from 'lucide-vue-next'
|
||||
|
||||
const systems = ref([])
|
||||
const selectedCategory = ref('alle')
|
||||
|
||||
const loadSystems = async () => {
|
||||
try {
|
||||
const response = await fetch('/data/spielsysteme.csv')
|
||||
if (!response.ok) return
|
||||
|
||||
const csv = await response.text()
|
||||
const lines = csv.split('\n').filter(line => line.trim() !== '')
|
||||
|
||||
if (lines.length < 2) return
|
||||
|
||||
systems.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 < 8) return null
|
||||
|
||||
return {
|
||||
name: values[0].trim(),
|
||||
description: values[1].trim(),
|
||||
mannschaftsgroesse: values[2].trim(),
|
||||
kategorie: values[3].trim(),
|
||||
details: values[4].trim(),
|
||||
spielabfolge: values[5].trim(),
|
||||
anzahl_spiele: values[6].trim(),
|
||||
besonderheiten: values[7].trim()
|
||||
}
|
||||
}).filter(system => system !== null)
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Spielsysteme:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const verfuegbareKategorien = computed(() => {
|
||||
const kategorien = [...new Set(systems.value.map(s => s.kategorie).filter(k => k !== ''))]
|
||||
return kategorien.sort()
|
||||
})
|
||||
|
||||
const filteredSystems = computed(() => {
|
||||
if (selectedCategory.value === 'alle') {
|
||||
return systems.value
|
||||
}
|
||||
return systems.value.filter(s => s.kategorie === selectedCategory.value)
|
||||
})
|
||||
|
||||
const getCategoryColor = (kategorie) => {
|
||||
const colors = {
|
||||
'Klassisch': 'bg-blue-100 text-blue-800',
|
||||
'Flexibel': 'bg-green-100 text-green-800',
|
||||
'Strukturiert': 'bg-purple-100 text-purple-800',
|
||||
'Modifiziert': 'bg-orange-100 text-orange-800',
|
||||
'International': 'bg-red-100 text-red-800',
|
||||
'Standard': 'bg-gray-100 text-gray-800',
|
||||
'Professionell': 'bg-yellow-100 text-yellow-800'
|
||||
}
|
||||
return colors[kategorie] || 'bg-gray-100 text-gray-800'
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
loadSystems()
|
||||
})
|
||||
|
||||
useHead({
|
||||
title: 'Spielsysteme - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
159
pages/termine.vue
Normal file
159
pages/termine.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center mb-12">
|
||||
<h1 class="text-4xl sm:text-5xl font-display font-bold text-gray-900 mb-4">
|
||||
Termine & Events
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mx-auto mb-6" />
|
||||
<p class="text-xl text-gray-600 max-w-3xl mx-auto">
|
||||
Alle kommenden Termine und Veranstaltungen des Harheimer TC
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="naechsteTermine.length > 0" class="space-y-4">
|
||||
<div
|
||||
v-for="(termin, index) in naechsteTermine"
|
||||
:key="index"
|
||||
class="bg-white rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow"
|
||||
>
|
||||
<div class="flex items-start space-x-4">
|
||||
<div class="flex-shrink-0 w-16 h-16 bg-primary-600 rounded-xl flex flex-col items-center justify-center text-white">
|
||||
<span class="text-2xl font-bold">{{ formatDay(termin.datum) }}</span>
|
||||
<span class="text-xs">{{ formatMonth(termin.datum) }}</span>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-1">{{ termin.titel }}</h3>
|
||||
<p class="text-gray-600 mb-2">{{ termin.beschreibung }}</p>
|
||||
<p class="text-sm text-gray-500">{{ formatFullDate(termin.datum) }}</p>
|
||||
</div>
|
||||
<span :class="[
|
||||
'px-3 py-1 text-sm font-medium rounded-full',
|
||||
termin.kategorie === 'Turnier' ? 'bg-yellow-100 text-yellow-800' : 'bg-blue-100 text-blue-800'
|
||||
]">
|
||||
{{ termin.kategorie }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center py-16 bg-white rounded-xl shadow-lg">
|
||||
<Calendar :size="64" class="text-gray-400 mx-auto mb-4" />
|
||||
<h3 class="text-2xl font-semibold text-gray-900 mb-2">Keine kommenden Termine</h3>
|
||||
<p class="text-gray-600">
|
||||
Aktuell sind keine Termine geplant. Schauen Sie bald wieder vorbei!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-12 bg-primary-50 border border-primary-100 rounded-xl p-6">
|
||||
<h3 class="text-lg font-semibold text-primary-900 mb-2">
|
||||
Hinweis
|
||||
</h3>
|
||||
<p class="text-primary-800">
|
||||
Alle Termine sind vorbehaltlich kurzfristiger Änderungen.
|
||||
Bei Fragen zu einzelnen Veranstaltungen kontaktieren Sie uns gerne.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { Calendar } from 'lucide-vue-next'
|
||||
|
||||
const termine = ref([])
|
||||
|
||||
const naechsteTermine = computed(() => {
|
||||
const heute = new Date()
|
||||
heute.setHours(0, 0, 0, 0)
|
||||
|
||||
return termine.value
|
||||
.filter(t => {
|
||||
const terminDatum = new Date(t.datum)
|
||||
return terminDatum >= heute
|
||||
})
|
||||
.sort((a, b) => new Date(a.datum) - new Date(b.datum))
|
||||
})
|
||||
|
||||
const formatDay = (dateString) => {
|
||||
const date = new Date(dateString)
|
||||
return date.getDate()
|
||||
}
|
||||
|
||||
const formatMonth = (dateString) => {
|
||||
const date = new Date(dateString)
|
||||
const monate = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']
|
||||
return monate[date.getMonth()]
|
||||
}
|
||||
|
||||
const formatFullDate = (dateString) => {
|
||||
const date = new Date(dateString)
|
||||
const wochentage = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag']
|
||||
const monate = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember']
|
||||
|
||||
return `${wochentage[date.getDay()]}, ${date.getDate()}. ${monate[date.getMonth()]} ${date.getFullYear()}`
|
||||
}
|
||||
|
||||
const loadTermine = async () => {
|
||||
try {
|
||||
const response = await fetch('/data/termine.csv')
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const csv = await response.text()
|
||||
const lines = csv.split('\n').filter(line => line.trim() !== '')
|
||||
|
||||
if (lines.length < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
termine.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 < 4) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
datum: values[0].trim(),
|
||||
titel: values[1].trim(),
|
||||
beschreibung: values[2].trim(),
|
||||
kategorie: values[3].trim()
|
||||
}
|
||||
}).filter(termin => termin !== null)
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Termine:', error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadTermine()
|
||||
})
|
||||
|
||||
useHead({
|
||||
title: 'Termine & Events - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
76
pages/training/anfaenger.vue
Normal file
76
pages/training/anfaenger.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-7xl 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">
|
||||
Tischtennis für Anfänger
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<div class="prose prose-lg max-w-none">
|
||||
<p class="text-xl text-gray-600 mb-8">
|
||||
Du möchtest mit Tischtennis anfangen? Perfekt! Bei uns bist du richtig.
|
||||
</p>
|
||||
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg not-prose mb-8">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">
|
||||
Was du wissen solltest
|
||||
</h3>
|
||||
<ul class="space-y-3">
|
||||
<li class="flex items-start">
|
||||
<Check :size="24" class="text-primary-600 mr-3 flex-shrink-0 mt-0.5" />
|
||||
<span class="text-gray-700">Keine Vorkenntnisse nötig</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<Check :size="24" class="text-primary-600 mr-3 flex-shrink-0 mt-0.5" />
|
||||
<span class="text-gray-700">Schläger und Material werden gestellt</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<Check :size="24" class="text-primary-600 mr-3 flex-shrink-0 mt-0.5" />
|
||||
<span class="text-gray-700">Sportkleidung und Hallenschuhe mitbringen</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<Check :size="24" class="text-primary-600 mr-3 flex-shrink-0 mt-0.5" />
|
||||
<span class="text-gray-700">3x kostenlos Probetraining</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<Check :size="24" class="text-primary-600 mr-3 flex-shrink-0 mt-0.5" />
|
||||
<span class="text-gray-700">Einstieg jederzeit möglich</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="bg-primary-50 p-8 rounded-xl border border-primary-100 not-prose">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">
|
||||
Anfängergruppen
|
||||
</h3>
|
||||
<div class="space-y-4 mb-6">
|
||||
<div>
|
||||
<h4 class="font-semibold text-gray-900 mb-1">Schüler/Jugend (ab 6 Jahre)</h4>
|
||||
<p class="text-gray-600">Dienstag, 17:30 - 19:30 Uhr</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold text-gray-900 mb-1">Damen und Herren</h4>
|
||||
<p class="text-gray-600">Dienstag & Donnerstag, 19:30 - 22:30 Uhr</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NuxtLink
|
||||
to="/kontakt"
|
||||
class="inline-flex items-center px-6 py-3 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
Zum Probetraining anmelden
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Check } from 'lucide-vue-next'
|
||||
|
||||
useHead({
|
||||
title: 'Für Anfänger - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
102
pages/training/index.vue
Normal file
102
pages/training/index.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-7xl 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">
|
||||
Trainingszeiten
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<!-- Trainingsort -->
|
||||
<div class="bg-white rounded-xl shadow-lg p-8 mb-12">
|
||||
<div class="flex items-start space-x-4 mb-6">
|
||||
<MapPin :size="32" class="text-primary-600 flex-shrink-0" />
|
||||
<div>
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-4">Trainingsort</h2>
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">
|
||||
Sporthalle der Grundschule Harheim
|
||||
</h3>
|
||||
<p class="text-gray-700 mb-1">In den Schafgärten 25</p>
|
||||
<p class="text-gray-700 mb-4">60437 Frankfurt/Main</p>
|
||||
<a
|
||||
href="https://www.google.com/maps/search/?api=1&query=In+den+Schafgärten+25+60437+Frankfurt"
|
||||
target="_blank"
|
||||
class="inline-flex items-center px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white font-medium rounded-lg transition-colors text-sm"
|
||||
>
|
||||
<MapPin :size="16" class="mr-2" />
|
||||
Anfahrtsplan anzeigen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trainingszeiten -->
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6">
|
||||
Trainingszeiten
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-6 mb-12">
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-2">Damen und Herren</h3>
|
||||
<div class="space-y-2">
|
||||
<p class="text-lg font-semibold text-primary-600">
|
||||
Dienstag: 19:30 - 22:30 Uhr
|
||||
</p>
|
||||
<p class="text-lg font-semibold text-primary-600">
|
||||
Donnerstag: 19:30 - 22:30 Uhr
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Clock :size="32" class="text-primary-600" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-2">Schüler/Jugend</h3>
|
||||
<p class="text-gray-600 mb-2">Ab 6 Jahre</p>
|
||||
<p class="text-lg font-semibold text-primary-600">
|
||||
Dienstag: 17:30 - 19:30 Uhr
|
||||
</p>
|
||||
</div>
|
||||
<Clock :size="32" class="text-primary-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-12 bg-primary-50 p-8 rounded-xl border border-primary-100">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-4">
|
||||
Interessiert?
|
||||
</h3>
|
||||
<p class="text-gray-600 mb-6">
|
||||
Komm einfach zum Schnuppertraining vorbei oder kontaktiere uns für weitere Informationen!
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<NuxtLink
|
||||
to="/training/anfaenger"
|
||||
class="inline-flex items-center px-6 py-3 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
Infos für Anfänger
|
||||
</NuxtLink>
|
||||
<NuxtLink
|
||||
to="/kontakt"
|
||||
class="inline-flex items-center px-6 py-3 bg-white hover:bg-gray-50 text-primary-600 border-2 border-primary-600 font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
Kontakt
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Clock, MapPin } from 'lucide-vue-next'
|
||||
|
||||
useHead({
|
||||
title: 'Trainingszeiten - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
52
pages/training/trainer.vue
Normal file
52
pages/training/trainer.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-7xl 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">
|
||||
Unsere Trainer
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<p class="text-xl text-gray-600 mb-12">
|
||||
Erfahrene und qualifizierte Trainer für alle Leistungsstufen
|
||||
</p>
|
||||
|
||||
<div class="grid md:grid-cols-3 gap-8">
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-2">C-Trainer</h3>
|
||||
<p class="text-gray-600 mb-4">Torsten Schulz</p>
|
||||
<p class="text-sm text-gray-500">
|
||||
Lizenz: C-Trainer<br />
|
||||
Schwerpunkt: Nachwuchsförderung<br />
|
||||
Erwachsenen bei Wunsch zur Verfügung
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-2">Kindertrainer</h3>
|
||||
<p class="text-gray-600 mb-4">Thomas Steinbrech</p>
|
||||
<p class="text-sm text-gray-500">
|
||||
Lizenz: Kindertrainer<br />
|
||||
Schwerpunkt: Nachwuchsförderung
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg">
|
||||
<h3 class="text-2xl font-display font-bold text-gray-900 mb-2">Assistenztrainerin</h3>
|
||||
<p class="text-gray-600 mb-4">Magda Schwallbach</p>
|
||||
<p class="text-sm text-gray-500">
|
||||
Lizenz: Assistenztrainerin<br />
|
||||
Schwerpunkt: Unterstützung & Betreuung
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
useHead({
|
||||
title: 'Trainer - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
200
pages/tt-regeln.vue
Normal file
200
pages/tt-regeln.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<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">
|
||||
Tischtennis-Regeln
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<p class="text-xl text-gray-600 mb-12">
|
||||
Offizielle Regeln und Bestimmungen für den Tischtennissport
|
||||
</p>
|
||||
|
||||
<!-- Offizielle Regeln -->
|
||||
<div class="grid md:grid-cols-2 gap-8 mb-12 items-stretch">
|
||||
<!-- ITTF-Reglement -->
|
||||
<div class="bg-white rounded-xl shadow-lg p-8 border border-gray-100 flex flex-col h-full">
|
||||
<div class="flex items-center mb-6">
|
||||
<div class="w-12 h-12 bg-gradient-to-br from-blue-500 to-blue-600 rounded-xl flex items-center justify-center mr-4">
|
||||
<Globe :size="24" class="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900">Offizielles ITTF-Reglement</h2>
|
||||
<p class="text-gray-600">Internationale Tischtennis-Regeln</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-gray-700 mb-6 leading-relaxed flex-grow">
|
||||
Die offiziellen Regeln des Internationalen Tischtennis-Verbands (ITTF)
|
||||
gelten weltweit für alle Wettkämpfe und Turniere.
|
||||
</p>
|
||||
|
||||
<div class="space-y-4 mt-auto">
|
||||
<a
|
||||
href="https://www.tischtennis.de/dttb/regeln-satzung/satzung-ordnungen.html"
|
||||
target="_blank"
|
||||
class="block w-full px-6 py-4 bg-primary-600 hover:bg-primary-700 text-white font-bold rounded-lg transition-colors text-center text-lg border-2 border-primary-600 shadow-lg"
|
||||
>
|
||||
🔗 Offizielle ITTF-Regeln aufrufen
|
||||
</a>
|
||||
<div class="text-center">
|
||||
<p class="text-sm text-gray-600 font-medium">
|
||||
Deutsche Übersetzung auf tischtennis.de
|
||||
</p>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
Internationale Tischtennis-Regeln A & B
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Vereinfachte Regeln -->
|
||||
<div class="bg-white rounded-xl shadow-lg p-8 border border-gray-100 flex flex-col h-full">
|
||||
<div class="flex items-center mb-6">
|
||||
<div class="w-12 h-12 bg-gradient-to-br from-primary-500 to-primary-600 rounded-xl flex items-center justify-center mr-4">
|
||||
<FileText :size="24" class="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900">Tischtennis-Regeln Light</h2>
|
||||
<p class="text-gray-600">Vereinfachte Übersicht</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-gray-700 mb-6 leading-relaxed flex-grow">
|
||||
Eine kompakte Übersicht der wichtigsten Tischtennis-Regeln
|
||||
für Einsteiger und Hobbyspieler. Diese vereinfachte Version
|
||||
erklärt die Grundlagen verständlich und übersichtlich.
|
||||
</p>
|
||||
|
||||
<div class="space-y-3 mt-auto">
|
||||
<a
|
||||
href="/documents/Tischtennisregeln light.pdf"
|
||||
target="_blank"
|
||||
download
|
||||
class="block w-full px-6 py-4 bg-primary-600 hover:bg-primary-700 text-white font-bold rounded-lg transition-colors text-center text-lg border-2 border-primary-600 shadow-lg"
|
||||
>
|
||||
⬇️ Regeln Light herunterladen
|
||||
</a>
|
||||
<p class="text-sm text-gray-500 text-center">
|
||||
PDF-Dokument (vereinfachte Fassung)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grundregeln Übersicht -->
|
||||
<div class="bg-white rounded-xl shadow-lg p-8 mb-12">
|
||||
<h2 class="text-3xl font-display font-bold text-gray-900 mb-8 text-center">
|
||||
Grundregeln im Überblick
|
||||
</h2>
|
||||
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div class="text-center p-6 bg-gray-50 rounded-lg">
|
||||
<div class="w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Target :size="32" class="text-primary-600" />
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">Spielfeld</h3>
|
||||
<p class="text-gray-600 text-sm">
|
||||
Tisch: 2,74m × 1,525m, Höhe: 76cm<br>
|
||||
Netz: 15,25cm hoch
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center p-6 bg-gray-50 rounded-lg">
|
||||
<div class="w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Circle :size="32" class="text-primary-600" />
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">Ball</h3>
|
||||
<p class="text-gray-600 text-sm">
|
||||
Durchmesser: 40mm<br>
|
||||
Gewicht: 2,7g
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center p-6 bg-gray-50 rounded-lg">
|
||||
<div class="w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Zap :size="32" class="text-primary-600" />
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">Schläger</h3>
|
||||
<p class="text-gray-600 text-sm">
|
||||
Belag: schwarz + farbig<br>
|
||||
(rot, grün, pink, blau, gelb, lila)<br>
|
||||
Holz: mindestens 85%
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center p-6 bg-gray-50 rounded-lg">
|
||||
<div class="w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Play :size="32" class="text-primary-600" />
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">Aufschlag</h3>
|
||||
<p class="text-gray-600 text-sm">
|
||||
Ball muss sichtbar hochgeworfen werden<br>
|
||||
Mindestens 16cm Höhe
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center p-6 bg-gray-50 rounded-lg">
|
||||
<div class="w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Trophy :size="32" class="text-primary-600" />
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">Satz</h3>
|
||||
<p class="text-gray-600 text-sm">
|
||||
Gewinn bei 11 Punkten<br>
|
||||
Mindestens 2 Punkte Vorsprung
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center p-6 bg-gray-50 rounded-lg">
|
||||
<div class="w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Users :size="32" class="text-primary-600" />
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">Spiel</h3>
|
||||
<p class="text-gray-600 text-sm">
|
||||
Best of 5 oder 7 Sätze<br>
|
||||
Wechsel alle 2 Punkte
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Zusätzliche Informationen -->
|
||||
<div class="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 flex items-center">
|
||||
<BookOpen :size="28" class="mr-3" />
|
||||
Weitere Informationen
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
<p class="text-primary-100 leading-relaxed">
|
||||
Die offiziellen ITTF-Regeln werden regelmäßig aktualisiert und gelten für alle
|
||||
internationalen Wettkämpfe. Für regionale Turniere können abweichende
|
||||
Bestimmungen gelten.
|
||||
</p>
|
||||
<p class="text-primary-100 leading-relaxed">
|
||||
Bei Fragen zu spezifischen Regeln wenden Sie sich an den
|
||||
<a href="https://www.tischtennis.de" target="_blank" class="underline hover:text-white">
|
||||
Deutschen Tischtennis-Bund (DTTB)
|
||||
</a> oder Ihren regionalen Verband.
|
||||
</p>
|
||||
<div class="mt-6 text-center">
|
||||
<a
|
||||
href="https://www.tischtennis.de/dttb/regeln-satzung/satzung-ordnungen.html"
|
||||
target="_blank"
|
||||
class="inline-flex items-center px-8 py-4 bg-primary-600 hover:bg-primary-700 text-white font-bold rounded-lg transition-colors text-lg border-2 border-primary-600 shadow-lg"
|
||||
>
|
||||
🔗 Alle DTTB-Regeln und Ordnungen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Globe, FileText, Download, ExternalLink, Target, Circle, Zap, Play, Trophy, Users, BookOpen } from 'lucide-vue-next'
|
||||
|
||||
useHead({
|
||||
title: 'TT-Regeln - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
14
pages/ueber-uns.vue
Normal file
14
pages/ueber-uns.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="min-h-full">
|
||||
<About />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import About from '~/components/About.vue'
|
||||
|
||||
useHead({
|
||||
title: 'Über uns - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
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>
|
||||
65
pages/vorstand.vue
Normal file
65
pages/vorstand.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="min-h-full py-16 bg-gray-50">
|
||||
<div class="max-w-7xl 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">
|
||||
Vorstand
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-8" />
|
||||
|
||||
<div class="prose prose-lg max-w-none">
|
||||
<p class="text-xl text-gray-600 mb-8">
|
||||
Unser engagiertes Vorstandsteam leitet den Harheimer TC mit Herz und Sachverstand.
|
||||
</p>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-8 not-prose">
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-2">Vorsitzender</h3>
|
||||
<h4 class="text-lg font-semibold text-primary-600 mb-3">Roger Dichmann</h4>
|
||||
<div class="space-y-1 text-gray-600">
|
||||
<p>Reginastr. 46</p>
|
||||
<p>60437 Frankfurt</p>
|
||||
<p>Tel. 06101-9953015</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-2">Stellvertreter des Vorsitzenden</h3>
|
||||
<h4 class="text-lg font-semibold text-primary-600 mb-3">Jürgen Kratz</h4>
|
||||
<div class="space-y-1 text-gray-600">
|
||||
<p>Bürgerstr. 68</p>
|
||||
<p>60437 Frankfurt</p>
|
||||
<p>Tel. 06101-43221</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-2">Kassenwart</h3>
|
||||
<h4 class="text-lg font-semibold text-primary-600 mb-3">Olaf Nüßlein</h4>
|
||||
<div class="space-y-1 text-gray-600">
|
||||
<p>Am Eschbachtal 52</p>
|
||||
<p>60437 Frankfurt</p>
|
||||
<p>Tel. 06101-47469</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
|
||||
<h3 class="text-xl font-display font-bold text-gray-900 mb-2">Schriftführer</h3>
|
||||
<h4 class="text-lg font-semibold text-primary-600 mb-3">Jürgen Dichmann</h4>
|
||||
<div class="space-y-1 text-gray-600">
|
||||
<p>In der Fuchskaut 4</p>
|
||||
<p>60437 Frankfurt</p>
|
||||
<p>Tel. 06101-4992227</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
useHead({
|
||||
title: 'Vorstand - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user