Backup pre-corrección namespaces: mejoras schemas y componentes

Cambios incluidos:
- Actualización de copy/textos en 7 schemas JSON
- Mejoras en AdminAjaxHandler con mapeos adicionales
- Refactorización de FormBuilders y Renderers
- Correcciones en dashboard admin JS
- Nuevo ContactFormRenderer funcional

NOTA: Este commit sirve como respaldo antes de corregir
inconsistencias de case en namespaces (API→Api, WordPress→Wordpress)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
FrankZamora
2025-11-26 17:59:01 -06:00
parent 0846a3bf03
commit 4c807e1cf2
26 changed files with 717 additions and 79 deletions

View File

@@ -65,7 +65,7 @@ final class ContactFormFormBuilder
$html .= ' Seccion de contacto antes del footer con envio a webhook';
$html .= ' </p>';
$html .= ' </div>';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="contact_form">';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="contact-form">';
$html .= ' <i class="bi bi-arrow-counterclockwise me-1"></i>';
$html .= ' Restaurar valores por defecto';
$html .= ' </button>';

View File

@@ -63,7 +63,7 @@ final class CtaBoxSidebarFormBuilder
$html .= ' Caja de llamada a la accion en el sidebar';
$html .= ' </p>';
$html .= ' </div>';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="cta_box_sidebar">';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="cta-box-sidebar">';
$html .= ' <i class="bi bi-arrow-counterclockwise me-1"></i>';
$html .= ' Restaurar valores por defecto';
$html .= ' </button>';

View File

@@ -57,7 +57,7 @@ final class CtaPostFormBuilder
$html .= ' CTA promocional debajo del contenido del post';
$html .= ' </p>';
$html .= ' </div>';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="cta_post">';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="cta-post">';
$html .= ' <i class="bi bi-arrow-counterclockwise me-1"></i>';
$html .= ' Restaurar valores por defecto';
$html .= ' </button>';
@@ -252,8 +252,8 @@ final class CtaPostFormBuilder
$buttonBg = $this->renderer->getFieldValue($componentId, 'colors', 'button_bg_color', '#ffffff');
$html .= $this->buildColorPicker('ctaPostButtonBg', 'Fondo', $buttonBg);
$buttonText = $this->renderer->getFieldValue($componentId, 'colors', 'button_text_color', '#212529');
$html .= $this->buildColorPicker('ctaPostButtonText', 'Texto', $buttonText);
$buttonTextColor = $this->renderer->getFieldValue($componentId, 'colors', 'button_text_color', '#212529');
$html .= $this->buildColorPicker('ctaPostButtonTextColor', 'Texto', $buttonTextColor);
$html .= ' </div>';

View File

@@ -45,7 +45,7 @@ final class FeaturedImageFormBuilder
$html .= ' Personaliza la imagen destacada de los posts';
$html .= ' </p>';
$html .= ' </div>';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="featured_image">';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="featured-image">';
$html .= ' <i class="bi bi-arrow-counterclockwise me-1"></i>';
$html .= ' Restaurar valores por defecto';
$html .= ' </button>';

View File

@@ -192,14 +192,14 @@ final class FooterFormBuilder
$description = $this->renderer->getFieldValue($componentId, 'newsletter', 'newsletter_description', 'Recibe las ultimas actualizaciones.');
$html .= $this->buildTextarea('footerNewsletterDescription', 'Descripcion', 'bi-text-paragraph', $description);
$placeholder = $this->renderer->getFieldValue($componentId, 'newsletter', 'newsletter_placeholder', 'Email');
$placeholder = $this->renderer->getFieldValue($componentId, 'newsletter', 'newsletter_email_placeholder', 'Email');
$html .= $this->buildTextInput('footerNewsletterPlaceholder', 'Placeholder email', 'bi-input-cursor', $placeholder);
$buttonText = $this->renderer->getFieldValue($componentId, 'newsletter', 'newsletter_button_text', 'Suscribirse');
$html .= $this->buildTextInput('footerNewsletterButtonText', 'Texto boton', 'bi-cursor', $buttonText);
$webhookUrl = $this->renderer->getFieldValue($componentId, 'newsletter', 'newsletter_webhook_url', '');
$html .= $this->buildPasswordInput('footerNewsletterWebhookUrl', 'URL del Webhook', 'bi-link-45deg', $webhookUrl);
$html .= $this->buildTextarea('footerNewsletterWebhookUrl', 'URL del Webhook', 'bi-link-45deg', $webhookUrl);
$successMsg = $this->renderer->getFieldValue($componentId, 'newsletter', 'newsletter_success_message', 'Gracias por suscribirte!');
$html .= $this->buildTextInput('footerNewsletterSuccessMessage', 'Mensaje exito', 'bi-check-circle', $successMsg);

