Refactor CMS navigation and remove outdated pages

This commit updates the Navigation component to replace links for "Über uns", "Geschichte", "TT-Regeln", "Satzung", and "Termine" with a consolidated "Inhalte" and "Sportbetrieb" section. Additionally, it removes the corresponding pages for "Geschichte", "Mannschaften", "Satzung", "Termine", and "Spielpläne" to streamline the CMS structure and improve content management efficiency.
This commit is contained in:
Torsten Schulz (local)
2026-02-09 09:37:11 +01:00
parent 33ef5cda5f
commit 80c2b0bfeb
17 changed files with 1484 additions and 3108 deletions

View File

@@ -0,0 +1,278 @@
<template>
<div>
<!-- 1. PDF-Upload -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
<h2 class="text-xl font-semibold mb-4">
PDF-Upload
</h2>
<form
enctype="multipart/form-data"
class="space-y-4"
@submit.prevent="uploadPdf"
>
<div>
<label
for="pdf-file"
class="block text-sm font-medium text-gray-700 mb-2"
>
Neue Satzung hochladen (PDF)
</label>
<input
id="pdf-file"
ref="fileInput"
type="file"
accept=".pdf"
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-primary-50 file:text-primary-700 hover:file:bg-primary-100"
@change="handleFileSelect"
>
<p class="mt-1 text-sm text-gray-500">
Nur PDF-Dateien bis 10MB sind erlaubt
</p>
</div>
<button
type="submit"
:disabled="!selectedFile || uploading"
class="inline-flex items-center px-4 py-2 rounded-lg bg-primary-600 text-white hover:bg-primary-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
<svg
v-if="uploading"
class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
{{ uploading ? 'Wird hochgeladen...' : 'PDF hochladen' }}
</button>
</form>
</div>
<!-- 2. Aktuelle PDF-Information -->
<div
v-if="currentPdfUrl"
class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6"
>
<h2 class="text-xl font-semibold mb-4">
Aktuelle Satzung (PDF)
</h2>
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div>
<p class="text-gray-600">
PDF-Datei verfügbar
</p>
<a
:href="currentPdfUrl"
target="_blank"
class="text-primary-600 hover:text-primary-700 font-medium"
>
Satzung anzeigen
</a>
</div>
<div class="text-sm text-gray-500">
Zuletzt aktualisiert: {{ lastUpdated }}
</div>
</div>
</div>
<!-- 3. Textfassung (WYSIWYG) -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-semibold">
Textfassung für die Website
</h2>
<button
type="button"
class="inline-flex items-center px-4 py-2 rounded-lg bg-primary-600 text-white hover:bg-primary-700 disabled:opacity-50 disabled:cursor-not-allowed text-sm"
:disabled="savingText"
@click="saveText"
>
<svg
v-if="savingText"
class="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
{{ savingText ? 'Speichert...' : 'Text speichern' }}
</button>
</div>
<p class="text-sm text-gray-500 mb-4">
Diese HTML-Fassung wird auf der Seite Verein Satzung" angezeigt. Die PDF-Version bleibt die rechtlich verbindliche Fassung.
</p>
<RichTextEditor
v-model="satzungContent"
label="Satzung (HTML-Version)"
/>
</div>
<div
v-if="message"
class="mt-4 p-4 rounded-lg"
:class="messageType === 'success' ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'"
>
{{ message }}
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import RichTextEditor from '~/components/RichTextEditor.vue'
const fileInput = ref(null)
const selectedFile = ref(null)
const uploading = ref(false)
const currentPdfUrl = ref('')
const lastUpdated = ref('')
const message = ref('')
const messageType = ref('')
const satzungContent = ref('')
const savingText = ref(false)
async function loadCurrentSatzung() {
try {
const data = await $fetch('/api/config')
const satzung = data?.seiten?.satzung
if (satzung?.pdfUrl) {
currentPdfUrl.value = satzung.pdfUrl
lastUpdated.value = new Date().toLocaleDateString('de-DE')
}
if (satzung?.content) {
const content = typeof satzung.content === 'string' ? satzung.content : String(satzung.content || '')
satzungContent.value = content
} else {
satzungContent.value = ''
}
} catch (e) {
console.error('Fehler beim Laden der aktuellen Satzung:', e)
satzungContent.value = ''
}
}
function handleFileSelect(event) {
const file = event.target.files[0]
if (file) {
if (file.type !== 'application/pdf') {
message.value = 'Bitte wählen Sie eine PDF-Datei aus'
messageType.value = 'error'
return
}
if (file.size > 10 * 1024 * 1024) {
message.value = 'Die Datei ist zu groß (max. 10MB)'
messageType.value = 'error'
return
}
selectedFile.value = file
message.value = ''
}
}
async function uploadPdf() {
if (!selectedFile.value) return
uploading.value = true
message.value = ''
try {
const formData = new FormData()
formData.append('pdf', selectedFile.value)
const result = await $fetch('/api/cms/satzung-upload', {
method: 'POST',
body: formData
})
message.value = result.message
messageType.value = 'success'
await loadCurrentSatzung()
selectedFile.value = null
if (fileInput.value) fileInput.value.value = ''
} catch (error) {
message.value = error.data?.message || 'Fehler beim Hochladen der PDF-Datei'
messageType.value = 'error'
} finally {
uploading.value = false
}
}
async function saveText() {
savingText.value = true
message.value = ''
try {
const current = await $fetch('/api/config')
const currentSeiten = current.seiten || {}
const currentSatzung = currentSeiten.satzung || {}
const updated = {
...current,
seiten: {
...currentSeiten,
satzung: {
...currentSatzung,
content: satzungContent.value || ''
}
}
}
await $fetch('/api/config', {
method: 'PUT',
body: updated
})
message.value = 'Satzungstext erfolgreich gespeichert'
messageType.value = 'success'
try {
window.showSuccessModal && window.showSuccessModal('Erfolg', 'Satzungstext erfolgreich gespeichert.')
} catch {
// Modal optional
}
} catch (error) {
const errMsg = error?.data?.message || 'Fehler beim Speichern des Satzungstextes'
message.value = errMsg
messageType.value = 'error'
try {
window.showErrorModal && window.showErrorModal('Fehler', errMsg)
} catch {
// optional
}
} finally {
savingText.value = false
}
}
onMounted(loadCurrentSatzung)
</script>