diff --git a/public/assets/js/ui-editor.js b/public/assets/js/ui-editor.js index 0027dc4..443b4c2 100644 --- a/public/assets/js/ui-editor.js +++ b/public/assets/js/ui-editor.js @@ -26,10 +26,14 @@ export function initEditor() { const sendInfo = document.getElementById('send_template_info'); const btnCancelSend= document.getElementById('btn-cancel-send'); const btnSendNow = document.getElementById('btn-send-now'); - const sendSender = document.getElementById('send_sender'); - const sendSenderHint = document.getElementById('send_sender_hint'); - const prevFrame = document.getElementById('previewFrame'); - const btnPrevClose = document.getElementById('btn-close-preview'); + const sendSender = document.getElementById('send_sender'); + const sendSenderHint = document.getElementById('send_sender_hint'); + const prevFrame = document.getElementById('previewFrame'); + const btnPrevClose = document.getElementById('btn-close-preview'); + const unsavedDialog = document.getElementById('unsavedDialog'); + const btnUnsavedCancel = document.getElementById('btn-unsaved-cancel'); + const btnUnsavedDiscard = document.getElementById('btn-unsaved-discard'); + const btnUnsavedSave = document.getElementById('btn-unsaved-save'); let current = null; // { resource, id, name, section } let bridgeListener = null; @@ -39,6 +43,7 @@ export function initEditor() { let currentEditorType = 'grapesjs'; let versionItems = []; let savedSnapshot = ''; + let lastVersionSelection = ''; const ok = (m) => toast(m, true); const err = (m) => toast(m, false); @@ -153,16 +158,41 @@ export function initEditor() { return currentSnapshot !== savedSnapshot; } + function showUnsavedDialog() { + return new Promise((resolve) => { + if (!unsavedDialog || typeof unsavedDialog.showModal !== 'function') { + resolve('discard'); + return; + } + const cleanup = () => { + btnUnsavedCancel && btnUnsavedCancel.removeEventListener('click', onCancel); + btnUnsavedDiscard && btnUnsavedDiscard.removeEventListener('click', onDiscard); + btnUnsavedSave && btnUnsavedSave.removeEventListener('click', onSave); + }; + const closeWith = (choice) => { + cleanup(); + unsavedDialog.close(); + resolve(choice); + }; + const onCancel = () => closeWith('cancel'); + const onDiscard = () => closeWith('discard'); + const onSave = () => closeWith('save'); + btnUnsavedCancel && btnUnsavedCancel.addEventListener('click', onCancel); + btnUnsavedDiscard && btnUnsavedDiscard.addEventListener('click', onDiscard); + btnUnsavedSave && btnUnsavedSave.addEventListener('click', onSave); + unsavedDialog.showModal(); + }); + } + async function confirmUnsavedChanges() { const dirty = await hasUnsavedChanges(); - if (!dirty) return true; - const shouldSave = window.confirm('Ungespeicherte Änderungen gefunden. Jetzt speichern?'); - if (shouldSave) { + if (!dirty) return 'ok'; + const choice = await showUnsavedDialog(); + if (choice === 'save') { const okSave = await save(); - if (!okSave) return false; - return true; + if (!okSave) return 'cancel'; } - return true; + return choice; } function renderVersionOptions(items) { @@ -187,6 +217,11 @@ export function initEditor() { opt.textContent = label; versionSelect.appendChild(opt); }); + if (lastVersionSelection) { + versionSelect.value = lastVersionSelection; + } else { + versionSelect.value = ''; + } } async function loadVersionsForCurrent() { @@ -209,6 +244,7 @@ export function initEditor() { setEditorType(targetType); if (targetType === 'craftjs') { craftEditor?.setContent(data.html || '', data.craftJson || ''); + setSavedSnapshotFromData(payload); return; } const editor = await waitForEditor(3000); @@ -217,17 +253,18 @@ export function initEditor() { try { const project = JSON.parse(jsonRaw); editor.loadProjectData(project); + setSavedSnapshotFromData(payload); return; } catch {} } const html = normalizeSnapshotValue(data.html); editor.setComponents(html); + setSavedSnapshotFromData(payload); } async function loadLatestContentFromServer() { const res = await apiAction('content.get', { method: 'GET', data: { id: current.id, section_id: current.section.id } }); await applyVersionPayload(res || {}); - setSavedSnapshotFromData(res || {}); } function writeHtmlToFrame(html) { @@ -451,11 +488,11 @@ export function initEditor() { async function open(item, resource, sectionOverride) { const section = item?.section || sectionOverride || window.__activeSection || null; current = { - resource: 'content', - id: Number(item?.id || 0), - name: item?.name || '', + resource: 'content', + id: Number(item?.id || 0), + name: item?.name || '', section: section, - }; + }; if (!current.id) return err('Ungültige ID'); if (!current.section) return err('Section nicht gefunden'); @@ -465,6 +502,8 @@ export function initEditor() { setSendContext(current.section?.is_template ? current.id : 0, current.name); if (btnTest) btnTest.classList.toggle('hidden', !current.section?.is_template); setVersionUiVisible(true); + if (versionSelect) versionSelect.value = ''; + lastVersionSelection = ''; // Neuen Token erzeugen & alten Listener entfernen reqToken++; @@ -699,8 +738,8 @@ export function initEditor() { function closePreview(){ prevDlg?.close?.(); } async function close() { - const proceed = await confirmUnsavedChanges(); - if (!proceed) return; + const decision = await confirmUnsavedChanges(); + if (decision === 'cancel') return; // nächstes Öffnen invalidiert laufende asyncs reqToken++; @@ -780,22 +819,26 @@ export function initEditor() { editorSelect && (editorSelect.onchange = () => switchEditor(editorSelect.value)); versionSelect && (versionSelect.onchange = async () => { if (!current?.id) return; - const proceed = await confirmUnsavedChanges(); - if (!proceed) { - versionSelect.value = ''; + const previousSelection = lastVersionSelection; + const decision = await confirmUnsavedChanges(); + if (decision === 'cancel') { + versionSelect.value = previousSelection; return; } const versionId = Number(versionSelect.value || 0); if (!versionId) { await loadLatestContentFromServer(); + lastVersionSelection = ''; return; } try { const res = await apiAction('content_versions.get', { method: 'GET', data: { id: versionId, content_id: current.id } }); if (!res?.ok) throw new Error(res?.error || 'Version konnte nicht geladen werden'); await applyVersionPayload(res?.item || res); + lastVersionSelection = String(versionId); } catch (e) { err(e.message || 'Version konnte nicht geladen werden'); + versionSelect.value = previousSelection; } }); btnRestoreVersion && (btnRestoreVersion.onclick = async () => { diff --git a/public/index.php b/public/index.php index 90db251..b3ca15b 100644 --- a/public/index.php +++ b/public/index.php @@ -114,6 +114,18 @@ require __DIR__ . '/../partials/structure/layout_start.php'; + + + Ungespeicherte Änderungen + Es gibt ungespeicherte Änderungen. Möchtest du diese speichern? + + Abbrechen + Nicht speichern + Speichern + + + +
Es gibt ungespeicherte Änderungen. Möchtest du diese speichern?