728 lines
25 KiB
Vue
728 lines
25 KiB
Vue
<template>
|
|
<div class="min-h-full bg-gray-50">
|
|
<!-- Fixed Header below navigation -->
|
|
<div class="fixed top-20 left-0 right-0 z-40 bg-white border-b border-gray-200 shadow-sm">
|
|
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-3 sm:py-4">
|
|
<div class="flex items-center justify-between">
|
|
<h1 class="text-xl sm:text-3xl font-display font-bold text-gray-900">Vereinsmeisterschaften bearbeiten</h1>
|
|
<div class="space-x-3">
|
|
<button @click="addNewResult" class="inline-flex items-center px-3 py-1.5 sm:px-4 sm:py-2 rounded-lg bg-green-600 text-white hover:bg-green-700 text-sm sm:text-base">
|
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
</svg>
|
|
Neues Ergebnis
|
|
</button>
|
|
<button @click="save" class="inline-flex items-center px-3 py-1.5 sm:px-4 sm:py-2 rounded-lg bg-primary-600 text-white hover:bg-primary-700 text-sm sm:text-base">Speichern</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Content with top padding -->
|
|
<div class="pt-20 pb-16">
|
|
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
|
|
<!-- 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="entry in sortedGroupedResults"
|
|
:key="entry.jahr"
|
|
class="bg-white rounded-xl shadow-lg p-6"
|
|
>
|
|
<div class="flex items-center justify-between mb-6">
|
|
<h2 class="text-2xl font-display font-bold text-gray-900">{{ entry.jahr }}</h2>
|
|
<div class="flex space-x-2">
|
|
<button
|
|
@click="addResultForYear(entry.jahr)"
|
|
class="px-3 py-1 text-sm bg-green-100 hover:bg-green-200 text-green-700 rounded-lg transition-colors"
|
|
>
|
|
Ergebnis hinzufügen
|
|
</button>
|
|
<button
|
|
@click="deleteYear(entry.jahr)"
|
|
class="px-3 py-1 text-sm bg-red-100 hover:bg-red-200 text-red-700 rounded-lg transition-colors"
|
|
>
|
|
Jahr löschen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Besondere Bemerkungen -->
|
|
<div v-if="entry.data.bemerkungen" class="mb-6 p-4 bg-yellow-50 border-l-4 border-yellow-400 rounded-r-lg">
|
|
<div class="flex items-center justify-between">
|
|
<p class="text-gray-700 font-medium">{{ entry.data.bemerkungen }}</p>
|
|
<button
|
|
@click="editBemerkung(entry.jahr)"
|
|
class="px-2 py-1 text-xs bg-yellow-100 hover:bg-yellow-200 text-yellow-700 rounded transition-colors"
|
|
>
|
|
Bearbeiten
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Kategorien -->
|
|
<div v-if="Object.keys(entry.data.kategorien).length > 0" class="space-y-6">
|
|
<div
|
|
v-for="(kategorieResults, kategorie) in entry.data.kategorien"
|
|
:key="kategorie"
|
|
class="border border-gray-200 rounded-lg p-4"
|
|
>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-semibold text-gray-900">{{ kategorie }}</h3>
|
|
<div class="flex space-x-2">
|
|
<button
|
|
@click="addResultForKategorie(entry.jahr, kategorie)"
|
|
class="px-2 py-1 text-xs bg-blue-100 hover:bg-blue-200 text-blue-700 rounded transition-colors"
|
|
>
|
|
Ergebnis hinzufügen
|
|
</button>
|
|
<button
|
|
@click="deleteKategorie(entry.jahr, kategorie)"
|
|
class="px-2 py-1 text-xs bg-red-100 hover:bg-red-200 text-red-700 rounded transition-colors"
|
|
>
|
|
Kategorie löschen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-3">
|
|
<div
|
|
v-for="(result, index) in kategorieResults"
|
|
:key="index"
|
|
class="flex items-center justify-between p-3 bg-gray-50 rounded-lg"
|
|
>
|
|
<div class="flex items-center space-x-4">
|
|
<span class="w-8 h-8 bg-primary-600 text-white rounded-full flex items-center justify-center text-sm font-bold">
|
|
{{ result.platz }}
|
|
</span>
|
|
<div>
|
|
<span class="font-medium text-gray-900">{{ result.spieler1 }}</span>
|
|
<span v-if="result.spieler2" class="text-gray-600"> & {{ result.spieler2 }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex space-x-2">
|
|
<button
|
|
@click="editResult(result, entry.jahr, kategorie, index)"
|
|
class="px-2 py-1 text-xs bg-gray-100 hover:bg-gray-200 text-gray-700 rounded transition-colors"
|
|
>
|
|
Bearbeiten
|
|
</button>
|
|
<button
|
|
@click="deleteResult(entry.jahr, kategorie, index)"
|
|
class="px-2 py-1 text-xs bg-red-100 hover:bg-red-200 text-red-700 rounded transition-colors"
|
|
>
|
|
Löschen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else 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 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"></path>
|
|
</svg>
|
|
<p class="text-gray-600">Keine Ergebnisse vorhanden.</p>
|
|
<button
|
|
@click="addNewResult"
|
|
class="mt-4 px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors"
|
|
>
|
|
Erstes Ergebnis hinzufügen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal für Ergebnis bearbeiten/hinzufügen -->
|
|
<div
|
|
v-if="showModal"
|
|
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"
|
|
@click.self="closeModal"
|
|
>
|
|
<div class="bg-white rounded-lg max-w-md w-full p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">
|
|
{{ editingResult ? 'Ergebnis bearbeiten' : 'Neues Ergebnis hinzufügen' }}
|
|
</h3>
|
|
|
|
<form @submit.prevent="saveResult" class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Jahr</label>
|
|
<input
|
|
v-model="formData.jahr"
|
|
type="text"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Kategorie</label>
|
|
<select
|
|
v-model="formData.kategorie"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
|
>
|
|
<option value="">Kategorie wählen</option>
|
|
<option value="Einzel">Einzel</option>
|
|
<option value="Doppel">Doppel</option>
|
|
<option value="Mixed">Mixed</option>
|
|
<option value="Jugend">Jugend</option>
|
|
<option value="Senioren">Senioren</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Platz</label>
|
|
<select
|
|
v-model="formData.platz"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
|
>
|
|
<option value="">Platz wählen</option>
|
|
<option value="1">1. Platz</option>
|
|
<option value="2">2. Platz</option>
|
|
<option value="3">3. Platz</option>
|
|
<option value="4">4. Platz</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Spieler 1</label>
|
|
<input
|
|
v-model="formData.spieler1"
|
|
type="text"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
|
/>
|
|
</div>
|
|
|
|
<div v-if="formData.kategorie === 'Doppel' || formData.kategorie === 'Mixed'">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Spieler 2</label>
|
|
<input
|
|
v-model="formData.spieler2"
|
|
type="text"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Bemerkung (optional)</label>
|
|
<textarea
|
|
v-model="formData.bemerkung"
|
|
rows="3"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
|
></textarea>
|
|
</div>
|
|
|
|
<div class="flex justify-end space-x-3 pt-4">
|
|
<button
|
|
type="button"
|
|
@click="closeModal"
|
|
class="px-4 py-2 text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
|
|
>
|
|
Abbrechen
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors"
|
|
>
|
|
{{ editingResult ? 'Aktualisieren' : 'Hinzufügen' }}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal für Bemerkung bearbeiten -->
|
|
<div
|
|
v-if="showBemerkungModal"
|
|
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"
|
|
@click.self="closeBemerkungModal"
|
|
>
|
|
<div class="bg-white rounded-lg max-w-md w-full p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Bemerkung bearbeiten</h3>
|
|
|
|
<form @submit.prevent="saveBemerkung" class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Bemerkung</label>
|
|
<textarea
|
|
v-model="bemerkungText"
|
|
rows="3"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
|
></textarea>
|
|
</div>
|
|
|
|
<div class="flex justify-end space-x-3 pt-4">
|
|
<button
|
|
type="button"
|
|
@click="closeBemerkungModal"
|
|
class="px-4 py-2 text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
|
|
>
|
|
Abbrechen
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors"
|
|
>
|
|
Speichern
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from 'vue'
|
|
|
|
definePageMeta({
|
|
middleware: 'auth',
|
|
})
|
|
|
|
useHead({ title: 'CMS: Vereinsmeisterschaften' })
|
|
|
|
const results = ref([])
|
|
const selectedYear = ref('alle')
|
|
const showModal = ref(false)
|
|
const showBemerkungModal = ref(false)
|
|
const editingResult = ref(null)
|
|
const editingYear = ref(null)
|
|
const editingKategorie = ref(null)
|
|
const editingIndex = ref(null)
|
|
const bemerkungText = ref('')
|
|
const bemerkungYear = ref('')
|
|
|
|
const formData = ref({
|
|
jahr: '',
|
|
kategorie: '',
|
|
platz: '',
|
|
spieler1: '',
|
|
spieler2: '',
|
|
bemerkung: ''
|
|
})
|
|
|
|
const loadResults = async () => {
|
|
try {
|
|
// Verwende API-Endpoint statt statische Datei, um Cache-Probleme zu vermeiden
|
|
const timestamp = new Date().getTime()
|
|
const response = await fetch(`/api/vereinsmeisterschaften?t=${timestamp}`, {
|
|
cache: 'no-cache',
|
|
headers: {
|
|
'Cache-Control': 'no-cache'
|
|
}
|
|
})
|
|
|
|
console.log('=== FRONTEND LOAD DEBUG ===')
|
|
console.log('Load URL:', `/api/vereinsmeisterschaften?t=${timestamp}`)
|
|
console.log('Response Status:', response.status)
|
|
console.log('Response Headers - Content-Type:', response.headers.get('content-type'))
|
|
console.log('Response Headers - Cache-Control:', response.headers.get('cache-control'))
|
|
|
|
if (!response.ok) {
|
|
console.error('Fehler beim Laden - Response nicht OK:', response.status)
|
|
return
|
|
}
|
|
|
|
const csv = await response.text()
|
|
console.log('CSV Content Länge:', csv.length)
|
|
console.log('CSV Content Preview (first 300 chars):', csv.substring(0, 300))
|
|
console.log('CSV Content Preview (last 200 chars):', csv.substring(csv.length - 200))
|
|
|
|
const lines = csv.split('\n').filter(line => line.trim() !== '')
|
|
console.log('Anzahl Zeilen:', lines.length)
|
|
|
|
if (lines.length < 2) {
|
|
console.log('Zu wenige Zeilen zum Parsen')
|
|
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)
|
|
|
|
console.log('Anzahl geladener Ergebnisse:', results.value.length)
|
|
console.log('=== END FRONTEND LOAD DEBUG ===')
|
|
} 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(() => {
|
|
// Als Array zurückgeben, um die Sortierung zu garantieren
|
|
return Object.keys(groupedResults.value)
|
|
.sort((a, b) => b - a) // Neueste zuerst
|
|
.map(jahr => ({ jahr, data: groupedResults.value[jahr] }))
|
|
})
|
|
|
|
const addNewResult = () => {
|
|
editingResult.value = null
|
|
editingYear.value = null
|
|
editingKategorie.value = null
|
|
editingIndex.value = null
|
|
formData.value = {
|
|
jahr: '',
|
|
kategorie: '',
|
|
platz: '',
|
|
spieler1: '',
|
|
spieler2: '',
|
|
bemerkung: ''
|
|
}
|
|
showModal.value = true
|
|
}
|
|
|
|
const addResultForYear = (jahr) => {
|
|
editingResult.value = null
|
|
editingYear.value = jahr
|
|
editingKategorie.value = null
|
|
editingIndex.value = null
|
|
formData.value = {
|
|
jahr: jahr,
|
|
kategorie: '',
|
|
platz: '',
|
|
spieler1: '',
|
|
spieler2: '',
|
|
bemerkung: ''
|
|
}
|
|
showModal.value = true
|
|
}
|
|
|
|
const addResultForKategorie = (jahr, kategorie) => {
|
|
editingResult.value = null
|
|
editingYear.value = jahr
|
|
editingKategorie.value = kategorie
|
|
editingIndex.value = null
|
|
formData.value = {
|
|
jahr: jahr,
|
|
kategorie: kategorie,
|
|
platz: '',
|
|
spieler1: '',
|
|
spieler2: '',
|
|
bemerkung: ''
|
|
}
|
|
showModal.value = true
|
|
}
|
|
|
|
const editResult = (result, jahr, kategorie, index) => {
|
|
editingResult.value = result
|
|
editingYear.value = jahr
|
|
editingKategorie.value = kategorie
|
|
editingIndex.value = index
|
|
formData.value = {
|
|
jahr: result.jahr,
|
|
kategorie: result.kategorie,
|
|
platz: result.platz,
|
|
spieler1: result.spieler1,
|
|
spieler2: result.spieler2,
|
|
bemerkung: result.bemerkung
|
|
}
|
|
showModal.value = true
|
|
}
|
|
|
|
const editBemerkung = (jahr) => {
|
|
bemerkungYear.value = jahr
|
|
bemerkungText.value = groupedResults.value[jahr]?.bemerkungen || ''
|
|
showBemerkungModal.value = true
|
|
}
|
|
|
|
const saveResult = async () => {
|
|
if (editingResult.value) {
|
|
// Ergebnis aktualisieren
|
|
const resultIndex = results.value.findIndex(r =>
|
|
r.jahr === editingResult.value.jahr &&
|
|
r.kategorie === editingResult.value.kategorie &&
|
|
r.platz === editingResult.value.platz &&
|
|
r.spieler1 === editingResult.value.spieler1 &&
|
|
r.spieler2 === editingResult.value.spieler2
|
|
)
|
|
|
|
if (resultIndex !== -1) {
|
|
results.value[resultIndex] = { ...formData.value }
|
|
}
|
|
} else {
|
|
// Neues Ergebnis hinzufügen
|
|
results.value.push({ ...formData.value })
|
|
}
|
|
|
|
closeModal()
|
|
|
|
// Automatisch speichern
|
|
try {
|
|
await save()
|
|
window.showSuccessModal('Erfolg', 'Ergebnis wurde erfolgreich gespeichert')
|
|
} catch (error) {
|
|
window.showErrorModal('Fehler', 'Fehler beim Speichern des Ergebnisses: ' + error.message)
|
|
}
|
|
}
|
|
|
|
const saveBemerkung = async () => {
|
|
// Bemerkung als separates Ergebnis speichern
|
|
const existingBemerkungIndex = results.value.findIndex(r =>
|
|
r.jahr === bemerkungYear.value &&
|
|
r.kategorie === '' &&
|
|
r.platz === '' &&
|
|
r.spieler1 === '' &&
|
|
r.spieler2 === '' &&
|
|
r.bemerkung !== ''
|
|
)
|
|
|
|
if (existingBemerkungIndex !== -1) {
|
|
results.value[existingBemerkungIndex].bemerkung = bemerkungText.value
|
|
} else {
|
|
results.value.push({
|
|
jahr: bemerkungYear.value,
|
|
kategorie: '',
|
|
platz: '',
|
|
spieler1: '',
|
|
spieler2: '',
|
|
bemerkung: bemerkungText.value
|
|
})
|
|
}
|
|
|
|
closeBemerkungModal()
|
|
|
|
// Automatisch speichern
|
|
try {
|
|
await save()
|
|
window.showSuccessModal('Erfolg', 'Bemerkung wurde erfolgreich gespeichert')
|
|
} catch (error) {
|
|
window.showErrorModal('Fehler', 'Fehler beim Speichern der Bemerkung: ' + error.message)
|
|
}
|
|
}
|
|
|
|
const deleteResult = async (jahr, kategorie, index) => {
|
|
window.showConfirmModal('Ergebnis löschen', 'Möchten Sie dieses Ergebnis wirklich löschen?', async () => {
|
|
try {
|
|
const resultToDelete = groupedResults.value[jahr].kategorien[kategorie][index]
|
|
const resultIndex = results.value.findIndex(r =>
|
|
r.jahr === resultToDelete.jahr &&
|
|
r.kategorie === resultToDelete.kategorie &&
|
|
r.platz === resultToDelete.platz &&
|
|
r.spieler1 === resultToDelete.spieler1 &&
|
|
r.spieler2 === resultToDelete.spieler2
|
|
)
|
|
|
|
if (resultIndex !== -1) {
|
|
results.value.splice(resultIndex, 1)
|
|
// Automatisch speichern nach dem Löschen
|
|
await save()
|
|
window.showSuccessModal('Erfolg', 'Ergebnis wurde erfolgreich gelöscht')
|
|
}
|
|
} catch (error) {
|
|
window.showErrorModal('Fehler', 'Fehler beim Löschen des Ergebnisses: ' + error.message)
|
|
}
|
|
})
|
|
}
|
|
|
|
const deleteKategorie = async (jahr, kategorie) => {
|
|
window.showConfirmModal('Kategorie löschen', `Möchten Sie die Kategorie "${kategorie}" für ${jahr} wirklich löschen?`, async () => {
|
|
try {
|
|
const kategorieResults = groupedResults.value[jahr].kategorien[kategorie]
|
|
kategorieResults.forEach(result => {
|
|
const resultIndex = results.value.findIndex(r =>
|
|
r.jahr === result.jahr &&
|
|
r.kategorie === result.kategorie &&
|
|
r.platz === result.platz &&
|
|
r.spieler1 === result.spieler1 &&
|
|
r.spieler2 === result.spieler2
|
|
)
|
|
|
|
if (resultIndex !== -1) {
|
|
results.value.splice(resultIndex, 1)
|
|
}
|
|
})
|
|
// Automatisch speichern nach dem Löschen
|
|
await save()
|
|
window.showSuccessModal('Erfolg', 'Kategorie wurde erfolgreich gelöscht')
|
|
} catch (error) {
|
|
window.showErrorModal('Fehler', 'Fehler beim Löschen der Kategorie: ' + error.message)
|
|
}
|
|
})
|
|
}
|
|
|
|
const deleteYear = async (jahr) => {
|
|
window.showConfirmModal('Jahr löschen', `Möchten Sie alle Ergebnisse für ${jahr} wirklich löschen?`, async () => {
|
|
try {
|
|
results.value = results.value.filter(r => r.jahr !== jahr)
|
|
// Automatisch speichern nach dem Löschen
|
|
await save()
|
|
window.showSuccessModal('Erfolg', 'Jahr wurde erfolgreich gelöscht')
|
|
} catch (error) {
|
|
window.showErrorModal('Fehler', 'Fehler beim Löschen des Jahres: ' + error.message)
|
|
}
|
|
})
|
|
}
|
|
|
|
const closeModal = () => {
|
|
showModal.value = false
|
|
editingResult.value = null
|
|
editingYear.value = null
|
|
editingKategorie.value = null
|
|
editingIndex.value = null
|
|
}
|
|
|
|
const closeBemerkungModal = () => {
|
|
showBemerkungModal.value = false
|
|
bemerkungText.value = ''
|
|
bemerkungYear.value = ''
|
|
}
|
|
|
|
const save = async () => {
|
|
try {
|
|
// CSV generieren
|
|
const csvHeader = 'Jahr,Kategorie,Platz,Spieler1,Spieler2,Bemerkung'
|
|
const csvRows = results.value.map(result => {
|
|
return [
|
|
result.jahr,
|
|
result.kategorie,
|
|
result.platz,
|
|
result.spieler1,
|
|
result.spieler2,
|
|
result.bemerkung
|
|
].map(field => `"${field}"`).join(',')
|
|
})
|
|
|
|
const csvContent = [csvHeader, ...csvRows].join('\n')
|
|
|
|
// Frontend Debugging
|
|
console.log('=== FRONTEND SAVE DEBUG ===')
|
|
console.log('Anzahl Ergebnisse zum Speichern:', results.value.length)
|
|
console.log('CSV Content Länge:', csvContent.length)
|
|
console.log('CSV Content Preview (first 300 chars):', csvContent.substring(0, 300))
|
|
console.log('CSV Content Preview (last 200 chars):', csvContent.substring(csvContent.length - 200))
|
|
|
|
// CSV speichern
|
|
const response = await fetch('/api/cms/save-csv', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
filename: 'vereinsmeisterschaften.csv',
|
|
content: csvContent
|
|
})
|
|
})
|
|
|
|
const responseData = await response.json()
|
|
console.log('Response Status:', response.status)
|
|
console.log('Response Data:', responseData)
|
|
|
|
if (response.ok) {
|
|
console.log('=== END FRONTEND SAVE DEBUG ===')
|
|
// Erfolgreich gespeichert - keine weitere Nachricht bei automatischem Speichern
|
|
return true
|
|
} else {
|
|
console.error('Fehler beim Speichern - Response nicht OK:', response.status, responseData)
|
|
throw new Error('Fehler beim Speichern: ' + (responseData.message || response.statusText))
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Speichern:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadResults()
|
|
})
|
|
</script>
|