Update Hero component to dynamically display years since founding; enhance TermineVorschau component with improved date and time formatting, and add Uhrzeit column in the CMS for better event management. Refactor API to handle new fields and improve data handling in CSV exports.
This commit is contained in:
@@ -536,23 +536,43 @@ const saveConfig = async () => {
|
||||
})
|
||||
|
||||
successMessage.value = 'Konfiguration erfolgreich gespeichert!'
|
||||
try { window.showSuccessModal && window.showSuccessModal('Erfolg', 'Konfiguration erfolgreich gespeichert!') } catch (e) {}
|
||||
setTimeout(() => {
|
||||
successMessage.value = ''
|
||||
}, 3000)
|
||||
} catch (error) {
|
||||
errorMessage.value = error.data?.message || 'Fehler beim Speichern der Konfiguration.'
|
||||
try { window.showErrorModal && window.showErrorModal('Fehler', errorMessage.value) } catch (e) {}
|
||||
} finally {
|
||||
isSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const addTrainingTime = () => {
|
||||
// Finde die höchste vorhandene Gruppennummer
|
||||
let maxGruppeNummer = 0
|
||||
config.value.training.zeiten.forEach(zeit => {
|
||||
if (zeit.gruppe) {
|
||||
// Prüfe, ob das gruppe-Feld eine Nummer enthält (z.B. "Gruppe 1", "1", "Gruppe 3")
|
||||
const match = zeit.gruppe.match(/(\d+)/)
|
||||
if (match) {
|
||||
const nummer = parseInt(match[1], 10)
|
||||
if (nummer > maxGruppeNummer) {
|
||||
maxGruppeNummer = nummer
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Setze die nächste Gruppennummer
|
||||
const naechsteGruppeNummer = maxGruppeNummer + 1
|
||||
|
||||
config.value.training.zeiten.push({
|
||||
id: Date.now().toString(),
|
||||
tag: 'Montag',
|
||||
von: '19:00',
|
||||
bis: '22:00',
|
||||
gruppe: ''
|
||||
gruppe: `Gruppe ${naechsteGruppeNummer}`
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,12 @@ async function save() {
|
||||
const html = editor.value?.innerHTML || ''
|
||||
const current = await $fetch('/api/config')
|
||||
const updated = { ...current, seiten: { ...(current.seiten || {}), geschichte: html } }
|
||||
await $fetch('/api/config', { method: 'PUT', body: updated })
|
||||
try {
|
||||
await $fetch('/api/config', { method: 'PUT', body: updated })
|
||||
try { window.showSuccessModal && window.showSuccessModal('Erfolg', 'Inhalt erfolgreich gespeichert!') } catch (e) {}
|
||||
} catch (error) {
|
||||
try { window.showErrorModal && window.showErrorModal('Fehler', error?.data?.message || 'Speichern fehlgeschlagen') } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function format(cmd) {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Datum</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Uhrzeit</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Titel</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Beschreibung</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Kategorie</th>
|
||||
@@ -36,10 +37,13 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<tr v-for="termin in termine" :key="termin.id" class="hover:bg-gray-50">
|
||||
<tr v-for="termin in termine" :key="`${termin.datum}-${termin.uhrzeit || ''}-${termin.titel}`" class="hover:bg-gray-50">
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ formatDate(termin.datum) }}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ termin.uhrzeit || '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm font-medium text-gray-900">
|
||||
{{ termin.titel }}
|
||||
</td>
|
||||
@@ -60,7 +64,14 @@
|
||||
{{ termin.kategorie }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
|
||||
<td class="px-4 py-3 whitespace-nowrap text-right text-sm font-medium space-x-3">
|
||||
<button
|
||||
@click="openEditModal(termin)"
|
||||
class="text-gray-600 hover:text-gray-900"
|
||||
title="Bearbeiten"
|
||||
>
|
||||
<Pencil :size="18" />
|
||||
</button>
|
||||
<button
|
||||
@click="confirmDelete(termin)"
|
||||
class="text-red-600 hover:text-red-900"
|
||||
@@ -87,11 +98,11 @@
|
||||
>
|
||||
<div class="bg-white rounded-xl shadow-2xl max-w-2xl w-full p-8">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6">
|
||||
Termin hinzufügen
|
||||
{{ isEditing ? 'Termin bearbeiten' : 'Termin hinzufügen' }}
|
||||
</h2>
|
||||
|
||||
<form @submit.prevent="saveTermin" class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Datum *</label>
|
||||
<input
|
||||
@@ -102,6 +113,15 @@
|
||||
:disabled="isSaving"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Uhrzeit</label>
|
||||
<input
|
||||
v-model="formData.uhrzeit"
|
||||
type="time"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
:disabled="isSaving"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Kategorie *</label>
|
||||
@@ -173,7 +193,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { Calendar, Plus, Trash2, Loader2, AlertCircle } from 'lucide-vue-next'
|
||||
import { Calendar, Plus, Trash2, Loader2, AlertCircle, Pencil } from 'lucide-vue-next'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
@@ -182,6 +202,8 @@ const isSaving = ref(false)
|
||||
const termine = ref([])
|
||||
const showModal = ref(false)
|
||||
const errorMessage = ref('')
|
||||
const isEditing = ref(false)
|
||||
const originalTermin = ref(null)
|
||||
|
||||
const formData = ref({
|
||||
datum: '',
|
||||
@@ -207,15 +229,20 @@ const openAddModal = () => {
|
||||
datum: '',
|
||||
titel: '',
|
||||
beschreibung: '',
|
||||
kategorie: 'Sonstiges'
|
||||
kategorie: 'Sonstiges',
|
||||
uhrzeit: ''
|
||||
}
|
||||
showModal.value = true
|
||||
errorMessage.value = ''
|
||||
isEditing.value = false
|
||||
originalTermin.value = null
|
||||
}
|
||||
|
||||
const closeModal = () => {
|
||||
showModal.value = false
|
||||
errorMessage.value = ''
|
||||
isEditing.value = false
|
||||
originalTermin.value = null
|
||||
}
|
||||
|
||||
const saveTermin = async () => {
|
||||
@@ -223,6 +250,17 @@ const saveTermin = async () => {
|
||||
errorMessage.value = ''
|
||||
|
||||
try {
|
||||
if (isEditing.value && originalTermin.value) {
|
||||
const params = new URLSearchParams({
|
||||
datum: originalTermin.value.datum,
|
||||
uhrzeit: originalTermin.value.uhrzeit || '',
|
||||
titel: originalTermin.value.titel,
|
||||
beschreibung: originalTermin.value.beschreibung || '',
|
||||
kategorie: originalTermin.value.kategorie || 'Sonstiges'
|
||||
})
|
||||
await $fetch(`/api/termine-manage?${params.toString()}`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
await $fetch('/api/termine-manage', {
|
||||
method: 'POST',
|
||||
body: formData.value
|
||||
@@ -237,11 +275,26 @@ const saveTermin = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const openEditModal = (termin) => {
|
||||
formData.value = {
|
||||
datum: termin.datum || '',
|
||||
uhrzeit: termin.uhrzeit || '',
|
||||
titel: termin.titel || '',
|
||||
beschreibung: termin.beschreibung || '',
|
||||
kategorie: termin.kategorie || 'Sonstiges'
|
||||
}
|
||||
originalTermin.value = { ...termin }
|
||||
isEditing.value = true
|
||||
showModal.value = true
|
||||
errorMessage.value = ''
|
||||
}
|
||||
|
||||
const confirmDelete = async (termin) => {
|
||||
window.showConfirmModal('Termin löschen', `Möchten Sie den Termin "${termin.titel}" wirklich löschen?`, async () => {
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
datum: termin.datum,
|
||||
uhrzeit: termin.uhrzeit || '',
|
||||
titel: termin.titel,
|
||||
beschreibung: termin.beschreibung || '',
|
||||
kategorie: termin.kategorie || 'Sonstiges'
|
||||
|
||||
@@ -105,7 +105,12 @@ async function save() {
|
||||
const html = editor.value?.innerHTML || ''
|
||||
const current = await $fetch('/api/config')
|
||||
const updated = { ...current, seiten: { ...(current.seiten || {}), ttRegeln: html } }
|
||||
await $fetch('/api/config', { method: 'PUT', body: updated })
|
||||
try {
|
||||
await $fetch('/api/config', { method: 'PUT', body: updated })
|
||||
try { window.showSuccessModal && window.showSuccessModal('Erfolg', 'Regeln erfolgreich gespeichert!') } catch (e) {}
|
||||
} catch (error) {
|
||||
try { window.showErrorModal && window.showErrorModal('Fehler', error?.data?.message || 'Speichern fehlgeschlagen') } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function format(cmd) {
|
||||
|
||||
@@ -67,7 +67,12 @@ async function save() {
|
||||
const html = editor.value?.innerHTML || ''
|
||||
const current = await $fetch('/api/config')
|
||||
const updated = { ...current, seiten: { ...(current.seiten || {}), ueberUns: html } }
|
||||
await $fetch('/api/config', { method: 'PUT', body: updated })
|
||||
try {
|
||||
await $fetch('/api/config', { method: 'PUT', body: updated })
|
||||
try { window.showSuccessModal && window.showSuccessModal('Erfolg', 'Inhalt erfolgreich gespeichert!') } catch (e) {}
|
||||
} catch (error) {
|
||||
try { window.showErrorModal && window.showErrorModal('Fehler', error?.data?.message || 'Speichern fehlgeschlagen') } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function format(cmd) {
|
||||
|
||||
Reference in New Issue
Block a user