/* /assets/js/bridge/library-api.js (FINAL & KORRIGIERT FÜR FLEXIBLE API-BASES) */ (function(B){          // 🛑 WICHTIG: Globalen Cache-Speicher initialisieren (wird von blocks-api.js gelesen)     B.ApiItemCache = B.ApiItemCache || {};     if (!B || typeof grapesjs === 'undefined') return;     const PluginName = 'bridge-library-api'; // NEU: Standard-API-Basis für Abwärtskompatibilität, falls nichts konfiguriert const API_BASE_FALLBACK = (B.API_BASE || '/api/editor');     // Konstanten     const TARGET_CAT_ID = 'custom'; const PLACEHOLDER_ID = 'api-placeholder-loading'; const REFERENCE_COMPONENT_TYPE = 'library-reference';     if (B.LOG_CONFIG && B.LOG_CONFIG.PLUGINS) {         B.LOG_CONFIG.PLUGINS[PluginName] = true;      }     const log = (type, message, color = '#6A5ACD', logType = 'info', force = false) => {         if (typeof B.log === 'function') {             B.log(PluginName, `[${type}] ${message}`, color, logType, force);         } else {             if (logType === 'error') {                  console.error(`%c[${PluginName} - ${type}] %c${message}`, 'color:red; font-weight:bold;', 'color:inherit;');             }         }     };     const logApiData = (data) => B.logData(PluginName, data);     log('INIT', 'API-Schicht initialisiert.');          // --- HILFSFUNKTIONEN --- /** * Gibt die korrekte API-Basis-URL für einen Ressourcentyp (kind) zurück. * Nutzt die zentrale Map B.RESOURCE_API_BASES, die in category-config.js gefüllt wurde. */ const getApiBase = (resource) => { // Fallback auf die konfigurierte Standard-Basis, falls die Map noch nicht existiert oder der Eintrag fehlt. return (B.RESOURCE_API_BASES && B.RESOURCE_API_BASES[resource]) || API_BASE_FALLBACK; };     const buildApiUrl = (resource, action='list', params = {}) => { // KORREKTUR: Nutzt jetzt die dynamisch ermittelte API-Basis const apiBase = getApiBase(resource);         const url = new URL(apiBase, window.location.origin);                  url.searchParams.set('resource', resource);         url.searchParams.set('action', action);                  Object.entries(params).forEach(([key, value]) => {             if (value !== null && value !== undefined) url.searchParams.set(key, value);         });         return url.toString();     };     const shouldLoad = (resource) => {         const mode = (B.EDITOR_MODE || 'TEMPLATES').toUpperCase();          // HINWEIS: Hier muss für neue Ressourcen (wie 'products') ggf. der mode angepasst werden, // falls sie nicht in TEMPLATES geladen werden sollen.         switch (mode) {             case 'TEMPLATES':                 const templateResources = ['templates', 'sections', 'blocks', 'snippets', 'products']; // Beispiel: products hinzugefügt                 return templateResources.includes(resource);             case 'SECTIONS':                 const sectionResources = ['blocks', 'snippets'];                 return sectionResources.includes(resource);             case 'BLOCKS':                 return resource === 'snippets';                          default:                 log('MODE WARN', `Unbekannter Editor Modus '${mode}' festgestellt.`, 'orange', 'warn');                 return resource === 'snippets';         }     };     const fetchData = (resource, action='list', params = {}) => { // ... (Rest der fetchData-Funktion bleibt unverändert, nutzt aber die korrigierte buildApiUrl)         const url = buildApiUrl(resource, action, params);          const cacheKey = action === 'get' ? `${resource}-${params.id}` : null;         // Cache-Check verwendet B.ApiItemCache         if (cacheKey && B.ApiItemCache.hasOwnProperty(cacheKey)) {             log('CACHE HIT', `Cache Hit für /${resource}-${cacheKey}.`, '#708090', 'info');             return Promise.resolve(B.ApiItemCache[cacheKey]);         } return fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json'             },          })             .then(response => {                 if (!response.ok) {                     log('API ERROR', `API-Aufruf fehlgeschlagen für /${resource}/${action}: ${response.status} (${response.statusText})`, 'red', 'error');                     // 💡 KORREKTUR: Bei HTTP-Fehler immer ein leeres Array für LIST und leeres Objekt für GET zurückgeben.                     return action === 'get' ? {} : { items: [] };                  }                 return response.json();             })             .then(data => {                 if (data.ok === false) {                     log('API ERROR', `API-Fehler für /${resource}: ${data.error || 'Unbekannt'}`, 'red', 'error');                     // 💡 KORREKTUR: Bei API-Fehler ('ok: false') immer leeres Array/Objekt zurückgeben.                     return action === 'get' ? {} : { items: [] };                 }                  const result = data.items || data.data || data.item; let finalResult = result ? (Array.isArray(result) ? result : (action === 'list' ? (result.items || []) : result)) : (action === 'list' ? [] : {}); const resultIsArray = Array.isArray(finalResult); const resultLength = resultIsArray ? finalResult.length : (Object.keys(finalResult).length > 0 ? 1 : 0);                 log('EXTRACT SUCCESS', `Extrahiert ${resultLength} Elemente (Typ: ${action}) für /${resource}.`);                  // Bei GET-Antworten stellt der Server häufig html/content separat bereit. if (action === 'get' && finalResult && !resultIsArray) { if (data && typeof data.html !== 'undefined' && !('html' in finalResult)) { finalResult.html = data.html; } if (data && typeof data.content !== 'undefined' && !('content' in finalResult)) { finalResult.content = data.content; } } // Cache-Speicherung verwendet B.ApiItemCache if (cacheKey && resultLength > 0) { B.ApiItemCache[cacheKey] = finalResult; }                                  // 💡 KORREKTUR: Bei LIST (action='list') geben wir immer ein Array zurück, sonst das Objekt                 return finalResult;             })             .catch(error => {                 log('FETCH ERROR', `FEHLER beim Fetchen oder Parsen von /${resource}: ${error.message}`, 'red', 'error', true);                 return action === 'get' ? {} : [];             });     }; // --- Exportierte Core-Funktionen (jetzt generisch) --- // NEU: Generische Fetch-Funktion für jeden Ressourcentyp ('kind') B.fetchResource = (kind) => {         if (!shouldLoad(kind)) {             log('BLOCKED', `Blockiert: ${kind} (Modus: ${B.EDITOR_MODE})`, '#708090', 'info');             return Promise.resolve([]);         }         return fetchData(kind).then(items => Array.isArray(items) ? items : []); }; // Die alten hardcodierten Funktionen verwenden jetzt die neue generische Funktion B.fetchTemplates = () => B.fetchResource('templates');     B.fetchSnippets = () => B.fetchResource('snippets');     B.fetchSections = () => B.fetchResource('sections');     B.fetchBlocks = () => B.fetchResource('blocks');     B.getApiItem = (kind, id) => fetchData(kind, 'get', { id: id });      B.clearApiCache = () => {         B.ApiItemCache = {}; // Cache leeren         log('CACHE CLEAR', `API-Cache geleert.`, 'orange', 'warn');     };     // 🚀 Zentrale Funktion zum Laden und Registrieren der Blöcke B.loadAndRegisterApiBlocks = (editor) => { const bm = editor.BlockManager; // NEU: Ressourcen-Kinds aus der Konfiguration sammeln const resourceKindsToLoad = Object.keys(B.RESOURCE_API_BASES || {}); if (resourceKindsToLoad.length === 0) { log('FEHLER', 'Keine Ressourcen-Kind-Konfiguration (B.RESOURCE_API_BASES) gefunden.', '#dc3545', 'error', true); bm.remove(PLACEHOLDER_ID); return; } // Map aller Fetch-Promises erstellen const fetchPromises = resourceKindsToLoad.map(kind => B.fetchResource(kind).then(items => items.map(i => ({ ...i, kind: kind }))) ); log('API START', `Starte Promise.all für API-Abruf der Blöcke/Sektionen (${resourceKindsToLoad.join(', ')})...`, '#1E90FF'); Promise.all(fetchPromises) .then(results => { const apiItems = results.flat().filter(item => item && item.id);   log(`API SUCCESS`, `${apiItems.length} Elemente gefunden.`, '#9400D3'); logApiData(apiItems);    if (apiItems.length === 0) { log('NO DATA', 'Keine API-Daten gefunden.', 'orange', 'warn', true); } else { apiItems.forEach(item => { const blockId = `lib-${item.kind}-${item.id}`; const label = item.name || item.label || 'Unbenannter Block'; const itemKindUpper = item.kind.toUpperCase();   // Hier wird der Block-Manager-Block registriert // ... (Der Rest der Logik bleibt unverändert) ... const blockDefinition = { label: label, category: TARGET_CAT_ID,                            // 💡 KORREKTUR: Immer die library-reference-Komponente verwenden, um die Referenz-Logik                            // (mit editable: false) aus blocks-api.js zu erzwingen. content: { type: REFERENCE_COMPONENT_TYPE, 'lib-kind': item.kind, 'lib-id': item.id, attributes: { 'data-lib-kind': item.kind, 'data-lib-id': item.id, 'data-lib-ref': '1', },                                  // NEU: startContent wird nur als reines HTML übergeben.                                  // Die Logik in blocks-api.js (init/reloadComponentContent) kümmert sich um die Anzeige. startContent: item.html || item.content || '
🛑 Fehler: Inhalt fehlte beim Laden.
',                                  content: '', // Wichtig: Beim Drop keinen GrapesJS-Content setzen }, attributes: { 'title': itemKindUpper }, media: item.preview_url ? `` : '', }; bm.add(blockId, blockDefinition); });   bm.remove(PLACEHOLDER_ID); log(`REGISTRATION`, `${apiItems.length} API-Blöcke registriert. Platzhalter entfernt.`, '#008000'); const reloadExistingComponents = () => { const allComponents = editor.DomComponents.getWrapper().find(`[data-gjs-type="${REFERENCE_COMPONENT_TYPE}"]`); allComponents.forEach(component => { if (component.get('lib-id') && component.components().length === 0 && typeof component.reloadComponentContent === 'function') { log(`RELOAD START`, `Lade ${component.get('lib-kind')}/${component.get('lib-id')} nach Cache-Füllung (Sicherheitsnetz).`, '#FF4500'); component.reloadComponentContent({ forced: true, reason: 'EXISTING_CONTENT_RELOAD' });  } }); }; setTimeout(reloadExistingComponents, 100); } }) .catch(error => { // Hier wird der Fehler von fetchData oder map abgefangen log('FETCH ERROR', `FEHLER beim Laden der API-Blöcke: ${error.message}`, '#dc3545', 'error', true); bm.remove(PLACEHOLDER_ID); }); }; })(window.BridgeParts || (window.BridgeParts = {}));