From 4c807e1cf22f9b1103af28700086f43c60787181 Mon Sep 17 00:00:00 2001 From: FrankZamora Date: Wed, 26 Nov 2025 17:59:01 -0600 Subject: [PATCH] =?UTF-8?q?Backup=20pre-correcci=C3=B3n=20namespaces:=20me?= =?UTF-8?q?joras=20schemas=20y=20componentes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../Ui/ContactFormFormBuilder.php | 2 +- .../Ui/CtaBoxSidebarFormBuilder.php | 2 +- .../Infrastructure/Ui/CtaPostFormBuilder.php | 6 +- .../Ui/FeaturedImageFormBuilder.php | 2 +- .../Infrastructure/Ui/FooterFormBuilder.php | 4 +- .../Api/Wordpress/AdminAjaxHandler.php | 103 ++++++ .../Ui/Assets/Js/admin-dashboard.js | 67 +++- Admin/Infrastructure/Ui/Views/dashboard.php | 22 +- .../Ui/RelatedPostFormBuilder.php | 2 +- .../Ui/SocialShareFormBuilder.php | 2 +- .../Ui/TableOfContentsFormBuilder.php | 2 +- .../Infrastructure/Ui/ContactFormRenderer.php | 316 ++++++++++++++++++ .../Ui/CtaBoxSidebarRenderer.php | 24 +- .../Infrastructure/Ui/CtaPostRenderer.php | 23 +- .../Api/WordPress/NewsletterAjaxHandler.php | 20 +- .../Infrastructure/Ui/FooterRenderer.php | 32 +- _temp_check.php | 16 + functions.php | 47 ++- schemas/contact-form.json | 8 +- schemas/cta-box-sidebar.json | 10 +- schemas/cta-lets-talk.json | 4 +- schemas/cta-post.json | 36 +- schemas/footer.json | 37 +- schemas/navbar.json | 2 +- schemas/top-notification-bar.json | 6 +- .../ValueObjects/ComponentConfiguration.php | 1 + 26 files changed, 717 insertions(+), 79 deletions(-) create mode 100644 _temp_check.php diff --git a/Admin/ContactForm/Infrastructure/Ui/ContactFormFormBuilder.php b/Admin/ContactForm/Infrastructure/Ui/ContactFormFormBuilder.php index f0c3cc5b..9ff18a35 100644 --- a/Admin/ContactForm/Infrastructure/Ui/ContactFormFormBuilder.php +++ b/Admin/ContactForm/Infrastructure/Ui/ContactFormFormBuilder.php @@ -65,7 +65,7 @@ final class ContactFormFormBuilder $html .= ' Seccion de contacto antes del footer con envio a webhook'; $html .= '

'; $html .= ' '; - $html .= ' '; diff --git a/Admin/CtaBoxSidebar/Infrastructure/Ui/CtaBoxSidebarFormBuilder.php b/Admin/CtaBoxSidebar/Infrastructure/Ui/CtaBoxSidebarFormBuilder.php index eea6b8cc..35f58809 100644 --- a/Admin/CtaBoxSidebar/Infrastructure/Ui/CtaBoxSidebarFormBuilder.php +++ b/Admin/CtaBoxSidebar/Infrastructure/Ui/CtaBoxSidebarFormBuilder.php @@ -63,7 +63,7 @@ final class CtaBoxSidebarFormBuilder $html .= ' Caja de llamada a la accion en el sidebar'; $html .= '

'; $html .= ' '; - $html .= ' '; diff --git a/Admin/CtaPost/Infrastructure/Ui/CtaPostFormBuilder.php b/Admin/CtaPost/Infrastructure/Ui/CtaPostFormBuilder.php index ee1e14f4..5575894a 100644 --- a/Admin/CtaPost/Infrastructure/Ui/CtaPostFormBuilder.php +++ b/Admin/CtaPost/Infrastructure/Ui/CtaPostFormBuilder.php @@ -57,7 +57,7 @@ final class CtaPostFormBuilder $html .= ' CTA promocional debajo del contenido del post'; $html .= '

'; $html .= ' '; - $html .= ' '; @@ -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 .= ' '; diff --git a/Admin/FeaturedImage/Infrastructure/Ui/FeaturedImageFormBuilder.php b/Admin/FeaturedImage/Infrastructure/Ui/FeaturedImageFormBuilder.php index fbdf247e..05660e3e 100644 --- a/Admin/FeaturedImage/Infrastructure/Ui/FeaturedImageFormBuilder.php +++ b/Admin/FeaturedImage/Infrastructure/Ui/FeaturedImageFormBuilder.php @@ -45,7 +45,7 @@ final class FeaturedImageFormBuilder $html .= ' Personaliza la imagen destacada de los posts'; $html .= '

