Update timestamps in nitro.json and latest.json; modify client manifest for asset management; enhance history and rules editing features in CMS with new templates and improved toolbar layout.

This commit is contained in:
Torsten Schulz (local)
2025-10-23 16:46:08 +02:00
parent 6763b15487
commit a4269e970b
11 changed files with 1167 additions and 643 deletions

View File

@@ -13,33 +13,71 @@
</div>
<!-- Fixed Toolbar below header -->
<div class="fixed top-28 sm:top-32 left-0 right-0 z-30 bg-white border-b border-gray-200 shadow-sm">
<div class="fixed left-0 right-0 z-30 bg-white border-b border-gray-200 shadow-sm" style="top: 9.5rem;">
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex flex-wrap items-center gap-1 sm:gap-2 py-1.5 sm:py-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>
<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>
<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>
<!-- 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>
</div>
<!-- Content with top padding -->
<div class="pt-36 sm:pt-44 pb-16">
<div class="pb-16" style="padding-top: 12rem;">
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
<!-- Hilfe-Sektion -->
<div class="mb-6 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>
<p><strong>4. Grid-Layout:</strong> Kästchen werden automatisch im Grid-Layout angeordnet</p>
<p class="text-xs text-blue-600 mt-2">💡 <strong>Tipp:</strong> Neue Kästchen werden automatisch in das bestehende Grid eingefügt!</p>
</div>
</div>
<div class="bg-white rounded-xl 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 class="bg-white rounded-xl 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>
</div>
</div>
@@ -88,5 +126,172 @@ function removeFormat() {
document.execCommand('removeFormat', false, null)
}
function insertRuleTemplate(type) {
const editorElement = editor.value
if (!editorElement) return
let template = ''
let bgColor = 'bg-gray-50'
switch (type) {
case 'generic':
template = `
<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>
`
bgColor = 'bg-gray-50'
break
case 'basic':
template = `
<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 hier eingeben]<br><strong>Anwendung:</strong> [Wann gilt diese Regel?]</p>
</div>
`
bgColor = 'bg-blue-50'
break
case 'penalty':
template = `
<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>
`
bgColor = 'bg-green-50'
break
case 'service':
template = `
<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>
`
bgColor = 'bg-yellow-50'
break
}
// Editor fokussieren
editorElement.focus()
const selection = window.getSelection()
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0)
// Prüfen ob der Cursor im Editor ist
if (editorElement.contains(range.commonAncestorContainer) || editorElement === range.commonAncestorContainer) {
// Aktuelles Element finden
let currentElement = range.commonAncestorContainer
// Falls es ein Text-Node ist, zum Parent-Element gehen
if (currentElement.nodeType === Node.TEXT_NODE) {
currentElement = currentElement.parentElement
}
// Zum spezifischen Container navigieren mit den Klassen "grid md:grid-cols-2 lg:grid-cols-3 gap-6"
let targetContainer = currentElement
while (targetContainer && !targetContainer.classList.contains('grid')) {
targetContainer = targetContainer.parentElement
}
// Prüfen ob es der richtige Container ist
if (targetContainer &&
targetContainer.classList.contains('md:grid-cols-2') &&
targetContainer.classList.contains('lg:grid-cols-3') &&
targetContainer.classList.contains('gap-6')) {
// Wir sind im richtigen Container - neues Kästchen hinzufügen
const tempDiv = document.createElement('div')
tempDiv.innerHTML = template
// Neues Kästchen in den Container einfügen
// Suche nach dem ersten Element-Node (nicht Text-Node)
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)
// Cursor in das neue Kästchen setzen
const newRange = document.createRange()
const titleElement = newCard.querySelector('h3')
if (titleElement) {
newRange.setStart(titleElement, 0)
newRange.collapse(true)
selection.removeAllRanges()
selection.addRange(newRange)
}
} else {
console.error('No valid element found in template');
}
} else {
// Spezifischer Container nicht gefunden - am Ende einfügen
editorElement.innerHTML += template
}
} else {
// Cursor ist nicht im Editor - Template am Ende einfügen
editorElement.innerHTML += template
}
} else {
// Keine Auswahl - Template am Ende einfügen
editorElement.innerHTML += template
}
}
function deleteCurrentRule() {
const editorElement = editor.value
if (!editorElement) return
// Editor fokussieren
editorElement.focus()
const selection = window.getSelection()
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0)
// Prüfen ob der Cursor im Editor ist
if (editorElement.contains(range.commonAncestorContainer) || editorElement === range.commonAncestorContainer) {
// Aktuelles Element finden
let currentElement = range.commonAncestorContainer
// Falls es ein Text-Node ist, zum Parent-Element gehen
if (currentElement.nodeType === Node.TEXT_NODE) {
currentElement = currentElement.parentElement
}
// Zum Grid-Kästchen navigieren
let cardElement = currentElement
while (cardElement && !cardElement.classList.contains('text-center')) {
cardElement = cardElement.parentElement
}
if (cardElement && cardElement.classList.contains('text-center')) {
// Grid-Kästchen gefunden - löschen
cardElement.remove()
// Cursor in das nächste Kästchen oder Grid setzen
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>