View File

@@ -591,6 +591,109 @@ final class AdminAjaxHandler
'contactFormButtonPadding' => ['group' => 'visual_effects', 'attribute' => 'button_padding'],
'contactFormTransitionDuration' => ['group' => 'visual_effects', 'attribute' => 'transition_duration'],
'contactFormTextareaRows' => ['group' => 'visual_effects', 'attribute' => 'textarea_rows'],
// =====================================================
// CTA BOX SIDEBAR
// =====================================================
// Visibility
'ctaEnabled' => ['group' => 'visibility', 'attribute' => 'is_enabled'],
'ctaShowOnDesktop' => ['group' => 'visibility', 'attribute' => 'show_on_desktop'],
'ctaShowOnMobile' => ['group' => 'visibility', 'attribute' => 'show_on_mobile'],
'ctaShowOnPages' => ['group' => 'visibility', 'attribute' => 'show_on_pages'],
// Content
'ctaTitle' => ['group' => 'content', 'attribute' => 'title'],
'ctaDescription' => ['group' => 'content', 'attribute' => 'description'],
'ctaButtonText' => ['group' => 'content', 'attribute' => 'button_text'],
'ctaButtonIcon' => ['group' => 'content', 'attribute' => 'button_icon'],
'ctaButtonAction' => ['group' => 'content', 'attribute' => 'button_action'],
'ctaButtonLink' => ['group' => 'content', 'attribute' => 'button_link'],
// Behavior
'ctaTextAlign' => ['group' => 'behavior', 'attribute' => 'text_align'],
// Typography
'ctaTitleFontSize' => ['group' => 'typography', 'attribute' => 'title_font_size'],
'ctaTitleFontWeight' => ['group' => 'typography', 'attribute' => 'title_font_weight'],
'ctaDescFontSize' => ['group' => 'typography', 'attribute' => 'description_font_size'],
'ctaButtonFontSize' => ['group' => 'typography', 'attribute' => 'button_font_size'],
'ctaButtonFontWeight' => ['group' => 'typography', 'attribute' => 'button_font_weight'],
// Colors
'ctaBackgroundColor' => ['group' => 'colors', 'attribute' => 'background_color'],
'ctaTitleColor' => ['group' => 'colors', 'attribute' => 'title_color'],
'ctaDescriptionColor' => ['group' => 'colors', 'attribute' => 'description_color'],
'ctaButtonBgColor' => ['group' => 'colors', 'attribute' => 'button_background_color'],
'ctaButtonTextColor' => ['group' => 'colors', 'attribute' => 'button_text_color'],
'ctaButtonHoverBg' => ['group' => 'colors', 'attribute' => 'button_hover_background'],
'ctaButtonHoverText' => ['group' => 'colors', 'attribute' => 'button_hover_text_color'],
// Spacing
'ctaContainerPadding' => ['group' => 'spacing', 'attribute' => 'container_padding'],
'ctaTitleMarginBottom' => ['group' => 'spacing', 'attribute' => 'title_margin_bottom'],
'ctaDescMarginBottom' => ['group' => 'spacing', 'attribute' => 'description_margin_bottom'],
'ctaButtonPadding' => ['group' => 'spacing', 'attribute' => 'button_padding'],
'ctaIconMarginRight' => ['group' => 'spacing', 'attribute' => 'icon_margin_right'],
// Visual Effects
'ctaBorderRadius' => ['group' => 'visual_effects', 'attribute' => 'border_radius'],
'ctaButtonBorderRadius' => ['group' => 'visual_effects', 'attribute' => 'button_border_radius'],
'ctaBoxShadow' => ['group' => 'visual_effects', 'attribute' => 'box_shadow'],
'ctaTransitionDuration' => ['group' => 'visual_effects', 'attribute' => 'transition_duration'],
// =====================================================
// FOOTER
// =====================================================
// Visibility
'footerEnabled' => ['group' => 'visibility', 'attribute' => 'is_enabled'],
'footerShowOnDesktop' => ['group' => 'visibility', 'attribute' => 'show_on_desktop'],
'footerShowOnMobile' => ['group' => 'visibility', 'attribute' => 'show_on_mobile'],
// Widget 1
'footerWidget1Visible' => ['group' => 'widget_1', 'attribute' => 'widget_1_visible'],
'footerWidget1Title' => ['group' => 'widget_1', 'attribute' => 'widget_1_title'],
// Widget 2
'footerWidget2Visible' => ['group' => 'widget_2', 'attribute' => 'widget_2_visible'],
'footerWidget2Title' => ['group' => 'widget_2', 'attribute' => 'widget_2_title'],
// Widget 3
'footerWidget3Visible' => ['group' => 'widget_3', 'attribute' => 'widget_3_visible'],
'footerWidget3Title' => ['group' => 'widget_3', 'attribute' => 'widget_3_title'],
// Newsletter
'footerNewsletterVisible' => ['group' => 'newsletter', 'attribute' => 'newsletter_visible'],
'footerNewsletterTitle' => ['group' => 'newsletter', 'attribute' => 'newsletter_title'],
'footerNewsletterDescription' => ['group' => 'newsletter', 'attribute' => 'newsletter_description'],
'footerNewsletterPlaceholder' => ['group' => 'newsletter', 'attribute' => 'newsletter_email_placeholder'],
'footerNewsletterButtonText' => ['group' => 'newsletter', 'attribute' => 'newsletter_button_text'],
'footerNewsletterWebhookUrl' => ['group' => 'newsletter', 'attribute' => 'newsletter_webhook_url'],
'footerNewsletterSuccessMessage' => ['group' => 'newsletter', 'attribute' => 'newsletter_success_message'],
'footerNewsletterErrorMessage' => ['group' => 'newsletter', 'attribute' => 'newsletter_error_message'],
// Footer Bottom
'footerCopyrightText' => ['group' => 'footer_bottom', 'attribute' => 'copyright_text'],
// Colors
'footerBgColor' => ['group' => 'colors', 'attribute' => 'bg_color'],
'footerTextColor' => ['group' => 'colors', 'attribute' => 'text_color'],
'footerTitleColor' => ['group' => 'colors', 'attribute' => 'title_color'],
'footerLinkColor' => ['group' => 'colors', 'attribute' => 'link_color'],
'footerLinkHoverColor' => ['group' => 'colors', 'attribute' => 'link_hover_color'],
'footerButtonBgColor' => ['group' => 'colors', 'attribute' => 'button_bg_color'],
'footerButtonTextColor' => ['group' => 'colors', 'attribute' => 'button_text_color'],
'footerButtonHoverBg' => ['group' => 'colors', 'attribute' => 'button_hover_bg'],
// Spacing
'footerPaddingY' => ['group' => 'spacing', 'attribute' => 'padding_y'],
'footerMarginTop' => ['group' => 'spacing', 'attribute' => 'margin_top'],
// Visual Effects
'footerInputBorderRadius' => ['group' => 'visual_effects', 'attribute' => 'input_border_radius'],
'footerButtonBorderRadius' => ['group' => 'visual_effects', 'attribute' => 'button_border_radius'],
'footerTransitionDuration' => ['group' => 'visual_effects', 'attribute' => 'transition_duration'],
];
}
}