'; $html .= ' '; - $html .= ' '; diff --git a/Admin/Footer/Infrastructure/Ui/FooterFormBuilder.php b/Admin/Footer/Infrastructure/Ui/FooterFormBuilder.php index fe9f3418..abb58b30 100644 --- a/Admin/Footer/Infrastructure/Ui/FooterFormBuilder.php +++ b/Admin/Footer/Infrastructure/Ui/FooterFormBuilder.php @@ -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); diff --git a/Admin/Infrastructure/Api/Wordpress/AdminAjaxHandler.php b/Admin/Infrastructure/Api/Wordpress/AdminAjaxHandler.php index 96003ccf..338ef376 100644 --- a/Admin/Infrastructure/Api/Wordpress/AdminAjaxHandler.php +++ b/Admin/Infrastructure/Api/Wordpress/AdminAjaxHandler.php @@ -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'], ]; } } diff --git a/Admin/Infrastructure/Ui/Assets/Js/admin-dashboard.js b/Admin/Infrastructure/Ui/Assets/Js/admin-dashboard.js index 425167ae..2cbf34f6 100644 --- a/Admin/Infrastructure/Ui/Assets/Js/admin-dashboard.js +++ b/Admin/Infrastructure/Ui/Assets/Js/admin-dashboard.js @@ -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.'); } diff --git a/Admin/Infrastructure/Ui/Views/dashboard.php b/Admin/Infrastructure/Ui/Views/dashboard.php index e8d8f08d..64608928 100644 --- a/Admin/Infrastructure/Ui/Views/dashboard.php +++ b/Admin/Infrastructure/Ui/Views/dashboard.php @@ -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; + } +} ?>
@@ -21,13 +33,13 @@ $firstComponentId = array_key_first($components);
'; - $html .= ' '; diff --git a/Public/ContactForm/Infrastructure/Ui/ContactFormRenderer.php b/Public/ContactForm/Infrastructure/Ui/ContactFormRenderer.php index dae7b4e7..879ca7e3 100644 --- a/Public/ContactForm/Infrastructure/Ui/ContactFormRenderer.php +++ b/Public/ContactForm/Infrastructure/Ui/ContactFormRenderer.php @@ -50,6 +50,21 @@ final class ContactFormRenderer implements RendererInterface return sprintf("\n%s\n", $css, $html, $js); } + /** + * Renderiza el modal de contacto para el boton Let's Talk + * Usa la misma configuracion y webhook que el formulario de seccion + */ + public function renderModal(Component $component): string + { + $data = $component->getData(); + + $css = $this->generateModalCSS($data); + $html = $this->buildModalHTML($data); + $js = $this->buildModalJS($data); + + return sprintf("\n%s\n", $css, $html, $js); + } + public function supports(string $componentType): bool { return $componentType === 'contact-form'; @@ -454,6 +469,307 @@ final class ContactFormRenderer implements RendererInterface }); }); })(); +JS; + + return $js; + } + + /** + * Generar CSS para el modal + */ + private function generateModalCSS(array $data): string + { + $colors = $data['colors'] ?? []; + $effects = $data['visual_effects'] ?? []; + + $cssRules = []; + + // Modal header con gradiente del tema + $cssRules[] = $this->cssGenerator->generate('#contactModal .modal-header', [ + 'background' => 'linear-gradient(135deg, #0E2337 0%, #1e3a5f 100%)', + 'border-bottom' => 'none', + 'padding' => '1.5rem', + ]); + + $cssRules[] = $this->cssGenerator->generate('#contactModal .modal-title', [ + 'color' => '#ffffff', + 'font-weight' => '600', + ]); + + $cssRules[] = $this->cssGenerator->generate('#contactModal .btn-close', [ + 'filter' => 'brightness(0) invert(1)', + ]); + + // Modal body + $cssRules[] = $this->cssGenerator->generate('#contactModal .modal-body', [ + 'padding' => '2rem', + ]); + + // Form inputs + $inputBorderColor = $colors['input_border_color'] ?? '#dee2e6'; + $inputFocusBorder = $colors['input_focus_border'] ?? '#FF8600'; + $inputBorderRadius = $effects['input_border_radius'] ?? '6px'; + $transitionDuration = $effects['transition_duration'] ?? '0.3s'; + + $cssRules[] = $this->cssGenerator->generate('#contactModal .form-control', [ + 'border-color' => $inputBorderColor, + 'border-radius' => $inputBorderRadius, + 'transition' => "all {$transitionDuration} ease", + ]); + + $cssRules[] = $this->cssGenerator->generate('#contactModal .form-control:focus', [ + 'border-color' => $inputFocusBorder, + 'box-shadow' => '0 0 0 0.2rem rgba(255, 134, 0, 0.25)', + 'outline' => 'none', + ]); + + // Submit button + $buttonBgColor = $colors['button_bg_color'] ?? '#FF8600'; + $buttonTextColor = $colors['button_text_color'] ?? '#ffffff'; + $buttonHoverBg = $colors['button_hover_bg'] ?? '#e67a00'; + $buttonBorderRadius = $effects['button_border_radius'] ?? '6px'; + $buttonPadding = $effects['button_padding'] ?? '0.75rem 2rem'; + + $cssRules[] = $this->cssGenerator->generate('#contactModal .btn-modal-submit', [ + 'background-color' => $buttonBgColor, + 'color' => $buttonTextColor, + 'font-weight' => '600', + 'padding' => $buttonPadding, + 'border' => 'none', + 'border-radius' => $buttonBorderRadius, + 'transition' => "all {$transitionDuration} ease", + ]); + + $cssRules[] = $this->cssGenerator->generate('#contactModal .btn-modal-submit:hover', [ + 'background-color' => $buttonHoverBg, + 'color' => $buttonTextColor, + ]); + + $cssRules[] = $this->cssGenerator->generate('#contactModal .btn-modal-submit:disabled', [ + 'opacity' => '0.7', + 'cursor' => 'not-allowed', + ]); + + // Success/Error messages + $successBgColor = $colors['success_bg_color'] ?? '#d1e7dd'; + $successTextColor = $colors['success_text_color'] ?? '#0f5132'; + $errorBgColor = $colors['error_bg_color'] ?? '#f8d7da'; + $errorTextColor = $colors['error_text_color'] ?? '#842029'; + + $cssRules[] = $this->cssGenerator->generate('#contactModal .alert-success', [ + 'background-color' => $successBgColor, + 'color' => $successTextColor, + 'border-color' => $successBgColor, + ]); + + $cssRules[] = $this->cssGenerator->generate('#contactModal .alert-danger', [ + 'background-color' => $errorBgColor, + 'color' => $errorTextColor, + 'border-color' => $errorBgColor, + ]); + + return implode("\n", $cssRules); + } + + /** + * Generar HTML del modal + */ + private function buildModalHTML(array $data): string + { + $content = $data['content'] ?? []; + $formLabels = $data['form_labels'] ?? []; + $effects = $data['visual_effects'] ?? []; + + // Content + $sectionTitle = $content['section_title'] ?? '¿Tienes alguna pregunta?'; + $submitText = $content['submit_button_text'] ?? 'Enviar Mensaje'; + $submitIcon = $content['submit_button_icon'] ?? 'bi-send-fill'; + + // Form labels/placeholders + $fullnamePlaceholder = $formLabels['fullname_placeholder'] ?? 'Nombre completo *'; + $companyPlaceholder = $formLabels['company_placeholder'] ?? 'Empresa'; + $whatsappPlaceholder = $formLabels['whatsapp_placeholder'] ?? 'WhatsApp *'; + $emailPlaceholder = $formLabels['email_placeholder'] ?? 'Correo electrónico *'; + $messagePlaceholder = $formLabels['message_placeholder'] ?? '¿En qué podemos ayudarte?'; + + $textareaRows = $effects['textarea_rows'] ?? '4'; + + // Nonce for AJAX security + $nonce = wp_create_nonce('roi_contact_form_nonce'); + + $html = ''; // .modal + + return $html; + } + + /** + * Generar JS para el modal + */ + private function buildModalJS(array $data): string + { + $messages = $data['messages'] ?? []; + $content = $data['content'] ?? []; + + $successMessage = $messages['success_message'] ?? '¡Gracias por contactarnos! Te responderemos pronto.'; + $errorMessage = $messages['error_message'] ?? 'Hubo un error al enviar el mensaje. Por favor intenta de nuevo.'; + $sendingMessage = $messages['sending_message'] ?? 'Enviando...'; + + // AJAX URL for WordPress + $ajaxUrl = admin_url('admin-ajax.php'); + + $js = <<', esc_attr($buttonIcon)) : ''; - $html .= sprintf( - '', - $buttonAttributes, - $iconHtml, - esc_html($buttonText) - ); + // Use for link action, ', + $buttonAttributes, + $iconHtml, + esc_html($buttonText) + ); + } $html .= ''; diff --git a/Public/CtaPost/Infrastructure/Ui/CtaPostRenderer.php b/Public/CtaPost/Infrastructure/Ui/CtaPostRenderer.php index 831cfb27..2ee6ea19 100644 --- a/Public/CtaPost/Infrastructure/Ui/CtaPostRenderer.php +++ b/Public/CtaPost/Infrastructure/Ui/CtaPostRenderer.php @@ -75,20 +75,31 @@ final class CtaPostRenderer implements RendererInterface { $colors = $data['colors'] ?? []; $effects = $data['visual_effects'] ?? []; + $spacing = $data['spacing'] ?? []; $visibility = $data['visibility'] ?? []; $cssRules = []; + // Container values $gradientStart = $colors['gradient_start'] ?? '#FF8600'; $gradientEnd = $colors['gradient_end'] ?? '#FFB800'; $gradientAngle = $effects['gradient_angle'] ?? '135deg'; + $borderRadius = $effects['border_radius'] ?? '12px'; + $boxShadow = $effects['box_shadow'] ?? '0 8px 24px rgba(255, 133, 0, 0.3)'; + $containerPadding = $spacing['container_padding'] ?? '2rem'; + + // Button values $buttonBgColor = $colors['button_bg_color'] ?? '#FF8600'; $buttonTextColor = $colors['button_text_color'] ?? '#ffffff'; - $buttonHoverBgColor = $colors['button_hover_bg_color'] ?? '#e67a00'; + $buttonHoverBgColor = $colors['button_hover_bg'] ?? '#e67a00'; + $buttonBorderRadius = $effects['button_border_radius'] ?? '8px'; - // Container - gradient background + // Container - gradient background with box-shadow and border-radius $cssRules[] = $this->cssGenerator->generate('.cta-post-container', [ 'background' => "linear-gradient({$gradientAngle}, {$gradientStart} 0%, {$gradientEnd} 100%)", + 'box-shadow' => $boxShadow, + 'border-radius' => $borderRadius, + 'padding' => $containerPadding, ]); // Button styles (matching template .cta-button) - Using !important to override Bootstrap btn-light @@ -98,8 +109,8 @@ final class CtaPostRenderer implements RendererInterface font-weight: 600; padding: 0.75rem 2rem; border: none !important; - border-radius: 8px; - transition: 0.3s; + border-radius: {$buttonBorderRadius}; + transition: all 0.3s ease; text-decoration: none; display: inline-block; }"; @@ -149,7 +160,7 @@ final class CtaPostRenderer implements RendererInterface $buttonUrl = $content['button_url'] ?? '#'; $buttonIcon = $content['button_icon'] ?? 'bi-arrow-right'; - $html = '
'; + $html = '
'; $html .= '
'; // Left column - Content @@ -169,7 +180,7 @@ final class CtaPostRenderer implements RendererInterface // Right column - Button $html .= '
'; $html .= sprintf( - ' %s', + ' %s', esc_url($buttonUrl), esc_html($buttonText) ); diff --git a/Public/Footer/Infrastructure/Api/WordPress/NewsletterAjaxHandler.php b/Public/Footer/Infrastructure/Api/WordPress/NewsletterAjaxHandler.php index 24a92bb1..25f95435 100644 --- a/Public/Footer/Infrastructure/Api/WordPress/NewsletterAjaxHandler.php +++ b/Public/Footer/Infrastructure/Api/WordPress/NewsletterAjaxHandler.php @@ -58,8 +58,11 @@ final class NewsletterAjaxHandler return; } - // 3. Validar email + // 3. Validar y sanitizar campos $email = sanitize_email($_POST['email'] ?? ''); + $name = sanitize_text_field($_POST['name'] ?? ''); + $whatsapp = sanitize_text_field($_POST['whatsapp'] ?? ''); + if (empty($email) || !is_email($email)) { wp_send_json_error([ 'message' => __('Por favor ingresa un email valido.', 'roi-theme') @@ -94,12 +97,20 @@ final class NewsletterAjaxHandler // 5. Preparar payload $payload = [ 'email' => $email, + 'name' => $name, + 'whatsapp' => $whatsapp, 'source' => 'newsletter-footer', + 'pageUrl' => sanitize_url($_POST['pageUrl'] ?? ''), + 'pageTitle' => sanitize_text_field($_POST['pageTitle'] ?? ''), 'timestamp' => current_time('c'), + 'timezone' => wp_timezone_string(), 'siteName' => get_bloginfo('name'), 'siteUrl' => home_url(), ]; + // Debug: Log payload enviado + error_log('ROI Theme Newsletter: Enviando a webhook - ' . wp_json_encode($payload)); + // 6. Enviar a webhook $result = $this->sendToWebhook($webhookUrl, $payload); @@ -120,6 +131,8 @@ final class NewsletterAjaxHandler */ private function sendToWebhook(string $url, array $payload): array { + error_log('ROI Theme Newsletter: Webhook URL - ' . $url); + $response = wp_remote_post($url, [ 'timeout' => 30, 'headers' => [ @@ -130,6 +143,7 @@ final class NewsletterAjaxHandler ]); if (is_wp_error($response)) { + error_log('ROI Theme Newsletter: WP Error - ' . $response->get_error_message()); return [ 'success' => false, 'error' => $response->get_error_message() @@ -137,6 +151,10 @@ final class NewsletterAjaxHandler } $statusCode = wp_remote_retrieve_response_code($response); + $responseBody = wp_remote_retrieve_body($response); + + error_log('ROI Theme Newsletter: Response Code - ' . $statusCode); + error_log('ROI Theme Newsletter: Response Body - ' . $responseBody); if ($statusCode >= 200 && $statusCode < 300) { return ['success' => true, 'error' => '']; diff --git a/Public/Footer/Infrastructure/Ui/FooterRenderer.php b/Public/Footer/Infrastructure/Ui/FooterRenderer.php index 056b0990..3f845ff2 100644 --- a/Public/Footer/Infrastructure/Ui/FooterRenderer.php +++ b/Public/Footer/Infrastructure/Ui/FooterRenderer.php @@ -156,6 +156,11 @@ final class FooterRenderer implements RendererInterface 'color' => $linkHoverColor, ]); + // Widget 1B spacing + $cssRules[] = $this->cssGenerator->generate('.roi-footer .footer-widget-1b', [ + 'margin-top' => '1.5rem', + ]); + // Newsletter description $cssRules[] = $this->cssGenerator->generate('.roi-footer .newsletter-description', [ 'color' => $textColor, @@ -248,6 +253,7 @@ final class FooterRenderer implements RendererInterface private function generateHTML(array $data): string { $widget1 = $data['widget_1'] ?? []; + $widget1b = $data['widget_1b'] ?? []; $widget2 = $data['widget_2'] ?? []; $widget3 = $data['widget_3'] ?? []; $newsletter = $data['newsletter'] ?? []; @@ -259,12 +265,15 @@ final class FooterRenderer implements RendererInterface $newsletterVisible = $this->toBool($newsletter['newsletter_visible'] ?? true); $widget1Title = esc_html($widget1['widget_1_title'] ?? 'Recursos'); + $widget1bTitle = esc_html($widget1b['widget_1b_title'] ?? 'Bases de datos'); $widget2Title = esc_html($widget2['widget_2_title'] ?? 'Soporte'); $widget3Title = esc_html($widget3['widget_3_title'] ?? 'Empresa'); $newsletterTitle = esc_html($newsletter['newsletter_title'] ?? 'Suscribete al Newsletter'); $newsletterDesc = esc_html($newsletter['newsletter_description'] ?? 'Recibe las ultimas actualizaciones.'); - $newsletterPlaceholder = esc_attr($newsletter['newsletter_placeholder'] ?? 'Email'); + $newsletterNamePlaceholder = esc_attr($newsletter['newsletter_name_placeholder'] ?? 'Nombre'); + $newsletterEmailPlaceholder = esc_attr($newsletter['newsletter_email_placeholder'] ?? 'Email'); + $newsletterWhatsappPlaceholder = esc_attr($newsletter['newsletter_whatsapp_placeholder'] ?? 'WhatsApp'); $newsletterBtnText = esc_html($newsletter['newsletter_button_text'] ?? 'Suscribirse'); $copyrightText = esc_html($footerBottom['copyright_text'] ?? date('Y') . ' Todos los derechos reservados.'); @@ -276,12 +285,25 @@ final class FooterRenderer implements RendererInterface $html .= '
'; $html .= '