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.
381 lines
13 KiB
Vue
381 lines
13 KiB
Vue
<template>
|
|
<div>
|
|
<!-- Header with save button -->
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h2 class="text-xl sm:text-2xl font-display font-bold text-gray-900">
|
|
Geschichte bearbeiten
|
|
</h2>
|
|
<button
|
|
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"
|
|
@click="save"
|
|
>
|
|
Speichern
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Toolbar -->
|
|
<div class="sticky top-0 z-10 bg-white border border-gray-200 rounded-t-lg shadow-sm">
|
|
<div class="flex flex-wrap items-center gap-1 sm:gap-2 py-1.5 sm:py-2 px-3">
|
|
<!-- Formatierung -->
|
|
<div class="flex items-center gap-1 border-r pr-2 mr-2">
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded border hover:bg-gray-50 text-xs sm:text-sm"
|
|
@click="format('bold')"
|
|
>
|
|
<strong>B</strong>
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded border hover:bg-gray-50 text-xs sm:text-sm"
|
|
@click="format('italic')"
|
|
>
|
|
<em>I</em>
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded border hover:bg-gray-50 text-xs sm:text-sm"
|
|
@click="formatHeader(1)"
|
|
>
|
|
H1
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded border hover:bg-gray-50 text-xs sm:text-sm"
|
|
@click="formatHeader(2)"
|
|
>
|
|
H2
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded border hover:bg-gray-50 text-xs sm:text-sm"
|
|
@click="formatHeader(3)"
|
|
>
|
|
H3
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Listen -->
|
|
<div class="flex items-center gap-1 border-r pr-2 mr-2">
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded border hover:bg-gray-50 text-xs sm:text-sm"
|
|
@click="format('insertUnorderedList')"
|
|
>
|
|
•
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded border hover:bg-gray-50 text-xs sm:text-sm"
|
|
@click="format('insertOrderedList')"
|
|
>
|
|
1.
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Schnellzugriff für Geschichts-Abschnitte -->
|
|
<div class="flex items-center gap-1 border-r pr-2 mr-2">
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded bg-gray-100 hover:bg-gray-200 text-gray-700 text-xs sm:text-sm"
|
|
@click="insertHistoryTemplate('generic')"
|
|
>
|
|
Neuer Abschnitt
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded bg-blue-100 hover:bg-blue-200 text-blue-700 text-xs sm:text-sm"
|
|
@click="insertHistoryTemplate('founding')"
|
|
>
|
|
Gründung
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded bg-green-100 hover:bg-green-200 text-green-700 text-xs sm:text-sm"
|
|
@click="insertHistoryTemplate('milestone')"
|
|
>
|
|
Meilenstein
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded bg-yellow-100 hover:bg-yellow-200 text-yellow-700 text-xs sm:text-sm"
|
|
@click="insertHistoryTemplate('achievement')"
|
|
>
|
|
Erfolg
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded bg-red-100 hover:bg-red-200 text-red-700 text-xs sm:text-sm"
|
|
@click="deleteCurrentSection()"
|
|
>
|
|
Abschnitt löschen
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Weitere Tools -->
|
|
<div class="flex items-center gap-1">
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded border hover:bg-gray-50 text-xs sm:text-sm"
|
|
@click="createLink()"
|
|
>
|
|
Link
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 sm:px-3 sm:py-1 rounded border hover:bg-gray-50 text-xs sm:text-sm"
|
|
@click="removeFormat()"
|
|
>
|
|
Clear
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Editor -->
|
|
<div class="bg-white rounded-b-lg shadow-sm border border-t-0 border-gray-200 p-3 sm:p-4">
|
|
<div
|
|
ref="editor"
|
|
class="min-h-[320px] p-3 sm:p-4 outline-none prose max-w-none text-sm sm:text-base"
|
|
contenteditable
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
const editor = ref(null)
|
|
|
|
async function load() {
|
|
const data = await $fetch('/api/config')
|
|
const html = data?.seiten?.geschichte || ''
|
|
if (editor.value) editor.value.innerHTML = html
|
|
}
|
|
|
|
async function save() {
|
|
const html = editor.value?.innerHTML || ''
|
|
const current = await $fetch('/api/config')
|
|
const updated = { ...current, seiten: { ...(current.seiten || {}), geschichte: html } }
|
|
try {
|
|
await $fetch('/api/config', { method: 'PUT', body: updated })
|
|
try { window.showSuccessModal && window.showSuccessModal('Erfolg', 'Inhalt erfolgreich gespeichert!') } catch {
|
|
// Modal nicht verfügbar
|
|
}
|
|
} catch (error) {
|
|
try { window.showErrorModal && window.showErrorModal('Fehler', error?.data?.message || 'Speichern fehlgeschlagen') } catch {
|
|
// Modal nicht verfügbar
|
|
}
|
|
}
|
|
}
|
|
|
|
function format(cmd) {
|
|
document.execCommand(cmd, false, null)
|
|
}
|
|
|
|
function formatHeader(level) {
|
|
document.execCommand('formatBlock', false, 'H' + level)
|
|
}
|
|
|
|
function createLink() {
|
|
const url = prompt('URL eingeben:')
|
|
if (!url) return
|
|
document.execCommand('createLink', false, url)
|
|
}
|
|
|
|
function removeFormat() {
|
|
document.execCommand('removeFormat', false, null)
|
|
}
|
|
|
|
function insertHistoryTemplate(type) {
|
|
const editorElement = editor.value
|
|
if (!editorElement) return
|
|
|
|
let template = ''
|
|
|
|
switch (type) {
|
|
case 'generic':
|
|
template = `
|
|
<div class="bg-gray-50/30 p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
|
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">Neuer Geschichts-Abschnitt</h3>
|
|
<p class="text-gray-600 mb-3"><strong>Zeitraum:</strong> [Jahr oder Zeitraum]<br><strong>Ereignis:</strong> [Was ist passiert?]<br><strong>Bedeutung:</strong> [Warum war das wichtig?]</p>
|
|
<p class="text-gray-600"><strong>Details:</strong> [Weitere Informationen hier eingeben...]</p>
|
|
</div>
|
|
`
|
|
break
|
|
case 'founding':
|
|
template = `
|
|
<div class="bg-blue-50/30 p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
|
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">Gründung des Vereins</h3>
|
|
<p class="text-gray-600 mb-3"><strong>Datum:</strong> [Gründungsdatum]<br><strong>Gründer:</strong> [Wer hat den Verein gegründet?]<br><strong>Zweck:</strong> [Was war das Ziel?]</p>
|
|
<p class="text-gray-600"><strong>Gründungsgeschichte:</strong> [Wie kam es zur Gründung? Was waren die Umstände?]</p>
|
|
</div>
|
|
`
|
|
break
|
|
case 'milestone':
|
|
template = `
|
|
<div class="bg-green-50/30 p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
|
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">Wichtiger Meilenstein</h3>
|
|
<p class="text-gray-600 mb-3"><strong>Jahr:</strong> [Wann ist das passiert?]<br><strong>Ereignis:</strong> [Was war der Meilenstein?]<br><strong>Auswirkung:</strong> [Wie hat das den Verein verändert?]</p>
|
|
<p class="text-gray-600"><strong>Hintergrund:</strong> [Was führte zu diesem Ereignis? Wie wurde es erreicht?]</p>
|
|
</div>
|
|
`
|
|
break
|
|
case 'achievement':
|
|
template = `
|
|
<div class="bg-yellow-50/30 p-6 rounded-xl shadow-lg border-l-4 border-primary-600">
|
|
<h3 class="text-xl font-display font-bold text-gray-900 mb-3">Großer Erfolg</h3>
|
|
<p class="text-gray-600 mb-3"><strong>Jahr:</strong> [Wann war der Erfolg?]<br><strong>Erfolg:</strong> [Was wurde erreicht?]<br><strong>Beteiligte:</strong> [Wer war daran beteiligt?]</p>
|
|
<p class="text-gray-600"><strong>Details:</strong> [Wie wurde der Erfolg erreicht? Was war besonders bemerkenswert?]</p>
|
|
</div>
|
|
`
|
|
break
|
|
}
|
|
|
|
editorElement.focus()
|
|
|
|
const selection = window.getSelection()
|
|
if (selection.rangeCount > 0) {
|
|
const range = selection.getRangeAt(0)
|
|
|
|
if (editorElement.contains(range.commonAncestorContainer) || editorElement === range.commonAncestorContainer) {
|
|
let currentElement = range.commonAncestorContainer
|
|
|
|
if (currentElement.nodeType === Node.TEXT_NODE) {
|
|
currentElement = currentElement.parentElement
|
|
}
|
|
|
|
let sectionElement = currentElement
|
|
while (sectionElement && sectionElement !== editorElement) {
|
|
if (sectionElement.classList &&
|
|
sectionElement.classList.contains('border-l-4') &&
|
|
sectionElement.classList.contains('border-primary-600')) {
|
|
break
|
|
}
|
|
sectionElement = sectionElement.parentElement
|
|
}
|
|
|
|
if (sectionElement && sectionElement !== editorElement &&
|
|
sectionElement.classList.contains('border-l-4') &&
|
|
sectionElement.classList.contains('border-primary-600')) {
|
|
|
|
const tempDiv = document.createElement('div')
|
|
tempDiv.innerHTML = template
|
|
|
|
let newSection = null
|
|
for (let i = 0; i < tempDiv.childNodes.length; i++) {
|
|
if (tempDiv.childNodes[i].nodeType === Node.ELEMENT_NODE) {
|
|
newSection = tempDiv.childNodes[i]
|
|
break
|
|
}
|
|
}
|
|
|
|
if (newSection) {
|
|
if (sectionElement.nextSibling) {
|
|
sectionElement.parentElement.insertBefore(newSection, sectionElement.nextSibling)
|
|
} else {
|
|
sectionElement.parentElement.appendChild(newSection)
|
|
}
|
|
|
|
const newRange = document.createRange()
|
|
const titleElement = newSection.querySelector('h3')
|
|
if (titleElement) {
|
|
newRange.setStart(titleElement, 0)
|
|
newRange.collapse(true)
|
|
selection.removeAllRanges()
|
|
selection.addRange(newRange)
|
|
}
|
|
}
|
|
} else {
|
|
let nextSection = null
|
|
const walker = document.createTreeWalker(
|
|
editorElement,
|
|
NodeFilter.SHOW_ELEMENT,
|
|
{
|
|
acceptNode: function(node) {
|
|
if (node.classList &&
|
|
node.classList.contains('border-l-4') &&
|
|
node.classList.contains('border-primary-600')) {
|
|
return NodeFilter.FILTER_ACCEPT
|
|
}
|
|
return NodeFilter.FILTER_SKIP
|
|
}
|
|
}
|
|
)
|
|
|
|
let node = walker.nextNode()
|
|
while (node) {
|
|
if (currentElement.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
nextSection = node
|
|
break
|
|
}
|
|
node = walker.nextNode()
|
|
}
|
|
|
|
const tempDiv = document.createElement('div')
|
|
tempDiv.innerHTML = template
|
|
|
|
let newSection = null
|
|
for (let i = 0; i < tempDiv.childNodes.length; i++) {
|
|
if (tempDiv.childNodes[i].nodeType === Node.ELEMENT_NODE) {
|
|
newSection = tempDiv.childNodes[i]
|
|
break
|
|
}
|
|
}
|
|
|
|
if (newSection) {
|
|
if (nextSection) {
|
|
nextSection.parentElement.insertBefore(newSection, nextSection)
|
|
} else {
|
|
editorElement.appendChild(newSection)
|
|
}
|
|
|
|
const newRange = document.createRange()
|
|
const titleElement = newSection.querySelector('h3')
|
|
if (titleElement) {
|
|
newRange.setStart(titleElement, 0)
|
|
newRange.collapse(true)
|
|
selection.removeAllRanges()
|
|
selection.addRange(newRange)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
editorElement.innerHTML += template
|
|
}
|
|
} else {
|
|
editorElement.innerHTML += template
|
|
}
|
|
}
|
|
|
|
function deleteCurrentSection() {
|
|
const editorElement = editor.value
|
|
if (!editorElement) return
|
|
|
|
editorElement.focus()
|
|
|
|
const selection = window.getSelection()
|
|
if (selection.rangeCount > 0) {
|
|
const range = selection.getRangeAt(0)
|
|
|
|
if (editorElement.contains(range.commonAncestorContainer) || editorElement === range.commonAncestorContainer) {
|
|
let currentElement = range.commonAncestorContainer
|
|
|
|
if (currentElement.nodeType === Node.TEXT_NODE) {
|
|
currentElement = currentElement.parentElement
|
|
}
|
|
|
|
let sectionElement = currentElement
|
|
while (sectionElement && !(sectionElement.classList.contains('border-l-4') && sectionElement.classList.contains('border-primary-600'))) {
|
|
sectionElement = sectionElement.parentElement
|
|
}
|
|
|
|
if (sectionElement && sectionElement.classList.contains('border-l-4') && sectionElement.classList.contains('border-primary-600')) {
|
|
sectionElement.remove()
|
|
|
|
const nextElement = editorElement.querySelector('.border-l-4.border-primary-600')
|
|
if (nextElement) {
|
|
const titleElement = nextElement.querySelector('h3')
|
|
if (titleElement) {
|
|
const newRange = document.createRange()
|
|
newRange.setStart(titleElement, 0)
|
|
newRange.collapse(true)
|
|
selection.removeAllRanges()
|
|
selection.addRange(newRange)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
onMounted(load)
|
|
</script>
|