Enhance Vereinsmeisterschaften and Vorstand pages with image support for players and board members. Implement lightbox functionality for player images in Vereinsmeisterschaften. Update CSV handling to include image filenames for better data management. Refactor components to utilize PersonCard for board members, improving code readability and maintainability.
This commit is contained in:
@@ -88,13 +88,37 @@
|
||||
>
|
||||
{{ ergebnis.platz }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-semibold text-gray-900">
|
||||
{{ ergebnis.spieler1 }}
|
||||
<span v-if="ergebnis.spieler2" class="text-gray-600">
|
||||
/ {{ ergebnis.spieler2 }}
|
||||
<div class="flex items-center gap-2">
|
||||
<div v-if="ergebnis.imageFilename1" class="flex-shrink-0">
|
||||
<img
|
||||
:src="`/api/personen/${ergebnis.imageFilename1}?width=40&height=40`"
|
||||
:alt="ergebnis.spieler1"
|
||||
class="w-10 h-10 rounded-full object-cover border-2 border-gray-300 cursor-pointer hover:border-primary-500 transition-colors"
|
||||
loading="lazy"
|
||||
@click="openLightbox(ergebnis.imageFilename1, ergebnis.spieler1)"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-semibold text-gray-900">
|
||||
{{ ergebnis.spieler1 }}
|
||||
</span>
|
||||
</span>
|
||||
<span v-if="ergebnis.spieler2" class="text-gray-600">
|
||||
<span v-if="ergebnis.imageFilename2" class="ml-2 inline-flex items-center gap-2">
|
||||
/
|
||||
<img
|
||||
:src="`/api/personen/${ergebnis.imageFilename2}?width=40&height=40`"
|
||||
:alt="ergebnis.spieler2"
|
||||
class="w-10 h-10 rounded-full object-cover border-2 border-gray-300 cursor-pointer hover:border-primary-500 transition-colors"
|
||||
loading="lazy"
|
||||
@click="openLightbox(ergebnis.imageFilename2, ergebnis.spieler2)"
|
||||
/>
|
||||
{{ ergebnis.spieler2 }}
|
||||
</span>
|
||||
<span v-else class="text-gray-600">
|
||||
/ {{ ergebnis.spieler2 }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
@@ -147,6 +171,37 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lightbox für Bilder -->
|
||||
<div
|
||||
v-if="lightboxImage"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-90 p-4"
|
||||
@click="closeLightbox"
|
||||
tabindex="0"
|
||||
@keydown="handleLightboxKeydown"
|
||||
>
|
||||
<div class="relative max-w-5xl max-h-full" @click.stop>
|
||||
<!-- Close Button -->
|
||||
<button
|
||||
@click="closeLightbox"
|
||||
class="absolute top-4 right-4 text-white hover:text-gray-300 z-10 bg-black bg-opacity-50 rounded-full p-3"
|
||||
aria-label="Schließen"
|
||||
>
|
||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<img
|
||||
:src="`/api/personen/${lightboxImage.filename}`"
|
||||
:alt="lightboxImage.name"
|
||||
class="max-w-[90%] max-h-[90vh] object-contain mx-auto"
|
||||
/>
|
||||
<div class="mt-4 text-white text-center">
|
||||
<h3 class="text-xl font-semibold">{{ lightboxImage.name }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -156,6 +211,7 @@ import { Trophy } from 'lucide-vue-next'
|
||||
|
||||
const results = ref([])
|
||||
const selectedYear = ref('alle')
|
||||
const lightboxImage = ref(null)
|
||||
|
||||
const loadResults = async () => {
|
||||
try {
|
||||
@@ -188,6 +244,7 @@ const loadResults = async () => {
|
||||
}
|
||||
values.push(current.trim())
|
||||
|
||||
// Mindestens 6 Spalten erforderlich (die neuen Bildspalten sind optional)
|
||||
if (values.length < 6) return null
|
||||
|
||||
return {
|
||||
@@ -196,7 +253,9 @@ const loadResults = async () => {
|
||||
platz: values[2].trim(),
|
||||
spieler1: values[3].trim(),
|
||||
spieler2: values[4].trim(),
|
||||
bemerkung: values[5].trim()
|
||||
bemerkung: values[5].trim(),
|
||||
imageFilename1: values[6]?.trim() || '',
|
||||
imageFilename2: values[7]?.trim() || ''
|
||||
}
|
||||
}).filter(result => result !== null)
|
||||
} catch (error) {
|
||||
@@ -268,6 +327,26 @@ const totalDoubles = computed(() => {
|
||||
return results.value.filter(r => r.kategorie === 'Doppel' && r.platz === '1').length
|
||||
})
|
||||
|
||||
function openLightbox(filename, name) {
|
||||
lightboxImage.value = { filename, name }
|
||||
document.body.style.overflow = 'hidden'
|
||||
setTimeout(() => {
|
||||
const modal = document.querySelector('[tabindex="0"]')
|
||||
if (modal) modal.focus()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
function closeLightbox() {
|
||||
lightboxImage.value = null
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
|
||||
function handleLightboxKeydown(event) {
|
||||
if (event.key === 'Escape') {
|
||||
closeLightbox()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadResults()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user