Files
harheimertc/pages/spielplan.vue

196 lines
7.1 KiB
Vue

<template>
<div class="min-h-screen bg-gray-50">
<!-- Header -->
<div class="bg-white shadow-sm">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="flex items-center justify-between">
<div>
<h1 class="text-3xl font-bold text-gray-900">Spielplan</h1>
<p class="mt-2 text-gray-600">Aktuelle Termine und Spiele des Vereins</p>
</div>
<div class="flex items-center space-x-4">
<button @click="refreshData"
:disabled="isLoading"
class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors disabled:bg-gray-400">
<svg v-if="isLoading" class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
<svg v-else class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
Aktualisieren
</button>
</div>
</div>
</div>
</div>
<!-- Content -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Loading State -->
<div v-if="isLoading" class="text-center py-12">
<svg class="w-8 h-8 text-gray-400 mx-auto mb-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
<p class="text-gray-600">Spielplan wird geladen...</p>
</div>
<!-- Error State -->
<div v-else-if="error" class="bg-red-50 border border-red-200 rounded-lg p-6 text-center">
<svg class="w-12 h-12 text-red-400 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
</svg>
<h3 class="text-lg font-medium text-red-800 mb-2">Fehler beim Laden</h3>
<p class="text-red-600 mb-4">{{ error }}</p>
<button @click="loadData" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors">
Erneut versuchen
</button>
</div>
<!-- Empty State -->
<div v-else-if="!spielplanData || spielplanData.length === 0" class="text-center py-12 bg-white rounded-xl shadow-lg">
<svg class="w-12 h-12 text-gray-400 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
<h3 class="text-lg font-medium text-gray-900 mb-2">Kein Spielplan verfügbar</h3>
<p class="text-gray-600">Es wurden noch keine Spielplandaten hochgeladen.</p>
</div>
<!-- Spielplan Table -->
<div v-else class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-xl font-semibold text-gray-900">Aktuelle Spiele</h2>
<p class="text-sm text-gray-600 mt-1">{{ spielplanData.length }} Einträge</p>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th v-for="header in headers" :key="header"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{{ formatHeader(header) }}
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="(row, index) in spielplanData" :key="index"
:class="index % 2 === 0 ? 'bg-white' : 'bg-gray-50'">
<td v-for="header in headers" :key="header"
class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span v-if="header.toLowerCase().includes('datum')" class="font-mono">
{{ formatDate(row[header]) }}
</span>
<span v-else-if="header.toLowerCase().includes('uhrzeit')" class="font-mono">
{{ formatTime(row[header]) }}
</span>
<span v-else>
{{ row[header] || '-' }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="px-6 py-4 bg-gray-50 border-t border-gray-200">
<p class="text-xs text-gray-500">
Letzte Aktualisierung: {{ lastUpdated }}
</p>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
useHead({
title: 'Spielplan - Harheimer TC'
})
const spielplanData = ref([])
const headers = ref([])
const isLoading = ref(false)
const error = ref(null)
const lastUpdated = ref('')
const loadData = async () => {
isLoading.value = true
error.value = null
try {
const response = await fetch('/api/spielplan')
const result = await response.json()
if (result.success) {
spielplanData.value = result.data
headers.value = result.headers
lastUpdated.value = new Date().toLocaleString('de-DE')
} else {
error.value = result.message
}
} catch (err) {
console.error('Fehler beim Laden des Spielplans:', err)
error.value = 'Fehler beim Laden der Daten'
} finally {
isLoading.value = false
}
}
const refreshData = () => {
loadData()
}
const formatHeader = (header) => {
const headerMap = {
'Datum': 'Datum',
'Mannschaft': 'Mannschaft',
'Gegner': 'Gegner',
'Ort': 'Ort',
'Uhrzeit': 'Uhrzeit',
'Ergebnis': 'Ergebnis',
'Bemerkung': 'Bemerkung',
'Status': 'Status'
}
return headerMap[header] || header
}
const formatDate = (dateString) => {
if (!dateString) return '-'
try {
// Versuche verschiedene Datumsformate zu parsen
const date = new Date(dateString)
if (isNaN(date.getTime())) {
return dateString // Falls nicht parsbar, Original zurückgeben
}
return date.toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
})
} catch {
return dateString
}
}
const formatTime = (timeString) => {
if (!timeString) return '-'
// Einfache Zeitformatierung (HH:MM)
if (timeString.match(/^\d{1,2}:\d{2}$/)) {
return timeString
}
return timeString
}
onMounted(() => {
loadData()
})
</script>