{{-- Sections Manager Logic for LandingSectionsEditor --}} // Cache de templates de formularios no cliente para evitar requisicoes AJAX redundantes const formTemplatesCache = {}; /** * Gerenciamento de overlay CUSTOM — sem Bootstrap Modal, sem backdrop do Bootstrap. * Usa contador para suportar futuros sub-modais (gallery picker inline). */ var lseOverlayCount = 0; /** * Exibe o overlay + painel do modal de secao. */ const showSectionModal = () => { lseOverlayCount++; document.getElementById('lse-overlay-' + name).classList.add('show'); document.getElementById('section-modal-' + name).classList.add('show'); document.body.classList.add('modal-open'); }; /** * Esconde o painel do modal e, se nenhum outro modal estiver aberto, o overlay. */ const hideSectionModal = () => { lseOverlayCount = Math.max(0, lseOverlayCount - 1); document.getElementById('section-modal-' + name).classList.remove('show'); if (lseOverlayCount === 0) { document.getElementById('lse-overlay-' + name).classList.remove('show'); document.body.classList.remove('modal-open'); document.body.style.removeProperty('padding-right'); document.body.style.removeProperty('overflow'); } }; /** * Registra os ouvintes de eventos do DOM especificos */ const registerEvents = () => { // Configuracao global AJAX para requisicoes desse editor $.ajaxSetup({ headers: { 'X-CSRF-TOKEN': csrfToken } }); // Atualizar preview visual de icone ao alterar o select $(document).on('change', '.icon-selector', function() { var iconClass = $(this).val(); $(this).closest('.input-group').find('.input-group-text i').attr('class', iconClass); }); // Adicionar Secao (Clique no dropdown) $('.btn-add-section-' + name).on('click', function(e) { e.preventDefault(); currentType = $(this).data('type'); currentSectionId = ''; loadForm(currentType, ''); }); // Editar Secao $(document).on('click', '.lse-section-item-' + name + ' .btn-edit-section', function() { const $item = $(this).closest('.lse-section-item-' + name); currentType = $item.data('type'); currentSectionId = $item.data('id'); // Busca a secao no array local const section = sections.find(s => s.id === currentSectionId); const parameters = section ? section.parameters : {}; loadForm(currentType, currentSectionId, parameters); }); // Mover Secao para Cima $(document).on('click', '.lse-section-item-' + name + ' .btn-move-up-section', function(e) { e.stopPropagation(); const $item = $(this).closest('.lse-section-item-' + name); const id = $item.data('id'); const idx = sections.findIndex(s => s.id === id); if (idx > 0) { [sections[idx - 1], sections[idx]] = [sections[idx], sections[idx - 1]]; // Recalcula ordens sections.forEach((s, i) => { s.order = i + 1; }); persistState(); } }); // Mover Secao para Baixo $(document).on('click', '.lse-section-item-' + name + ' .btn-move-down-section', function(e) { e.stopPropagation(); const $item = $(this).closest('.lse-section-item-' + name); const id = $item.data('id'); const idx = sections.findIndex(s => s.id === id); if (idx !== -1 && idx < sections.length - 1) { [sections[idx], sections[idx + 1]] = [sections[idx + 1], sections[idx]]; // Recalcula ordens sections.forEach((s, i) => { s.order = i + 1; }); persistState(); } }); // Remover Secao $(document).on('click', '.lse-section-item-' + name + ' .btn-remove-section', function() { const $item = $(this).closest('.lse-section-item-' + name); const sectionId = $item.data('id'); removeSection(sectionId); }); // Salvar Secao no Modal $('#btn-modal-save-' + name).on('click', () => { saveSection(); }); // Fechar modal (botoes Fechar / X / overlay) $('#btn-modal-close-' + name + ', #btn-modal-close2-' + name).on('click', function() { hideSectionModal(); }); $('#lse-overlay-' + name).on('click', function() { hideSectionModal(); }); // --- Gerenciamento de Anexos / Imagens --- // Abrir o seletor de imagens global $(document).on('click', '.lse-container-{{ $name }} .btn-select-image, #section-modal-' + name + ' .btn-select-image', function() { activeImageInputId = $(this).data('input-id'); window.dispatchEvent(new CustomEvent('lotep:open-gallery-picker-lse_global_media_picker', { detail: { callback: (id, url) => { selectImage(id, url); } } })); }); // Limpar imagem selecionada $(document).on('click', '.lse-container-{{ $name }} .btn-clear-image, #section-modal-' + name + ' .btn-clear-image', function() { const inputId = $(this).data('input-id'); $(`#${inputId}`).val(''); $(`#${inputId}-preview-text`).val('Nenhuma imagem selecionada'); $(`#${inputId}-thumbnail`).html(''); $(this).prop('disabled', true); }); }; /** * Inicializa o reordenamento */ const initSortable = () => { // Desativado em prol do sistema de setinhas (Up/Down) fixa nos blocos }; /** * Recalcula o array de secoes baseado na ordem do DOM */ const updateOrderFromDOM = () => { // Mantido como fallback se necessario }; /** * Carrega o formulario de parametros via AJAX com suporte a Cache no Cliente */ const loadForm = (type, sectionId, parameters = {}) => { const $modalBody = $('#modal-body-content-' + name); const $modalDialog = $('#section-modal-' + name + ' .modal-dialog'); $modalBody.html('

