Phase 4.4 Accessibility fixes: - ContactFormRenderer: Change h6 info-labels to span (WhatsApp, Email, Location) - RelatedPostRenderer: Change h5 card-title to span (semantic hierarchy) - top-notification-bar schema: Change link_url default from # to /suscripcion-vip (identical links must have same destination) Fixes: "Headings not in sequential order" and "Identical links have different purposes" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
778 lines
30 KiB
PHP
778 lines
30 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace ROITheme\Public\ContactForm\Infrastructure\Ui;
|
|
|
|
use ROITheme\Shared\Domain\Contracts\RendererInterface;
|
|
use ROITheme\Shared\Domain\Contracts\CSSGeneratorInterface;
|
|
use ROITheme\Shared\Domain\Entities\Component;
|
|
|
|
/**
|
|
* ContactFormRenderer - Renderiza formulario de contacto con webhook
|
|
*
|
|
* RESPONSABILIDAD: Generar HTML y CSS del formulario de contacto
|
|
*
|
|
* CARACTERISTICAS:
|
|
* - Formulario responsive Bootstrap 5
|
|
* - Envio a webhook configurable (no expuesto en frontend)
|
|
* - Info de contacto configurable
|
|
* - Mensajes de exito/error personalizables
|
|
*
|
|
* @package ROITheme\Public\ContactForm\Infrastructure\Ui
|
|
*/
|
|
final class ContactFormRenderer implements RendererInterface
|
|
{
|
|
public function __construct(
|
|
private CSSGeneratorInterface $cssGenerator
|
|
) {}
|
|
|
|
public function render(Component $component): string
|
|
{
|
|
$data = $component->getData();
|
|
|
|
if (!$this->isEnabled($data)) {
|
|
return '';
|
|
}
|
|
|
|
if (!$this->shouldShowOnCurrentPage($data)) {
|
|
return '';
|
|
}
|
|
|
|
$visibilityClass = $this->getVisibilityClass($data);
|
|
if ($visibilityClass === null) {
|
|
return '';
|
|
}
|
|
|
|
$css = $this->generateCSS($data);
|
|
$html = $this->buildHTML($data, $visibilityClass);
|
|
$js = $this->buildJS($data);
|
|
|
|
return sprintf("<style>%s</style>\n%s\n<script>%s</script>", $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("<style>%s</style>\n%s\n<script>%s</script>", $css, $html, $js);
|
|
}
|
|
|
|
public function supports(string $componentType): bool
|
|
{
|
|
return $componentType === 'contact-form';
|
|
}
|
|
|
|
private function isEnabled(array $data): bool
|
|
{
|
|
$value = $data['visibility']['is_enabled'] ?? false;
|
|
return $value === true || $value === '1' || $value === 1;
|
|
}
|
|
|
|
private function shouldShowOnCurrentPage(array $data): bool
|
|
{
|
|
$showOn = $data['visibility']['show_on_pages'] ?? 'all';
|
|
|
|
switch ($showOn) {
|
|
case 'all':
|
|
return true;
|
|
case 'posts':
|
|
return is_single();
|
|
case 'pages':
|
|
return is_page();
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private function getVisibilityClass(array $data): ?string
|
|
{
|
|
$showDesktop = $data['visibility']['show_on_desktop'] ?? true;
|
|
$showDesktop = $showDesktop === true || $showDesktop === '1' || $showDesktop === 1;
|
|
$showMobile = $data['visibility']['show_on_mobile'] ?? true;
|
|
$showMobile = $showMobile === true || $showMobile === '1' || $showMobile === 1;
|
|
|
|
if (!$showDesktop && !$showMobile) {
|
|
return null;
|
|
}
|
|
if (!$showDesktop && $showMobile) {
|
|
return 'd-lg-none';
|
|
}
|
|
if ($showDesktop && !$showMobile) {
|
|
return 'd-none d-lg-block';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
private function generateCSS(array $data): string
|
|
{
|
|
$colors = $data['colors'] ?? [];
|
|
$spacing = $data['spacing'] ?? [];
|
|
$effects = $data['visual_effects'] ?? [];
|
|
|
|
$cssRules = [];
|
|
|
|
// Section background
|
|
$sectionBgColor = $colors['section_bg_color'] ?? 'rgba(108, 117, 125, 0.25)';
|
|
$sectionPaddingY = $spacing['section_padding_y'] ?? '3rem';
|
|
$sectionMarginTop = $spacing['section_margin_top'] ?? '3rem';
|
|
|
|
$cssRules[] = $this->cssGenerator->generate('.roi-contact-form-section', [
|
|
'background-color' => $sectionBgColor,
|
|
'padding-top' => $sectionPaddingY,
|
|
'padding-bottom' => $sectionPaddingY,
|
|
'margin-top' => $sectionMarginTop,
|
|
]);
|
|
|
|
// Title
|
|
$titleColor = $colors['title_color'] ?? '#212529';
|
|
$titleMarginBottom = $spacing['title_margin_bottom'] ?? '0.75rem';
|
|
|
|
$cssRules[] = $this->cssGenerator->generate('.roi-contact-form-section .contact-title', [
|
|
'color' => $titleColor,
|
|
'margin-bottom' => $titleMarginBottom,
|
|
]);
|
|
|
|
// Description
|
|
$descColor = $colors['description_color'] ?? '#212529';
|
|
$descMarginBottom = $spacing['description_margin_bottom'] ?? '1.5rem';
|
|
|
|
$cssRules[] = $this->cssGenerator->generate('.roi-contact-form-section .contact-description', [
|
|
'color' => $descColor,
|
|
'margin-bottom' => $descMarginBottom,
|
|
]);
|
|
|
|
// Icons
|
|
$iconColor = $colors['icon_color'] ?? '#FF8600';
|
|
|
|
$cssRules[] = $this->cssGenerator->generate('.roi-contact-form-section .contact-icon', [
|
|
'color' => $iconColor,
|
|
]);
|
|
|
|
// Info labels and values
|
|
$infoLabelColor = $colors['info_label_color'] ?? '#212529';
|
|
$infoValueColor = $colors['info_value_color'] ?? '#6c757d';
|
|
|
|
$cssRules[] = $this->cssGenerator->generate('.roi-contact-form-section .info-label', [
|
|
'color' => $infoLabelColor,
|
|
]);
|
|
|
|
$cssRules[] = $this->cssGenerator->generate('.roi-contact-form-section .info-value', [
|
|
'color' => $infoValueColor,
|
|
]);
|
|
|
|
// 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('.roi-contact-form-section .form-control', [
|
|
'border-color' => $inputBorderColor,
|
|
'border-radius' => $inputBorderRadius,
|
|
'transition' => "all {$transitionDuration} ease",
|
|
]);
|
|
|
|
$cssRules[] = $this->cssGenerator->generate('.roi-contact-form-section .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('.roi-contact-form-section .btn-contact-submit', [
|
|
'background-color' => $buttonBgColor,
|
|
'color' => $buttonTextColor,
|
|
'font-weight' => '600',
|
|
'padding' => $buttonPadding,
|
|
'border' => 'none',
|
|
'border-radius' => $buttonBorderRadius,
|
|
'transition' => "all {$transitionDuration} ease",
|
|
]);
|
|
|
|
$cssRules[] = $this->cssGenerator->generate('.roi-contact-form-section .btn-contact-submit:hover', [
|
|
'background-color' => $buttonHoverBg,
|
|
'color' => $buttonTextColor,
|
|
]);
|
|
|
|
$cssRules[] = $this->cssGenerator->generate('.roi-contact-form-section .btn-contact-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('.roi-contact-form-section .alert-success', [
|
|
'background-color' => $successBgColor,
|
|
'color' => $successTextColor,
|
|
'border-color' => $successBgColor,
|
|
]);
|
|
|
|
$cssRules[] = $this->cssGenerator->generate('.roi-contact-form-section .alert-danger', [
|
|
'background-color' => $errorBgColor,
|
|
'color' => $errorTextColor,
|
|
'border-color' => $errorBgColor,
|
|
]);
|
|
|
|
return implode("\n", $cssRules);
|
|
}
|
|
|
|
private function buildHTML(array $data, string $visibilityClass): string
|
|
{
|
|
$content = $data['content'] ?? [];
|
|
$contactInfo = $data['contact_info'] ?? [];
|
|
$formLabels = $data['form_labels'] ?? [];
|
|
$effects = $data['visual_effects'] ?? [];
|
|
|
|
// Content
|
|
$sectionTitle = $content['section_title'] ?? '¿Tienes alguna pregunta?';
|
|
$sectionDesc = $content['section_description'] ?? 'Completa el formulario y nuestro equipo te responderá en menos de 24 horas.';
|
|
$submitText = $content['submit_button_text'] ?? 'Enviar Mensaje';
|
|
$submitIcon = $content['submit_button_icon'] ?? 'bi-send-fill';
|
|
|
|
// Contact info
|
|
$showContactInfo = $contactInfo['show_contact_info'] ?? true;
|
|
$showContactInfo = $showContactInfo === true || $showContactInfo === '1' || $showContactInfo === 1;
|
|
|
|
// 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';
|
|
|
|
// Container class
|
|
$containerClass = 'roi-contact-form-section';
|
|
if (!empty($visibilityClass)) {
|
|
$containerClass .= ' ' . $visibilityClass;
|
|
}
|
|
|
|
// Nonce for AJAX security
|
|
$nonce = wp_create_nonce('roi_contact_form_nonce');
|
|
|
|
$html = sprintf('<section class="%s">', esc_attr($containerClass));
|
|
$html .= '<div class="container">';
|
|
$html .= '<div class="row justify-content-center">';
|
|
$html .= '<div class="col-lg-10">';
|
|
$html .= '<div class="row">';
|
|
|
|
// Left column - Contact info
|
|
$html .= '<div class="col-lg-5 mb-4 mb-lg-0">';
|
|
$html .= sprintf('<h2 class="h3 contact-title">%s</h2>', esc_html($sectionTitle));
|
|
$html .= sprintf('<p class="contact-description">%s</p>', esc_html($sectionDesc));
|
|
|
|
if ($showContactInfo) {
|
|
$html .= $this->buildContactInfoHTML($contactInfo);
|
|
}
|
|
|
|
$html .= '</div>';
|
|
|
|
// Right column - Form
|
|
$html .= '<div class="col-lg-7">';
|
|
$html .= sprintf('<form id="roiContactForm" data-nonce="%s">', esc_attr($nonce));
|
|
$html .= '<div class="row g-3">';
|
|
|
|
// Full name field
|
|
$html .= '<div class="col-md-6">';
|
|
$html .= sprintf(
|
|
'<input type="text" class="form-control" id="roiContactFullName" name="fullName" placeholder="%s" required>',
|
|
esc_attr($fullnamePlaceholder)
|
|
);
|
|
$html .= '</div>';
|
|
|
|
// Company field
|
|
$html .= '<div class="col-md-6">';
|
|
$html .= sprintf(
|
|
'<input type="text" class="form-control" id="roiContactCompany" name="company" placeholder="%s">',
|
|
esc_attr($companyPlaceholder)
|
|
);
|
|
$html .= '</div>';
|
|
|
|
// WhatsApp field
|
|
$html .= '<div class="col-md-6">';
|
|
$html .= sprintf(
|
|
'<input type="tel" class="form-control" id="roiContactWhatsapp" name="whatsapp" placeholder="%s" required>',
|
|
esc_attr($whatsappPlaceholder)
|
|
);
|
|
$html .= '</div>';
|
|
|
|
// Email field
|
|
$html .= '<div class="col-md-6">';
|
|
$html .= sprintf(
|
|
'<input type="email" class="form-control" id="roiContactEmail" name="email" placeholder="%s" required>',
|
|
esc_attr($emailPlaceholder)
|
|
);
|
|
$html .= '</div>';
|
|
|
|
// Message field
|
|
$html .= '<div class="col-12">';
|
|
$html .= sprintf(
|
|
'<textarea class="form-control" id="roiContactMessage" name="message" rows="%s" placeholder="%s"></textarea>',
|
|
esc_attr($textareaRows),
|
|
esc_attr($messagePlaceholder)
|
|
);
|
|
$html .= '</div>';
|
|
|
|
// Submit button
|
|
$html .= '<div class="col-12">';
|
|
$html .= '<button type="submit" class="btn btn-contact-submit w-100">';
|
|
$html .= sprintf('<i class="%s me-2"></i>', esc_attr($submitIcon));
|
|
$html .= esc_html($submitText);
|
|
$html .= '</button>';
|
|
$html .= '</div>';
|
|
|
|
// Message container
|
|
$html .= '<div id="roiContactFormMessage" class="col-12 mt-2 alert" style="display: none;"></div>';
|
|
|
|
$html .= '</div>'; // .row g-3
|
|
$html .= '</form>';
|
|
$html .= '</div>'; // .col-lg-7
|
|
|
|
$html .= '</div>'; // .row
|
|
$html .= '</div>'; // .col-lg-10
|
|
$html .= '</div>'; // .row justify-content-center
|
|
$html .= '</div>'; // .container
|
|
$html .= '</section>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
private function buildContactInfoHTML(array $contactInfo): string
|
|
{
|
|
$phoneLabel = $contactInfo['phone_label'] ?? 'Teléfono';
|
|
$phoneValue = $contactInfo['phone_value'] ?? '+52 55 1234 5678';
|
|
$emailLabel = $contactInfo['email_label'] ?? 'Email';
|
|
$emailValue = $contactInfo['email_value'] ?? 'contacto@apumexico.com';
|
|
$locationLabel = $contactInfo['location_label'] ?? 'Ubicación';
|
|
$locationValue = $contactInfo['location_value'] ?? 'Ciudad de México, México';
|
|
|
|
$html = '<div class="contact-info">';
|
|
|
|
// Phone
|
|
$html .= '<div class="d-flex align-items-start mb-3">';
|
|
$html .= '<i class="bi bi-telephone-fill me-3 fs-5 contact-icon"></i>';
|
|
$html .= '<div>';
|
|
$html .= sprintf('<span class="d-block mb-1 info-label fw-semibold">%s</span>', esc_html($phoneLabel));
|
|
$html .= sprintf('<p class="mb-0 info-value">%s</p>', esc_html($phoneValue));
|
|
$html .= '</div>';
|
|
$html .= '</div>';
|
|
|
|
// Email
|
|
$html .= '<div class="d-flex align-items-start mb-3">';
|
|
$html .= '<i class="bi bi-envelope-fill me-3 fs-5 contact-icon"></i>';
|
|
$html .= '<div>';
|
|
$html .= sprintf('<span class="d-block mb-1 info-label fw-semibold">%s</span>', esc_html($emailLabel));
|
|
$html .= sprintf('<p class="mb-0 info-value">%s</p>', esc_html($emailValue));
|
|
$html .= '</div>';
|
|
$html .= '</div>';
|
|
|
|
// Location
|
|
$html .= '<div class="d-flex align-items-start">';
|
|
$html .= '<i class="bi bi-geo-alt-fill me-3 fs-5 contact-icon"></i>';
|
|
$html .= '<div>';
|
|
$html .= sprintf('<span class="d-block mb-1 info-label fw-semibold">%s</span>', esc_html($locationLabel));
|
|
$html .= sprintf('<p class="mb-0 info-value">%s</p>', esc_html($locationValue));
|
|
$html .= '</div>';
|
|
$html .= '</div>';
|
|
|
|
$html .= '</div>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
private function buildJS(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...';
|
|
$submitText = $content['submit_button_text'] ?? 'Enviar Mensaje';
|
|
$submitIcon = $content['submit_button_icon'] ?? 'bi-send-fill';
|
|
|
|
// AJAX URL for WordPress
|
|
$ajaxUrl = admin_url('admin-ajax.php');
|
|
|
|
$js = <<<JS
|
|
(function() {
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const form = document.getElementById('roiContactForm');
|
|
if (!form) return;
|
|
|
|
form.addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
|
|
const submitBtn = form.querySelector('button[type="submit"]');
|
|
const messageDiv = document.getElementById('roiContactFormMessage');
|
|
const originalBtnHtml = submitBtn.innerHTML;
|
|
const nonce = form.dataset.nonce;
|
|
|
|
// Disable button and show sending state
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>' + '{$sendingMessage}';
|
|
messageDiv.style.display = 'none';
|
|
|
|
// Collect form data
|
|
const formData = new FormData(form);
|
|
formData.append('action', 'roi_contact_form_submit');
|
|
formData.append('nonce', nonce);
|
|
formData.append('pageUrl', window.location.href);
|
|
formData.append('pageTitle', document.title);
|
|
|
|
try {
|
|
const response = await fetch('{$ajaxUrl}', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
messageDiv.className = 'col-12 mt-2 alert alert-success';
|
|
messageDiv.textContent = '{$successMessage}';
|
|
messageDiv.style.display = 'block';
|
|
form.reset();
|
|
} else {
|
|
messageDiv.className = 'col-12 mt-2 alert alert-danger';
|
|
messageDiv.textContent = result.data?.message || '{$errorMessage}';
|
|
messageDiv.style.display = 'block';
|
|
}
|
|
} catch (error) {
|
|
console.error('Contact form error:', error);
|
|
messageDiv.className = 'col-12 mt-2 alert alert-danger';
|
|
messageDiv.textContent = '{$errorMessage}';
|
|
messageDiv.style.display = 'block';
|
|
} finally {
|
|
submitBtn.disabled = false;
|
|
submitBtn.innerHTML = originalBtnHtml;
|
|
}
|
|
});
|
|
});
|
|
})();
|
|
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 = '<div class="modal fade" id="contactModal" tabindex="-1" aria-labelledby="contactModalLabel" aria-hidden="true">';
|
|
$html .= '<div class="modal-dialog modal-dialog-centered modal-lg">';
|
|
$html .= '<div class="modal-content">';
|
|
|
|
// Modal Header
|
|
$html .= '<div class="modal-header">';
|
|
$html .= '<h5 class="modal-title" id="contactModalLabel">';
|
|
$html .= '<i class="bi bi-chat-dots-fill me-2" style="color: #FF8600;"></i>';
|
|
$html .= esc_html($sectionTitle);
|
|
$html .= '</h5>';
|
|
$html .= '<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>';
|
|
$html .= '</div>';
|
|
|
|
// Modal Body
|
|
$html .= '<div class="modal-body">';
|
|
$html .= sprintf('<form id="roiContactModalForm" data-nonce="%s">', esc_attr($nonce));
|
|
$html .= '<div class="row g-3">';
|
|
|
|
// Full name field
|
|
$html .= '<div class="col-md-6">';
|
|
$html .= sprintf(
|
|
'<input type="text" class="form-control" id="roiModalFullName" name="fullName" placeholder="%s" required>',
|
|
esc_attr($fullnamePlaceholder)
|
|
);
|
|
$html .= '</div>';
|
|
|
|
// Company field
|
|
$html .= '<div class="col-md-6">';
|
|
$html .= sprintf(
|
|
'<input type="text" class="form-control" id="roiModalCompany" name="company" placeholder="%s">',
|
|
esc_attr($companyPlaceholder)
|
|
);
|
|
$html .= '</div>';
|
|
|
|
// WhatsApp field
|
|
$html .= '<div class="col-md-6">';
|
|
$html .= sprintf(
|
|
'<input type="tel" class="form-control" id="roiModalWhatsapp" name="whatsapp" placeholder="%s" required>',
|
|
esc_attr($whatsappPlaceholder)
|
|
);
|
|
$html .= '</div>';
|
|
|
|
// Email field
|
|
$html .= '<div class="col-md-6">';
|
|
$html .= sprintf(
|
|
'<input type="email" class="form-control" id="roiModalEmail" name="email" placeholder="%s" required>',
|
|
esc_attr($emailPlaceholder)
|
|
);
|
|
$html .= '</div>';
|
|
|
|
// Message field
|
|
$html .= '<div class="col-12">';
|
|
$html .= sprintf(
|
|
'<textarea class="form-control" id="roiModalMessage" name="message" rows="%s" placeholder="%s"></textarea>',
|
|
esc_attr($textareaRows),
|
|
esc_attr($messagePlaceholder)
|
|
);
|
|
$html .= '</div>';
|
|
|
|
// Submit button
|
|
$html .= '<div class="col-12">';
|
|
$html .= '<button type="submit" class="btn btn-modal-submit w-100">';
|
|
$html .= sprintf('<i class="%s me-2"></i>', esc_attr($submitIcon));
|
|
$html .= esc_html($submitText);
|
|
$html .= '</button>';
|
|
$html .= '</div>';
|
|
|
|
// Message container
|
|
$html .= '<div id="roiContactModalMessage" class="col-12 mt-2 alert" style="display: none;"></div>';
|
|
|
|
$html .= '</div>'; // .row g-3
|
|
$html .= '</form>';
|
|
$html .= '</div>'; // .modal-body
|
|
|
|
$html .= '</div>'; // .modal-content
|
|
$html .= '</div>'; // .modal-dialog
|
|
$html .= '</div>'; // .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 = <<<JS
|
|
(function() {
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const form = document.getElementById('roiContactModalForm');
|
|
if (!form) return;
|
|
|
|
form.addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
|
|
const submitBtn = form.querySelector('button[type="submit"]');
|
|
const messageDiv = document.getElementById('roiContactModalMessage');
|
|
const originalBtnHtml = submitBtn.innerHTML;
|
|
const nonce = form.dataset.nonce;
|
|
|
|
// Disable button and show sending state
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>' + '{$sendingMessage}';
|
|
messageDiv.style.display = 'none';
|
|
|
|
// Collect form data
|
|
const formData = new FormData(form);
|
|
formData.append('action', 'roi_contact_form_submit');
|
|
formData.append('nonce', nonce);
|
|
formData.append('pageUrl', window.location.href);
|
|
formData.append('pageTitle', document.title);
|
|
|
|
try {
|
|
const response = await fetch('{$ajaxUrl}', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
messageDiv.className = 'col-12 mt-2 alert alert-success';
|
|
messageDiv.textContent = '{$successMessage}';
|
|
messageDiv.style.display = 'block';
|
|
form.reset();
|
|
|
|
// Cerrar modal despues de 2 segundos en exito
|
|
setTimeout(function() {
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('contactModal'));
|
|
if (modal) {
|
|
modal.hide();
|
|
}
|
|
messageDiv.style.display = 'none';
|
|
}, 2000);
|
|
} else {
|
|
messageDiv.className = 'col-12 mt-2 alert alert-danger';
|
|
messageDiv.textContent = result.data?.message || '{$errorMessage}';
|
|
messageDiv.style.display = 'block';
|
|
}
|
|
} catch (error) {
|
|
console.error('Contact modal form error:', error);
|
|
messageDiv.className = 'col-12 mt-2 alert alert-danger';
|
|
messageDiv.textContent = '{$errorMessage}';
|
|
messageDiv.style.display = 'block';
|
|
} finally {
|
|
submitBtn.disabled = false;
|
|
submitBtn.innerHTML = originalBtnHtml;
|
|
}
|
|
});
|
|
|
|
// Limpiar formulario cuando se cierra el modal
|
|
const contactModal = document.getElementById('contactModal');
|
|
if (contactModal) {
|
|
contactModal.addEventListener('hidden.bs.modal', function() {
|
|
form.reset();
|
|
const messageDiv = document.getElementById('roiContactModalMessage');
|
|
if (messageDiv) {
|
|
messageDiv.style.display = 'none';
|
|
}
|
|
});
|
|
}
|
|
});
|
|
})();
|
|
JS;
|
|
|
|
return $js;
|
|
}
|
|
}
|