View File

@@ -17,19 +17,62 @@
});
/**
* Inicializa el sistema de tabs
* Inicializa el sistema de tabs con persistencia en URL
*/
function initializeTabs() {
const tabs = document.querySelectorAll('.nav-tab');
const tabButtons = document.querySelectorAll('[data-bs-toggle="tab"]');
tabs.forEach(function(tab) {
tab.addEventListener('click', function(e) {
// Prevenir comportamiento por defecto si es necesario
// (En este caso dejamos que funcione la navegación normal)
// Leer parametro admin-tab de la URL al cargar
const urlParams = new URLSearchParams(window.location.search);
const activeTabParam = urlParams.get('admin-tab');
if (activeTabParam) {
// Buscar el boton del tab correspondiente
const targetButton = document.querySelector('[data-bs-target="#' + activeTabParam + 'Tab"]');
if (targetButton) {
// Activar el tab usando Bootstrap API
const tab = new bootstrap.Tab(targetButton);
tab.show();
}
}
// Escuchar cambios de tab para actualizar URL
tabButtons.forEach(function(tabButton) {
tabButton.addEventListener('shown.bs.tab', function(e) {
// Obtener el ID del componente desde data-bs-target
const target = e.target.getAttribute('data-bs-target');
const componentId = target.replace('#', '').replace('Tab', '');
// Actualizar URL sin recargar pagina
updateUrlWithTab(componentId);
});
});
}
/**
* Actualiza la URL con el parametro admin-tab sin recargar la pagina
*
* @param {string} tabId ID del tab activo
*/
function updateUrlWithTab(tabId) {
const url = new URL(window.location.href);
url.searchParams.set('admin-tab', tabId);
window.history.replaceState({}, '', url.toString());
}
/**
* Obtiene el ID del tab activo actualmente
*
* @returns {string|null} ID del componente activo o null
*/
function getActiveTabId() {
const activeTab = document.querySelector('.tab-pane.active');
if (activeTab) {
return activeTab.id.replace('Tab', '');
}
return null;
}
/**
* Inicializa validación de formularios
*/
@@ -227,7 +270,17 @@
.then(data => {
if (data.success) {
showNotice('success', data.data.message || 'Valores restaurados correctamente.');
setTimeout(() => location.reload(), 1500);
// Recargar preservando el tab activo
setTimeout(() => {
const activeTabId = getActiveTabId();
if (activeTabId) {
const url = new URL(window.location.href);
url.searchParams.set('admin-tab', activeTabId);
window.location.href = url.toString();
} else {
location.reload();
}
}, 1500);
} else {
showNotice('error', data.data.message || 'Error al restaurar los valores.');
}

View File

@@ -13,7 +13,19 @@ if (!defined('ABSPATH')) {
}
$components = $this->getComponents();
$firstComponentId = array_key_first($components);
// Determinar tab activo: desde URL o primer componente
$activeComponentId = array_key_first($components);
// Leer parametro admin-tab de la URL con sanitizacion
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Solo lectura de parametro para UI
if (isset($_GET['admin-tab'])) {
$requestedTab = sanitize_text_field(wp_unslash($_GET['admin-tab']));
// Validar que el componente exista
if (array_key_exists($requestedTab, $components)) {
$activeComponentId = $requestedTab;
}
}
?>
<div class="wrap roi-admin-panel">
@@ -21,13 +33,13 @@ $firstComponentId = array_key_first($components);
<ul class="nav nav-tabs nav-tabs-admin mb-0" role="tablist">
<?php foreach ($components as $componentId => $component): ?>
<li class="nav-item" role="presentation">
<button class="nav-link <?php echo $componentId === $firstComponentId ? 'active' : ''; ?>"
<button class="nav-link <?php echo $componentId === $activeComponentId ? 'active' : ''; ?>"
data-bs-toggle="tab"
data-bs-target="#<?php echo esc_attr($componentId); ?>Tab"
type="button"
role="tab"
aria-controls="<?php echo esc_attr($componentId); ?>Tab"
aria-selected="<?php echo $componentId === $firstComponentId ? 'true' : 'false'; ?>">
aria-selected="<?php echo $componentId === $activeComponentId ? 'true' : 'false'; ?>">
<i class="bi <?php echo esc_attr($component['icon']); ?> me-1"></i>
<?php echo esc_html($component['label']); ?>
</button>
@@ -38,11 +50,11 @@ $firstComponentId = array_key_first($components);
<!-- Tab Content -->
<div class="tab-content mt-3">
<?php foreach ($components as $componentId => $component):
$isFirst = ($componentId === $firstComponentId);
$isActive = ($componentId === $activeComponentId);
$componentSettings = $this->getComponentSettings($componentId);
?>
<!-- Tab: <?php echo esc_html($component['label']); ?> -->
<div class="tab-pane fade <?php echo $isFirst ? 'show active' : ''; ?>"
<div class="tab-pane fade <?php echo $isActive ? 'show active' : ''; ?>"
id="<?php echo esc_attr($componentId); ?>Tab"
role="tabpanel">

View File

@@ -58,7 +58,7 @@ final class RelatedPostFormBuilder
$html .= ' Seccion de posts relacionados con grid de cards';
$html .= ' </p>';
$html .= ' </div>';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="related_post">';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="related-post">';
$html .= ' <i class="bi bi-arrow-counterclockwise me-1"></i>';
$html .= ' Restaurar valores por defecto';
$html .= ' </button>';

View File

@@ -63,7 +63,7 @@ final class SocialShareFormBuilder
$html .= ' Botones para compartir contenido en redes sociales';
$html .= ' </p>';
$html .= ' </div>';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="social_share">';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="social-share">';
$html .= ' <i class="bi bi-arrow-counterclockwise me-1"></i>';
$html .= ' Restaurar valores por defecto';
$html .= ' </button>';

View File

@@ -63,7 +63,7 @@ final class TableOfContentsFormBuilder
$html .= ' Navegacion automatica con ScrollSpy';
$html .= ' </p>';
$html .= ' </div>';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="table_of_contents">';
$html .= ' <button type="button" class="btn btn-sm btn-outline-light btn-reset-defaults" data-component="table-of-contents">';
$html .= ' <i class="bi bi-arrow-counterclockwise me-1"></i>';
$html .= ' Restaurar valores por defecto';
$html .= ' </button>';