Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 49s
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.
184 lines
10 KiB
Vue
184 lines
10 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">
|
|
TT-Regeln 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 Regeln -->
|
|
<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="insertRuleTemplate('generic')">Neue Regel</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="insertRuleTemplate('basic')">Grundregel</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="insertRuleTemplate('penalty')">Strafregel</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="insertRuleTemplate('service')">Aufschlag</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="deleteCurrentRule()">Regel 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>
|
|
|
|
<!-- Hilfe-Sektion -->
|
|
<div class="my-4 bg-blue-50 border border-blue-200 rounded-lg p-4">
|
|
<h3 class="text-lg font-semibold text-blue-900 mb-2">So arbeiten Sie mit Regel-Kästchen:</h3>
|
|
<div class="text-sm text-blue-800 space-y-2">
|
|
<p><strong>1. Neue Kästchen hinzufügen:</strong> Klicken Sie in ein bestehendes Kästchen und verwenden Sie die Buttons:</p>
|
|
<ul class="ml-4 space-y-1">
|
|
<li>• <span class="bg-gray-100 px-2 py-1 rounded text-xs">Neue Regel</span> - Graues Kästchen</li>
|
|
<li>• <span class="bg-blue-100 px-2 py-1 rounded text-xs">Grundregel</span> - Blaues Kästchen</li>
|
|
<li>• <span class="bg-green-100 px-2 py-1 rounded text-xs">Strafregel</span> - Grünes Kästchen</li>
|
|
<li>• <span class="bg-yellow-100 px-2 py-1 rounded text-xs">Aufschlag</span> - Gelbes Kästchen</li>
|
|
</ul>
|
|
<p><strong>2. Kästchen löschen:</strong> Klicken Sie in ein Kästchen und dann auf <span class="bg-red-100 px-2 py-1 rounded text-xs">Regel löschen</span></p>
|
|
<p><strong>3. Kästchen bearbeiten:</strong> Klicken Sie direkt in die Texte und bearbeiten Sie sie</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Editor -->
|
|
<div class="bg-white rounded-lg shadow-sm border 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?.ttRegeln || ''
|
|
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 || {}), ttRegeln: html } }
|
|
try {
|
|
await $fetch('/api/config', { method: 'PUT', body: updated })
|
|
try { window.showSuccessModal && window.showSuccessModal('Erfolg', 'Regeln erfolgreich gespeichert!') } catch { /* */ }
|
|
} catch (error) {
|
|
try { window.showErrorModal && window.showErrorModal('Fehler', error?.data?.message || 'Speichern fehlgeschlagen') } catch { /* */ }
|
|
}
|
|
}
|
|
|
|
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) document.execCommand('createLink', false, url) }
|
|
function removeFormat() { document.execCommand('removeFormat', false, null) }
|
|
|
|
function insertRuleTemplate(type) {
|
|
const editorElement = editor.value
|
|
if (!editorElement) return
|
|
|
|
const templates = {
|
|
generic: '<div class="text-center p-6 bg-gray-50 rounded-lg"><h3 class="text-xl font-semibold text-gray-900 mb-2">Neue Regel</h3><p class="text-gray-600 text-sm">[Regeltext hier eingeben]</p></div>',
|
|
basic: '<div class="text-center p-6 bg-blue-50 rounded-lg"><h3 class="text-xl font-semibold text-gray-900 mb-2">Neue Grundregel</h3><p class="text-gray-600 text-sm"><strong>Regel:</strong> [Regeltext hier eingeben]<br><strong>Beschreibung:</strong> [Detaillierte Beschreibung]<br><strong>Anwendung:</strong> [Wann gilt diese Regel?]</p></div>',
|
|
penalty: '<div class="text-center p-6 bg-green-50 rounded-lg"><h3 class="text-xl font-semibold text-gray-900 mb-2">Neue Strafregel</h3><p class="text-gray-600 text-sm"><strong>Verstoß:</strong> [Was ist der Verstoß?]<br><strong>Strafe:</strong> [Welche Strafe wird verhängt?]<br><strong>Häufigkeit:</strong> [Bei wiederholten Verstößen?]</p></div>',
|
|
service: '<div class="text-center p-6 bg-yellow-50 rounded-lg"><h3 class="text-xl font-semibold text-gray-900 mb-2">Neue Aufschlagregel</h3><p class="text-gray-600 text-sm"><strong>Regel:</strong> [Aufschlagregel hier eingeben]<br><strong>Technik:</strong> [Wie muss der Aufschlag ausgeführt werden?]<br><strong>Fehler:</strong> [Was gilt als Fehler?]</p></div>'
|
|
}
|
|
|
|
const template = templates[type] || templates.generic
|
|
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 targetContainer = currentElement
|
|
while (targetContainer && !targetContainer.classList.contains('grid')) {
|
|
targetContainer = targetContainer.parentElement
|
|
}
|
|
|
|
if (targetContainer && targetContainer.classList.contains('md:grid-cols-2') && targetContainer.classList.contains('lg:grid-cols-3') && targetContainer.classList.contains('gap-6')) {
|
|
const tempDiv = document.createElement('div')
|
|
tempDiv.innerHTML = template
|
|
let newCard = null
|
|
for (let i = 0; i < tempDiv.childNodes.length; i++) {
|
|
if (tempDiv.childNodes[i].nodeType === Node.ELEMENT_NODE) { newCard = tempDiv.childNodes[i]; break }
|
|
}
|
|
if (newCard) {
|
|
targetContainer.appendChild(newCard)
|
|
const newRange = document.createRange()
|
|
const titleElement = newCard.querySelector('h3')
|
|
if (titleElement) { newRange.setStart(titleElement, 0); newRange.collapse(true); selection.removeAllRanges(); selection.addRange(newRange) }
|
|
}
|
|
} else {
|
|
editorElement.innerHTML += template
|
|
}
|
|
} else {
|
|
editorElement.innerHTML += template
|
|
}
|
|
} else {
|
|
editorElement.innerHTML += template
|
|
}
|
|
}
|
|
|
|
function deleteCurrentRule() {
|
|
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 cardElement = currentElement
|
|
while (cardElement && !cardElement.classList.contains('text-center')) { cardElement = cardElement.parentElement }
|
|
if (cardElement && cardElement.classList.contains('text-center')) {
|
|
cardElement.remove()
|
|
const gridContainer = editorElement.querySelector('.grid')
|
|
if (gridContainer && gridContainer.children.length > 0) {
|
|
const firstCard = gridContainer.firstElementChild
|
|
const titleElement = firstCard.querySelector('h3')
|
|
if (titleElement) {
|
|
const newRange = document.createRange()
|
|
newRange.setStart(titleElement, 0); newRange.collapse(true)
|
|
selection.removeAllRanges(); selection.addRange(newRange)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
onMounted(load)
|
|
</script>
|