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:
Torsten Schulz (local)
2025-10-21 00:41:12 +02:00
commit 737c3064bd
61 changed files with 25816 additions and 0 deletions

106
components/Gallery.vue Normal file
View File

@@ -0,0 +1,106 @@
<template>
<section v-if="images.length > 0" id="gallery" class="py-16 sm:py-20 bg-gradient-to-b from-white to-gray-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-16">
<h2 class="text-4xl sm:text-5xl font-display font-bold text-gray-900 mb-4">
Galerie
</h2>
<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">
Eindrücke von unserem Verein
</p>
</div>
<div class="grid sm:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-2">
<div
v-for="image in images"
:key="image.filename"
class="group relative w-20 h-20 rounded-md overflow-hidden shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer"
@click="openLightbox(image)"
>
<img
:src="`/galerie/${image.filename}`"
:alt="image.title"
class="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700"
/>
<div class="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-end">
<p class="text-white font-semibold text-xs p-1 truncate">{{ image.title }}</p>
</div>
</div>
</div>
<!-- Lightbox Modal -->
<div
v-if="lightboxImage"
class="fixed inset-0 z-50 bg-black/90 flex items-center justify-center p-4"
@click="closeLightbox"
>
<div class="relative w-full h-full flex items-center justify-center">
<button
@click.stop="closeLightbox"
class="absolute top-4 right-4 z-10 w-10 h-10 bg-white/20 hover:bg-white/30 rounded-full flex items-center justify-center text-white transition-colors"
>
<X :size="24" />
</button>
<img
:src="`/galerie/${lightboxImage.filename}`"
:alt="lightboxImage.title"
class="max-w-[80vw] max-h-[80vh] object-contain rounded-lg"
@click.stop
/>
<div class="absolute bottom-4 left-4 right-4 text-center">
<p class="text-white font-semibold text-lg bg-black/50 rounded-lg px-4 py-2">
{{ lightboxImage.title }}
</p>
</div>
</div>
</div>
</div>
</section>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { X } from 'lucide-vue-next'
const images = ref([])
const lightboxImage = ref(null)
const loadImages = async () => {
try {
const response = await $fetch('/api/galerie')
images.value = response || []
} catch (error) {
console.error('Fehler beim Laden der Galerie-Bilder:', error)
images.value = []
}
}
const openLightbox = (image) => {
lightboxImage.value = image
document.body.style.overflow = 'hidden' // Verhindert Scrollen im Hintergrund
}
const closeLightbox = () => {
lightboxImage.value = null
document.body.style.overflow = 'auto' // Erlaubt wieder Scrollen
}
// ESC-Taste zum Schließen
const handleKeydown = (event) => {
if (event.key === 'Escape' && lightboxImage.value) {
closeLightbox()
}
}
onMounted(() => {
loadImages()
document.addEventListener('keydown', handleKeydown)
})
onUnmounted(() => {
document.removeEventListener('keydown', handleKeydown)
document.body.style.overflow = 'auto' // Cleanup
})
</script>