";
- $changes++;
- }
- }
- }
-
- if ($changes > 0) {
- $wrapper = $doc->getElementById('temp-wrapper');
- if ($wrapper) {
- $innerHTML = '';
- foreach ($wrapper->childNodes as $child) {
- $innerHTML .= $doc->saveHTML($child);
- }
- $result['html'] = $innerHTML;
- $result['fixed'] = true;
- $result['changes'] = $changes;
- }
- }
-
- return $result;
-}
-
-/**
- * Generar HTML wrapper para visualización
- */
-function wrapForVisualization(string $content, string $title, string $status): string {
- $statusColor = $status === 'error' ? '#dc3545' : '#28a745';
- return <<
-
-
-
-
- $title
-
-
-
- $status
-
- $content
-
-
-
-HTML;
-}
-
-// Conectar a DB
-$conn = new mysqli($db_config['host'], $db_config['username'], $db_config['password'], $db_config['database']);
-$conn->set_charset($db_config['charset']);
-
-if ($conn->connect_error) {
- die("Error de conexión: " . $conn->connect_error);
-}
-
-echo "✓ Conexión establecida\n\n";
-
-// Buscar posts con problemas
-$query = "SELECT id, page, html FROM datos_seo_pagina WHERE html IS NOT NULL AND html != '' ORDER BY id LIMIT 500";
-$result = $conn->query($query);
-
-$samples = [];
-while ($row = $result->fetch_assoc()) {
- $issues = detectIssues($row['html']);
- if (!empty($issues) && count($samples) < $sample_size) {
- $samples[] = $row;
- }
-}
-
-echo "Encontrados " . count($samples) . " posts con problemas para validar\n\n";
-
-$comparison_data = [];
-
-foreach ($samples as $idx => $post) {
- $id = $post['id'];
- $url = $post['page'];
- $html_before = $post['html'];
-
- echo "─────────────────────────────────\n";
- echo "POST $id: $url\n";
-
- // Detectar problemas antes
- $issues_before = detectIssues($html_before);
- echo " Problemas ANTES: " . count($issues_before) . "\n";
-
- // Aplicar corrección
- $fixResult = fixMalformedLists($html_before);
- $html_after = $fixResult['html'];
-
- // Detectar problemas después
- $issues_after = detectIssues($html_after);
- echo " Problemas DESPUÉS: " . count($issues_after) . "\n";
- echo " Cambios aplicados: " . $fixResult['changes'] . "\n";
-
- // Guardar archivos HTML
- $file_before = "$output_dir/post_{$id}_BEFORE.html";
- $file_after = "$output_dir/post_{$id}_AFTER.html";
-
- file_put_contents($file_before, wrapForVisualization(
- $html_before,
- "Post $id - ANTES (con errores)",
- "ANTES: " . count($issues_before) . " problemas de listas"
- ));
-
- file_put_contents($file_after, wrapForVisualization(
- $html_after,
- "Post $id - DESPUÉS (corregido)",
- "DESPUÉS: " . count($issues_after) . " problemas - " . $fixResult['changes'] . " correcciones aplicadas"
- ));
-
- echo " ✓ Archivos generados:\n";
- echo " - $file_before\n";
- echo " - $file_after\n";
-
- // Guardar datos para reporte
- $comparison_data[] = [
- 'id' => $id,
- 'url' => $url,
- 'issues_before' => count($issues_before),
- 'issues_after' => count($issues_after),
- 'changes' => $fixResult['changes'],
- 'file_before' => "post_{$id}_BEFORE.html",
- 'file_after' => "post_{$id}_AFTER.html"
- ];
-}
-
-// Generar reporte comparativo
-$report_html = <<
-
-
-
- Reporte de Validación - Corrección de Listas
-
-
-
- Reporte de Validación - Corrección de Listas HTML
-
-
-
Instrucciones:
-
- Abre cada par de archivos (ANTES/DESPUÉS) en el navegador
- Verifica que el contenido se muestre correctamente
- Las listas (fondo amarillo) deben contener solo items (fondo verde)
- Si todo se ve bien, la corrección es segura
-
-
-
-
-
-
- ID
- URL
- Problemas Antes
- Problemas Después
- Cambios
- Archivos
-
-
-
-HTML;
-
-foreach ($comparison_data as $data) {
- $status_class = $data['issues_after'] == 0 ? 'success' : ($data['issues_after'] < $data['issues_before'] ? 'warning' : 'error');
-
- $report_html .= <<
- {$data['id']}
- {$data['url']}
- {$data['issues_before']}
- {$data['issues_after']}
- {$data['changes']}
-
- ANTES |
- DESPUÉS
-
-
-HTML;
-}
-
-$report_html .= <<
-
-
- Generado: {$_SERVER['REQUEST_TIME_FLOAT']}
-
-
-HTML;
-
-$report_file = "$output_dir/comparison_report.html";
-file_put_contents($report_file, $report_html);
-
-echo "\n─────────────────────────────────\n";
-echo "REPORTE GENERADO:\n";
-echo " $report_file\n\n";
-echo "Para revisar, descarga el directorio:\n";
-echo " scp -r VPSContabo:$output_dir ./validation/\n\n";
-
-$conn->close();
-echo "✓ Validación completada.\n";
diff --git a/Shared/Infrastructure/Services/.gitkeep b/Shared/Infrastructure/Services/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/Shared/Infrastructure/Ui/TopNotificationBarFormBuilder.php b/Shared/Infrastructure/Ui/TopNotificationBarFormBuilder.php
deleted file mode 100644
index 779c599b..00000000
--- a/Shared/Infrastructure/Ui/TopNotificationBarFormBuilder.php
+++ /dev/null
@@ -1,697 +0,0 @@
-getData();
- $componentId = $component->getName();
-
- $html = '';
-
- // Sección de Visibilidad
- $html .= $this->buildVisibilitySection($data, $componentId);
-
- // Sección de Contenido
- $html .= $this->buildContentSection($data, $componentId);
-
- // Sección de Estilos
- $html .= $this->buildStylesSection($data, $componentId);
-
- // Vista previa
- $html .= $this->buildPreviewSection($data);
-
- $html .= '
';
-
- // Agregar scripts de formulario
- $html .= $this->buildFormScripts($componentId);
-
- return $html;
- }
-
- private function buildVisibilitySection(array $data, string $componentId): string
- {
- $html = '';
-
- return $html;
- }
-
- private function buildContentSection(array $data, string $componentId): string
- {
- $html = '';
- $html .= '
';
- $html .= '
';
-
- // Icon Type
- $iconType = $data['content']['icon_type'] ?? 'bootstrap';
- $html .= $this->buildSelect(
- 'icon_type',
- 'Tipo de ícono',
- $iconType,
- [
- 'bootstrap' => 'Bootstrap Icons',
- 'custom' => 'Imagen personalizada',
- 'none' => 'Sin ícono'
- ],
- $componentId,
- 'Selecciona el tipo de ícono a mostrar'
- );
-
- // Bootstrap Icon
- $bootstrapIcon = $data['content']['bootstrap_icon'] ?? 'bi-megaphone-fill';
- $html .= $this->buildTextField(
- 'bootstrap_icon',
- 'Clase de ícono Bootstrap',
- $bootstrapIcon,
- $componentId,
- 'Nombre de la clase del ícono sin el prefijo \'bi\' (ej: megaphone-fill)',
- 'Ej: bi-megaphone-fill',
- ['data-conditional-field' => 'icon_type', 'data-conditional-value' => 'bootstrap']
- );
-
- // Custom Icon URL
- $customIconUrl = $data['content']['custom_icon_url'] ?? '';
- $html .= $this->buildMediaField(
- 'custom_icon_url',
- 'Imagen personalizada',
- $customIconUrl,
- $componentId,
- 'Sube una imagen personalizada (recomendado: PNG 24x24px)',
- ['data-conditional-field' => 'icon_type', 'data-conditional-value' => 'custom']
- );
-
- // Announcement Label
- $announcementLabel = $data['content']['announcement_label'] ?? 'Nuevo:';
- $html .= $this->buildTextField(
- 'announcement_label',
- 'Etiqueta del anuncio',
- $announcementLabel,
- $componentId,
- 'Texto destacado en negrita antes del mensaje',
- 'Ej: Nuevo:, Importante:, Aviso:'
- );
-
- // Announcement Text
- $announcementText = $data['content']['announcement_text'] ?? 'Accede a más de 200,000 Análisis de Precios Unitarios actualizados para 2025.';
- $html .= $this->buildTextArea(
- 'announcement_text',
- 'Texto del anuncio',
- $announcementText,
- $componentId,
- 'Mensaje principal del anuncio (máximo 200 caracteres)',
- 3
- );
-
- // Link Enabled
- $linkEnabled = $data['content']['link_enabled'] ?? true;
- $html .= $this->buildToggle(
- 'link_enabled',
- 'Mostrar enlace',
- $linkEnabled,
- $componentId,
- 'Activa o desactiva el enlace de acción'
- );
-
- // Link Text
- $linkText = $data['content']['link_text'] ?? 'Ver Catálogo';
- $html .= $this->buildTextField(
- 'link_text',
- 'Texto del enlace',
- $linkText,
- $componentId,
- 'Texto del enlace de acción',
- '',
- ['data-conditional-field' => 'link_enabled', 'data-conditional-value' => 'true']
- );
-
- // Link URL
- $linkUrl = $data['content']['link_url'] ?? '#';
- $html .= $this->buildUrlField(
- 'link_url',
- 'URL del enlace',
- $linkUrl,
- $componentId,
- 'URL de destino del enlace',
- 'https://',
- ['data-conditional-field' => 'link_enabled', 'data-conditional-value' => 'true']
- );
-
- // Link Target
- $linkTarget = $data['content']['link_target'] ?? '_self';
- $html .= $this->buildSelect(
- 'link_target',
- 'Abrir enlace en',
- $linkTarget,
- [
- '_self' => 'Misma ventana',
- '_blank' => 'Nueva ventana'
- ],
- $componentId,
- 'Define cómo se abrirá el enlace',
- ['data-conditional-field' => 'link_enabled', 'data-conditional-value' => 'true']
- );
-
- $html .= '
';
- $html .= '
';
-
- return $html;
- }
-
- private function buildStylesSection(array $data, string $componentId): string
- {
- $html = '';
- $html .= '
';
- $html .= '
';
-
- // Background Color
- $bgColor = $data['styles']['background_color'] ?? '#FF8600';
- $html .= $this->buildColorField(
- 'background_color',
- 'Color de fondo',
- $bgColor,
- $componentId,
- 'Color de fondo de la barra (por defecto: orange primary)'
- );
-
- // Text Color
- $textColor = $data['styles']['text_color'] ?? '#FFFFFF';
- $html .= $this->buildColorField(
- 'text_color',
- 'Color del texto',
- $textColor,
- $componentId,
- 'Color del texto del anuncio'
- );
-
- // Link Color
- $linkColor = $data['styles']['link_color'] ?? '#FFFFFF';
- $html .= $this->buildColorField(
- 'link_color',
- 'Color del enlace',
- $linkColor,
- $componentId,
- 'Color del enlace de acción'
- );
-
- // Font Size
- $fontSize = $data['styles']['font_size'] ?? 'small';
- $html .= $this->buildSelect(
- 'font_size',
- 'Tamaño de fuente',
- $fontSize,
- [
- 'extra-small' => 'Muy pequeño (0.75rem)',
- 'small' => 'Pequeño (0.875rem)',
- 'normal' => 'Normal (1rem)',
- 'large' => 'Grande (1.125rem)'
- ],
- $componentId,
- 'Tamaño del texto del anuncio'
- );
-
- // Padding Vertical
- $padding = $data['styles']['padding_vertical'] ?? 'normal';
- $html .= $this->buildSelect(
- 'padding_vertical',
- 'Padding vertical',
- $padding,
- [
- 'compact' => 'Compacto (0.5rem)',
- 'normal' => 'Normal (0.75rem)',
- 'spacious' => 'Espacioso (1rem)'
- ],
- $componentId,
- 'Espaciado vertical interno de la barra'
- );
-
- // Text Alignment
- $alignment = $data['styles']['text_alignment'] ?? 'center';
- $html .= $this->buildSelect(
- 'text_alignment',
- 'Alineación del texto',
- $alignment,
- [
- 'left' => 'Izquierda',
- 'center' => 'Centro',
- 'right' => 'Derecha'
- ],
- $componentId,
- 'Alineación del contenido de la barra'
- );
-
- // Animation Enabled
- $animationEnabled = $data['styles']['animation_enabled'] ?? false;
- $html .= $this->buildToggle(
- 'animation_enabled',
- 'Activar animación',
- $animationEnabled,
- $componentId,
- 'Activa animación de entrada al cargar la página'
- );
-
- // Animation Type
- $animationType = $data['styles']['animation_type'] ?? 'slide-down';
- $html .= $this->buildSelect(
- 'animation_type',
- 'Tipo de animación',
- $animationType,
- [
- 'slide-down' => 'Deslizar desde arriba',
- 'fade-in' => 'Aparecer gradualmente'
- ],
- $componentId,
- 'Tipo de animación de entrada',
- ['data-conditional-field' => 'animation_enabled', 'data-conditional-value' => 'true']
- );
-
- $html .= '
';
- $html .= '
';
-
- return $html;
- }
-
- private function buildPreviewSection(array $data): string
- {
- $html = '';
-
- return $html;
- }
-
- private function buildToggle(string $name, string $label, bool $value, string $componentId, string $description = ''): string
- {
- $fieldId = "roi_{$componentId}_{$name}";
- $checked = $value ? 'checked' : '';
-
- $html = '';
-
- return $html;
- }
-
- private function buildTextField(string $name, string $label, string $value, string $componentId, string $description = '', string $placeholder = '', array $attrs = []): string
- {
- $fieldId = "roi_{$componentId}_{$name}";
-
- $html = '';
- $html .= sprintf('%s ', esc_attr($fieldId), esc_html($label));
-
- $attrString = $this->buildAttributesString($attrs);
-
- $html .= sprintf(
- ' ',
- esc_attr($fieldId),
- esc_attr($componentId),
- esc_attr($name),
- esc_attr($value),
- esc_attr($placeholder),
- $attrString
- );
-
- if (!empty($description)) {
- $html .= sprintf('%s ', esc_html($description));
- }
- $html .= '
';
-
- return $html;
- }
-
- private function buildTextArea(string $name, string $label, string $value, string $componentId, string $description = '', int $rows = 3, array $attrs = []): string
- {
- $fieldId = "roi_{$componentId}_{$name}";
-
- $html = '';
- $html .= sprintf('%s ', esc_attr($fieldId), esc_html($label));
-
- $attrString = $this->buildAttributesString($attrs);
-
- $html .= sprintf(
- '',
- esc_attr($fieldId),
- esc_attr($componentId),
- esc_attr($name),
- $rows,
- $attrString,
- esc_textarea($value)
- );
-
- if (!empty($description)) {
- $html .= sprintf('%s ', esc_html($description));
- }
- $html .= '
';
-
- return $html;
- }
-
- private function buildSelect(string $name, string $label, string $value, array $options, string $componentId, string $description = '', array $attrs = []): string
- {
- $fieldId = "roi_{$componentId}_{$name}";
-
- $html = '';
- $html .= sprintf('%s ', esc_attr($fieldId), esc_html($label));
-
- $attrString = $this->buildAttributesString($attrs);
-
- $html .= sprintf(
- '',
- esc_attr($fieldId),
- esc_attr($componentId),
- esc_attr($name),
- $attrString
- );
-
- foreach ($options as $optValue => $optLabel) {
- $selected = ($value === $optValue) ? 'selected' : '';
- $html .= sprintf(
- '%s ',
- esc_attr($optValue),
- $selected,
- esc_html($optLabel)
- );
- }
-
- $html .= ' ';
-
- if (!empty($description)) {
- $html .= sprintf('%s ', esc_html($description));
- }
- $html .= '
';
-
- return $html;
- }
-
- private function buildNumberField(string $name, string $label, $value, string $componentId, string $description = '', int $min = null, int $max = null, array $attrs = []): string
- {
- $fieldId = "roi_{$componentId}_{$name}";
-
- $html = '';
- $html .= sprintf('%s ', esc_attr($fieldId), esc_html($label));
-
- $attrs['type'] = 'number';
- if ($min !== null) {
- $attrs['min'] = $min;
- }
- if ($max !== null) {
- $attrs['max'] = $max;
- }
-
- $attrString = $this->buildAttributesString($attrs);
-
- $html .= sprintf(
- ' ',
- esc_attr($fieldId),
- esc_attr($componentId),
- esc_attr($name),
- esc_attr($value),
- $attrString
- );
-
- if (!empty($description)) {
- $html .= sprintf('%s ', esc_html($description));
- }
- $html .= '
';
-
- return $html;
- }
-
- private function buildUrlField(string $name, string $label, string $value, string $componentId, string $description = '', string $placeholder = '', array $attrs = []): string
- {
- $attrs['type'] = 'url';
- return $this->buildTextField($name, $label, $value, $componentId, $description, $placeholder, $attrs);
- }
-
- private function buildColorField(string $name, string $label, string $value, string $componentId, string $description = ''): string
- {
- $fieldId = "roi_{$componentId}_{$name}";
-
- $html = '';
-
- return $html;
- }
-
- private function buildMediaField(string $name, string $label, string $value, string $componentId, string $description = '', array $attrs = []): string
- {
- $fieldId = "roi_{$componentId}_{$name}";
-
- $html = '';
-
- return $html;
- }
-
- private function buildAttributesString(array $attrs): string
- {
- $attrString = '';
- foreach ($attrs as $key => $value) {
- $attrString .= sprintf(' %s="%s"', esc_attr($key), esc_attr($value));
- }
- return $attrString;
- }
-
- private function buildFormScripts(string $componentId): string
- {
- return <<
-SCRIPT;
- }
-
- public function supports(string $componentType): bool
- {
- return $componentType === 'top-notification-bar';
- }
-}