This commit is contained in:
2026-01-20 23:18:23 +01:00
parent 5a126eeaed
commit 921e3bad25

View File

@@ -38,6 +38,7 @@ export function initEditor() {
let senderLoadPromise = null; let senderLoadPromise = null;
let currentEditorType = 'grapesjs'; let currentEditorType = 'grapesjs';
let versionItems = []; let versionItems = [];
let savedSnapshot = '';
  const ok  = (m) => toast(m, true);   const ok  = (m) => toast(m, true);
  const err = (m) => toast(m, false);   const err = (m) => toast(m, false);
@@ -87,6 +88,83 @@ export function initEditor() {
setVersionUiVisible(false); setVersionUiVisible(false);
function normalizeSnapshotValue(value) {
return value === null || value === undefined ? '' : String(value);
}
function buildSnapshot({ editorType, html, json, craftJson }) {
return JSON.stringify({
editorType: normalizeSnapshotValue(editorType),
html: normalizeSnapshotValue(html),
json: normalizeSnapshotValue(json),
craftJson: normalizeSnapshotValue(craftJson),
});
}
function extractContentFields(payload = {}) {
const base = payload || {};
const item = base.item || {};
return {
html: base.html ?? base.html_content ?? item.html ?? item.html_content ?? '',
json: base.content ?? base.json_content ?? item.content ?? item.json_content ?? '',
craftJson: base.craft_json ?? item.craft_json ?? '',
editorType: String(base.editor_type ?? item.editor_type ?? currentEditorType ?? 'grapesjs').toLowerCase(),
};
}
async function buildCurrentSnapshot() {
if (currentEditorType === 'craftjs') {
return buildSnapshot({
editorType: 'craftjs',
html: craftEditor ? craftEditor.getContent() : '',
json: '',
craftJson: craftEditor && craftEditor.getCraftJson ? craftEditor.getCraftJson() : '',
});
}
try {
const editor = await waitForEditor(2000);
const win = iframe?.contentWindow;
const fontCss = win?.BridgeParts?.RTE_FONT_FACE_CSS || '';
const cssPayload = (fontCss ? fontCss + '\n' : '') + (editor.getCss() || '');
const htmlContent = (editor.getHtml() || '') + '<style>' + cssPayload + '</style>';
let jsonRaw = '';
try {
jsonRaw = JSON.stringify(editor.getProjectData());
} catch {}
return buildSnapshot({
editorType: 'grapesjs',
html: htmlContent,
json: jsonRaw,
craftJson: '',
});
} catch {
return buildSnapshot({ editorType: currentEditorType, html: '', json: '', craftJson: '' });
}
}
function setSavedSnapshotFromData(payload) {
const fields = extractContentFields(payload);
savedSnapshot = buildSnapshot(fields);
}
async function hasUnsavedChanges() {
if (!savedSnapshot) return false;
const currentSnapshot = await buildCurrentSnapshot();
return currentSnapshot !== savedSnapshot;
}
async function confirmUnsavedChanges() {
const dirty = await hasUnsavedChanges();
if (!dirty) return true;
const shouldSave = window.confirm('Ungespeicherte Änderungen gefunden. Jetzt speichern?');
if (shouldSave) {
const okSave = await save();
if (!okSave) return false;
return true;
}
return true;
}
function renderVersionOptions(items) { function renderVersionOptions(items) {
versionItems = items || []; versionItems = items || [];
if (!versionSelect) return; if (!versionSelect) return;
@@ -125,6 +203,33 @@ export function initEditor() {
} }
} }
async function applyVersionPayload(payload) {
const data = extractContentFields(payload);
const targetType = data.editorType === 'craftjs' ? 'craftjs' : 'grapesjs';
setEditorType(targetType);
if (targetType === 'craftjs') {
craftEditor?.setContent(data.html || '', data.craftJson || '');
return;
}
const editor = await waitForEditor(3000);
const jsonRaw = normalizeSnapshotValue(data.json).trim();
if (jsonRaw) {
try {
const project = JSON.parse(jsonRaw);
editor.loadProjectData(project);
return;
} catch {}
}
const html = normalizeSnapshotValue(data.html);
editor.setComponents(html);
}
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) {   function writeHtmlToFrame(html) {
    iframe.srcdoc = `<!doctype html><html>     iframe.srcdoc = `<!doctype html><html>
      <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head>       <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head>
@@ -404,6 +509,7 @@ export function initEditor() {
    ]);     ]);
editorType = editorType === 'craftjs' ? 'craftjs' : 'grapesjs'; editorType = editorType === 'craftjs' ? 'craftjs' : 'grapesjs';
setSavedSnapshotFromData({ html: fresh, content: jsonState, editor_type: editorType, craft_json: craftJson });
setEditorType(editorType); setEditorType(editorType);
if (editorType === 'craftjs') { if (editorType === 'craftjs') {
const craftHtml = extractCraftHtml(craftJson, fresh); const craftHtml = extractCraftHtml(craftJson, fresh);
@@ -512,7 +618,15 @@ export function initEditor() {
} }
const okSave = await delegateCommand('save-data'); const okSave = await delegateCommand('save-data');
if (okSave) setTimeout(loadVersionsForCurrent, 800); if (okSave) {
setTimeout(async () => {
await loadVersionsForCurrent();
try {
const res = await apiAction('content.get', { method: 'GET', data: { id: current.id, section_id: current.section.id } });
setSavedSnapshotFromData(res || {});
} catch {}
}, 800);
}
return okSave; return okSave;
} }
@@ -584,9 +698,11 @@ export function initEditor() {
  }   }
  function closePreview(){ prevDlg?.close?.(); }   function closePreview(){ prevDlg?.close?.(); }
function close() { async function close() {
    // nächstes Öffnen invalidiert laufende asyncs const proceed = await confirmUnsavedChanges();
    reqToken++; if (!proceed) return;
// nächstes Öffnen invalidiert laufende asyncs
reqToken++;
    try { iframe.contentWindow?.postMessage({source:'admin',type:'reset'}, '*'); } catch {}     try { iframe.contentWindow?.postMessage({source:'admin',type:'reset'}, '*'); } catch {}
    if (bridgeListener) window.removeEventListener('message', bridgeListener);     if (bridgeListener) window.removeEventListener('message', bridgeListener);
@@ -662,6 +778,26 @@ export function initEditor() {
btnCancelSend&& (btnCancelSend.onclick= closeSend); btnCancelSend&& (btnCancelSend.onclick= closeSend);
sendForm && (sendForm.onsubmit = doSend); sendForm && (sendForm.onsubmit = doSend);
editorSelect && (editorSelect.onchange = () => switchEditor(editorSelect.value)); editorSelect && (editorSelect.onchange = () => switchEditor(editorSelect.value));
versionSelect && (versionSelect.onchange = async () => {
if (!current?.id) return;
const proceed = await confirmUnsavedChanges();
if (!proceed) {
versionSelect.value = '';
return;
}
const versionId = Number(versionSelect.value || 0);
if (!versionId) {
await loadLatestContentFromServer();
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);
} catch (e) {
err(e.message || 'Version konnte nicht geladen werden');
}
});
btnRestoreVersion && (btnRestoreVersion.onclick = async () => { btnRestoreVersion && (btnRestoreVersion.onclick = async () => {
if (!current?.id) return; if (!current?.id) return;
const versionId = Number(versionSelect?.value || 0); const versionId = Number(versionSelect?.value || 0);