Upload new version

This commit is contained in:
2026-01-20 01:12:22 +01:00
parent abd74a9a50
commit 3d559924a9
15 changed files with 1632 additions and 517 deletions

View File

@@ -1,7 +1,7 @@
/* /assets/js/ui-editor.js (KORRIGIERT: Speichern wird an iFrame-Editor delegiert) */
// Öffnen, Befüllen, Speichern (mit Live-HTML), Preview Race-Schutz & Lade-Overlay.
import { apiUpdate, apiList, apiGet, toast, apiAction } from './api.js';
import { apiUpdate, toast, apiAction } from './api.js';
import { initCraftEditor } from './craft-editor.js';
export function initEditor() {
@@ -29,7 +29,7 @@ export function initEditor() {
  const prevFrame    = document.getElementById('previewFrame');
  const btnPrevClose = document.getElementById('btn-close-preview');
let current = null; // { resource, id, name }
let current = null; // { resource, id, name, section }
let bridgeListener = null;
let reqToken = 0; // steigender Token pro Öffnen -> ignoriert verspätete Events
let senderOptions = [];
@@ -40,12 +40,12 @@ export function initEditor() {
  const err = (m) => toast(m, false);
  // ---------- Hilfen ----------
  function activeMode() {
    const b = document.querySelector('nav [data-tab].bg-sky-50, nav [data-tab].text-sky-700, nav [data-tab].active');
    return (b?.dataset?.tab) || (current?.resource) || 'templates';
  }
function activeMode() {
const activeSection = window.__activeSection || current?.section || null;
return (activeSection?.slug) || (current?.resource) || 'emailtemplate';
}
  function setSendContext(id, name = '') {
function setSendContext(id, name = '') {
    if (sendDlg) {
      if (id) {
        sendDlg.dataset.templateId = String(id);
@@ -59,12 +59,12 @@ export function initEditor() {
        sendInfo.textContent = 'Kein Template ausgewählt.';
        sendInfo.classList.add('text-rose-600');
      } else {
        const label = name ? `${name} Template #${id}` : `Template #${id}`;
        sendInfo.textContent = label;
        sendInfo.classList.remove('text-rose-600');
      }
    }
  }
const label = name ? `${name} Template #${id}` : `Template #${id}`;
sendInfo.textContent = label;
sendInfo.classList.remove('text-rose-600');
}
}
}
  function writeHtmlToFrame(html) {
    iframe.srcdoc = `<!doctype html><html>
@@ -210,67 +210,13 @@ export function initEditor() {
  function showVeil(){ ensureVeil().style.display = 'flex'; }
  function hideVeil(){ if (veilEl) veilEl.style.display = 'none'; }
  // ... (Kontext-Filter-Ladung bleibt unverändert) ...
  async function listBlocksForTemplate(templateId){
    try {
      const direct = await apiList('blocks', { template_id: templateId });
      if (Array.isArray(direct) && direct.length) return direct;
    } catch {}
    const sections = await apiList('sections', { template_id: templateId }).catch(()=>[]);
    const out = [];
    for (const s of (sections || [])) {
      const b = await apiList('blocks', { section_id: s.id }).catch(()=>[]);
      if (b?.length) out.push(...b);
    }
    return out;
  }
  // Snippets eines Templates (direkt oder via Sections->Blocks als Fallback)
  async function listSnippetsForTemplate(templateId){
    try {
      const direct = await apiList('snippets', { template_id: templateId });
      if (Array.isArray(direct) && direct.length) return direct;
    } catch {}
    const sections = await apiList('sections', { template_id: templateId }).catch(()=>[]);
    const blocksAll = [];
    for (const s of (sections || [])) {
      const b = await apiList('blocks', { section_id: s.id }).catch(()=>[]);
      if (b?.length) blocksAll.push(...b);
    }
    const out = [];
    for (const b of blocksAll) {
      const sn = await apiList('snippets', { block_id: b.id }).catch(()=>[]);
      if (sn?.length) out.push(...sn);
    }
    return out;
  }
  // Referenz-Bibliothek (für „Custom Fix“)
  async function buildRefLibForContext(ctx){
    const kind = (ctx.resource || 'templates').replace(/s$/,''); // template|section|block
    const id   = ctx.id;
    if (kind === 'template'){
      const [sections, blocks] = await Promise.all([
        apiList('sections', { template_id: id }).catch(()=>[]),
        listBlocksForTemplate(id)
      ]);
      return { sections, blocks };
    }
    if (kind === 'section'){
      const blocks = await apiList('blocks', { section_id: id }).catch(()=>[]);
      return { sections: [], blocks };
    }
    return { sections: [], blocks: [] }; // block -> keine Sections/Blocks in Fix
  }
  // Snippets (für „Custom Flex“) kontextabhängig
  async function buildSnippetsForContext(ctx){
    const kind = (ctx.resource || 'templates').replace(/s$/,'');
    const id   = ctx.id;
    let rows = [];
    if (kind === 'template')      rows = await listSnippetsForTemplate(id);
    else if (kind === 'section')  rows = await apiList('snippets', { section_id: id }).catch(()=>[]);
    else if (kind === 'block')    rows = await apiList('snippets', { block_id: id }).catch(()=>[]);
    else                          rows = await apiList('snippets').catch(()=>[]);
    return (rows || []).map(r => ({ id: r.id, name: r.name, html: r.content || r.html || '' }));
  }
async function buildRefLibForContext() {
return { sections: [], blocks: [] };
}
async function buildSnippetsForContext() {
return [];
}
  async function loadSenderOptions(force = false) {
    if (!sendSender) return;
@@ -338,19 +284,22 @@ export function initEditor() {
  }
  // ---------- Öffnen ----------
async function open(item, resource) {
async function open(item, resource, sectionOverride) {
const section = item?.section || sectionOverride || window.__activeSection || null;
    current = {
      resource: String(resource || activeMode() || 'templates').toLowerCase(),
      resource: 'content',
      id: Number(item?.id || 0),
      name: item?.name || ''
      name: item?.name || '',
section: section,
    };
    if (!current.id) return err('Ungültige ID');
if (!current.section) return err('Section nicht gefunden');
    // globaler Kontext
    window.__currentItemId    = current.id;
    window.__currentEditorCtx = { id: current.id, mode: current.resource };
    setSendContext(current.id, current.name);
    if (btnTest) btnTest.classList.toggle('hidden', current.resource !== 'templates');
    window.__currentEditorCtx = { id: current.id, mode: current.section.slug, section: current.section };
    setSendContext(current.section?.is_template ? current.id : 0, current.name);
    if (btnTest) btnTest.classList.toggle('hidden', !current.section?.is_template);
    // Neuen Token erzeugen & alten Listener entfernen
    reqToken++;
@@ -374,7 +323,7 @@ export function initEditor() {
    await Promise.all([
      (async() => {
try {
const row = await apiGet(current.resource, current.id);
const row = await apiAction('content.get', { method: 'GET', data: { id: current.id, section_id: current.section.id } });
const rawContent = row?.content ?? row?.item?.content ?? '';
const trimmed = String(rawContent || '').trim();
const looksJson = trimmed.startsWith('{') || trimmed.startsWith('[');
@@ -403,10 +352,11 @@ export function initEditor() {
if (!looksCraftSerialized(craftJson) && craftEditor?.serializeFromHtml) {
const seed = craftEditor.serializeFromHtml(craftHtml);
try {
await apiUpdate(current.resource, current.id, {
await apiUpdate('content', current.id, {
editor_type: 'craftjs',
html: craftHtml,
craft_json: seed
craft_json: seed,
section_id: current.section.id,
});
} catch {}
}
@@ -436,7 +386,7 @@ export function initEditor() {
          ok('Gespeichert');
          try {
            if (typeof window.reloadActiveList === 'function') window.reloadActiveList();
            else if (typeof window.__reloadList === 'function') window.__reloadList(current.resource);
            else if (typeof window.__reloadList === 'function') window.__reloadList(current.section);
          } catch {}
          return;
        }
@@ -444,7 +394,7 @@ export function initEditor() {
        // neue Bridge meldet gjs:ready; ältere evtl. core-ready/bridge:ready
        if (d.type === 'gjs:ready' || d.type === 'core-ready' || d.type === 'bridge:ready' || d.type === 'bridge:booted') {
          pushInitialHtmlToEditor({
            mode: current.resource,
            mode: current.section.slug,
            html: fresh,
            snippets,
ref: {
@@ -462,7 +412,7 @@ export function initEditor() {
      // Fallback, falls kein Ready ankommt
      setTimeout(() => {
pushInitialHtmlToEditor({
mode: current.resource,
mode: current.section.slug,
html: fresh,
snippets,
ref: {
@@ -474,10 +424,10 @@ export function initEditor() {
json: jsonState
});
}, 1200);
    };
};
    // Jetzt den Editor-Core laden (erst NACH about:blank)
    iframe.src = `editor/editor-core.php?mode=${encodeURIComponent(current.resource)}&id=${current.id}&t=${Date.now()}`;
    iframe.src = `editor/editor-core.php?mode=${encodeURIComponent(current.section.slug)}&id=${current.id}&section_id=${current.section.id}&t=${Date.now()}`;
    dlg?.showModal?.();
  }
@@ -492,8 +442,8 @@ export function initEditor() {
const craftJson = craftEditor && craftEditor.getCraftJson
? craftEditor.getCraftJson()
: JSON.stringify({ html });
const payload = { html, craft_json: craftJson, editor_type: 'craftjs' };
const res = await apiUpdate(current.resource, current.id, payload);
const payload = { html, craft_json: craftJson, editor_type: 'craftjs', section_id: current.section.id };
const res = await apiUpdate('content', current.id, payload);
if (res?.ok) ok('Gespeichert');
else err(res?.error || 'Speichern fehlgeschlagen');
return res?.ok;
@@ -525,7 +475,11 @@ export function initEditor() {
  }
  
  async function openSend(ctx = null) {
    const ctxId = ctx?.id ? Number(ctx.id) : (window.__currentItemId || current?.id || 0);
if (!current?.section?.is_template) {
err('Kein Template geladen');
return;
}
const ctxId = ctx?.id ? Number(ctx.id) : (window.__currentItemId || current?.id || 0);
    if (!ctxId) {
      err('Kein Template geladen');
      return;
@@ -600,10 +554,11 @@ export function initEditor() {
const craftJson = craftEditor && craftEditor.serializeFromHtml
? craftEditor.serializeFromHtml(html)
: JSON.stringify({ html });
const res = await apiUpdate(current.resource, current.id, {
const res = await apiUpdate('content', current.id, {
editor_type: 'craftjs',
html,
craft_json: craftJson
craft_json: craftJson,
section_id: current.section.id,
});
if (!res?.ok) {
err(res?.error || 'Editorwechsel fehlgeschlagen');
@@ -618,9 +573,10 @@ export function initEditor() {
}
if (currentEditorType === 'craftjs' && target === 'grapesjs') {
const html = craftEditor ? craftEditor.getContent() : '';
const res = await apiUpdate(current.resource, current.id, {
const res = await apiUpdate('content', current.id, {
editor_type: 'grapesjs',
html
html,
section_id: current.section.id,
});
if (!res?.ok) {
err(res?.error || 'Editorwechsel fehlgeschlagen');
@@ -628,7 +584,7 @@ export function initEditor() {
return;
}
ok('Editor gewechselt');
await open({ id: current.id, name: current.name }, current.resource);
await open({ id: current.id, name: current.name, section: current.section }, 'content', current.section);
}
}
@@ -643,17 +599,21 @@ export function initEditor() {
sendForm && (sendForm.onsubmit = doSend);
editorSelect && (editorSelect.onchange = () => switchEditor(editorSelect.value));
  window.AdminTestSend = window.AdminTestSend || {};
  window.AdminTestSend.open = (opts = {}) => {
    const targetId = Number(opts.id || window.__currentItemId || 0);
    if (!targetId) {
      err('Testversand: Keine ID vorhanden');
      return;
    }
    window.__currentItemId = targetId;
    setSendContext(targetId, opts.name || '');
    openSend({ id: targetId, name: opts.name || '' });
  };
window.AdminTestSend = window.AdminTestSend || {};
window.AdminTestSend.open = (opts = {}) => {
const targetId = Number(opts.id || window.__currentItemId || 0);
if (!targetId) {
err('Testversand: Keine ID vorhanden');
return;
}
if (!current?.section?.is_template) {
err('Kein Template geladen');
return;
}
window.__currentItemId = targetId;
setSendContext(targetId, opts.name || '');
openSend({ id: targetId, name: opts.name || '' });
};
  // Public API
  window.EditorUI = { open, save, close, clear: clearEditor, preview: openPreview };