Carregando formulario...

'); // Ajuste de largura para tabelas interativas if (type === 'interactive-table') { $modalDialog.addClass('modal-xl'); } else { $modalDialog.removeClass('modal-xl'); } // Abre o modal com estado limpo — instancia nova, sem backdrops acumulados showSectionModal(); // Chave unica de cache const cacheKey = sectionId ? 'edit_' + sectionId : 'new_' + type; // Handler para renderizar e inicializar o form const renderFormHtml = (html) => { $modalBody.html(html); // Inicializacao de componentes do formulario apos carregamento if (typeof UIManager !== 'undefined') { UIManager.initSelect2('.select2'); UIManager.initIconPickers('.iconpicker'); } if (typeof window.initContentRichEditors === 'function') { window.initContentRichEditors($modalBody[0]); } }; if (formTemplatesCache[cacheKey]) { console.log('[LandingSectionsEditor] Form obtido do cache:', cacheKey); renderFormHtml(formTemplatesCache[cacheKey]); return; } $.get(urls.getForm, { type: type, section_id: sectionId, parameters: JSON.stringify(parameters) }, (html) => { formTemplatesCache[cacheKey] = html; // Salva no cache renderFormHtml(html); }).fail(() => { $modalBody.html('
Erro ao carregar o formulario da secao.
'); }); }; /** * Valida e salva os dados da secao na memoria local e atualiza o Iframe atomicamente */ const saveSection = () => { const $form = $('#modal-body-content-' + name); const formData = $form.find('input, textarea, select').serialize() + `&type=${currentType}§ion_id=${currentSectionId}`; // Validacao no backend via AJAX $.post(urls.validate, formData, (response) => { if (response.success) { // Extracao de parametros de forma recursiva e robusta const serialized = $form.find('input, textarea, select').serializeArray(); const parameters = {}; serialized.forEach(item => { const nameAttr = item.name; if (nameAttr.startsWith('parameters[')) { // Detecta se eh array (termina com []) const isArrayInput = /\[\]$/.test(nameAttr); // Extrai os caminhos aninhados, ex: "parameters[items][0][title]" -> ["items", "0", "title"] // Para arrays: "parameters[visible_slugs][]" -> ["visible_slugs", ""] const paths = nameAttr .replace(/^parameters\[/, '') .replace(/\]$/, '') .split(']['); // Se for array e o ultimo path eh vazio, significa que eh um array simples // Ex: parameters[visible_slugs][] -> paths = ["visible_slugs", ""] if (isArrayInput && paths.length > 1 && paths[paths.length - 1] === '') { // Remove o ultimo elemento vazio paths.pop(); // Pula o ultimo loop porque vamos tratar como push } let current = parameters; for (let i = 0; i < paths.length; i++) { const path = paths[i]; const isLast = (i === paths.length - 1); if (isLast) { if (isArrayInput) { // Se nao existe, cria array vazio if (!Array.isArray(current[path])) { current[path] = []; } current[path].push(item.value); } else { current[path] = item.value; } } else { const nextPath = paths[i + 1]; const nextIsNumeric = !isNaN(nextPath) && nextPath.trim() !== ''; if (!current[path]) { current[path] = nextIsNumeric ? [] : {}; } current = current[path]; } } } }); // Limpa o cache da instancia modificada para forcar recarga no proximo clique de edicao const savedSectionId = currentSectionId || generateUuid(); delete formTemplatesCache['edit_' + savedSectionId]; delete formTemplatesCache['new_' + currentType]; if (currentSectionId) { // Edicao de secao existente const found = sections.find(s => s.id === currentSectionId); if (found) { found.parameters = parameters; } } else { // Nova secao sections.push({ id: savedSectionId, type: currentType, parameters: parameters, order: sections.length + 1 }); } // Fecha o modal e limpa backdrops hideSectionModal(); persistState(); if (typeof toastr !== 'undefined') toastr.success('Secao atualizada localmente!'); // --- FOCUSED PARTIAL SWAP PREVIEW (postMessage) --- $.post(urls.renderSection, { type: currentType, parameters: JSON.stringify(parameters) }, (renderResponse) => { if (renderResponse.success && renderResponse.html) { const iframe = document.getElementById('preview-iframe-' + name); if (iframe && iframe.contentWindow) { iframe.contentWindow.postMessage({ type: 'lotep:update-section-html', sectionId: savedSectionId, html: renderResponse.html }, '*'); } } }); } }).fail((xhr) => { if (xhr.status === 422 && xhr.responseJSON && xhr.responseJSON.errors) { const errors = xhr.responseJSON.errors; let errorMsg = 'Por favor, corrija os seguintes erros:\n'; for (const key in errors) { errorMsg += `- ${errors[key].join(', ')}\n`; } alert(errorMsg); } else { alert('Erro na validacao dos dados da secao.'); } }); }; /** * Remove uma secao da listagem local */ const removeSection = (sectionId) => { if (!confirm('Deseja realmente remover esta secao?')) return; // Limpa o cache correspondente delete formTemplatesCache['edit_' + sectionId]; sections = sections.filter(s => s.id !== sectionId); // Recalcula ordens sections.forEach((s, idx) => { s.order = idx + 1; }); persistState(); if (typeof toastr !== 'undefined') toastr.success('Secao removida localmente!'); }; /** * Renderiza a sidebar local de secoes baseada no estado em memoria */ const renderSidebar = () => { const $list = $('#section-list-' + name); if (sections.length === 0) { $list.html('

Nenhuma secao adicionada.

'); return; } let html = ''; sections.forEach((section, index) => { const data = sectionTypesData[section.type] || { icon: 'bi bi-box', title: section.type }; const isFirst = (index === 0); const isLast = (index === sections.length - 1); html += `
${data.title}
`; }); $list.html(html); }; /** * Gera um UUID v4 no cliente */ const generateUuid = () => { return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) ); }; /** * Inicializa o modulo editor */ const init = () => { // Limpeza inicial de backdrops residuais de outros modais Bootstrap document.querySelectorAll('.modal-backdrop').forEach(function (el) { el.remove(); }); if (document.querySelectorAll('.modal.show').length === 0) { document.body.classList.remove('modal-open'); } registerEvents(); initSortable(); renderSidebar(); // Salva o estado atual no cache ANTES de carregar o preview $hiddenInput.val(JSON.stringify(sections)); $.ajax({ url: urls.savePreview, type: 'POST', data: { token: previewToken, sections: JSON.stringify(sections) }, success: () => refreshPreview(), error: () => refreshPreview() }); };