diff --git a/Inc/apu-tables.php b/Inc/apu-tables.php
index f26b5d7e..f8faf5df 100644
--- a/Inc/apu-tables.php
+++ b/Inc/apu-tables.php
@@ -18,6 +18,10 @@ if (!defined('ABSPATH')) {
* Detecta tablas con el atributo data-apu y las envuelve
* con la clase .analisis para aplicar estilos específicos.
*
+ * TAMBIÉN agrega clases a filas especiales (section-header, subtotal-row, total-row)
+ * del lado del servidor para PREVENIR CLS. Anteriormente esto se hacía con JS
+ * en apu-tables-auto-class.js, lo cual causaba CLS de 0.692 en Lab Data.
+ *
* @param string $content El contenido del post
* @return string El contenido procesado
*/
@@ -43,10 +47,116 @@ function roi_process_apu_tables($content) {
return '
' . $table . '
';
}, $content);
+ // Procesar TODAS las tablas APU (dentro de .analisis o .desglose) para agregar clases a filas
+ // Esto reemplaza la lógica de apu-tables-auto-class.js para prevenir CLS
+ $content = roi_add_apu_row_classes($content);
+
return $content;
}
add_filter('the_content', 'roi_process_apu_tables', 20);
+/**
+ * Agrega clases CSS a filas especiales de tablas APU del lado del servidor
+ *
+ * IMPORTANTE: Esta función reemplaza apu-tables-auto-class.js para PREVENIR CLS.
+ * La manipulación del DOM con JS después de DOMContentLoaded causa Layout Shift.
+ *
+ * Detecta y clasifica:
+ * - section-header: Material, Mano de Obra, Herramienta, Equipo
+ * - subtotal-row: Filas que empiezan con "Suma de" o "Subtotal"
+ * - total-row: Costo Directo, Total
+ *
+ * @param string $content HTML del contenido
+ * @return string HTML con clases agregadas a filas
+ */
+function roi_add_apu_row_classes($content) {
+ // Solo procesar si hay tablas APU
+ if (strpos($content, 'class="analisis"') === false && strpos($content, 'class="desglose"') === false) {
+ return $content;
+ }
+
+ // Patrones para detectar tipos de fila (basado en contenido de segunda celda)
+ $section_headers = [
+ 'Material',
+ 'Mano de Obra',
+ 'Herramienta',
+ 'Equipo',
+ 'MATERIAL',
+ 'MANO DE OBRA',
+ 'HERRAMIENTA',
+ 'EQUIPO',
+ ];
+
+ $total_patterns = [
+ 'Costo Directo',
+ 'COSTO DIRECTO',
+ 'Costo directo',
+ 'Total',
+ 'TOTAL',
+ ];
+
+ // Procesar filas dentro de
+ // Patrón: capturar con sus atributos y contenido
+ $content = preg_replace_callback(
+ '/
]*)>(.*?)<\/tr>/is',
+ function($matches) use ($section_headers, $total_patterns) {
+ $tr_attrs = $matches[1];
+ $tr_content = $matches[2];
+
+ // Si ya tiene clase especial, no modificar
+ if (preg_match('/class\s*=\s*["\'][^"\']*(?:section-header|subtotal-row|total-row)/', $tr_attrs)) {
+ return $matches[0];
+ }
+
+ // Extraer texto de la segunda celda
+ if (!preg_match('/| ]*>.*?<\/td>\s* | ]*>(.*?)<\/td>/is', $tr_content, $cell_match)) {
+ return $matches[0]; // No hay segunda celda
+ }
+
+ $cell_text = trim(strip_tags($cell_match[1]));
+
+ // Determinar clase a agregar
+ $class_to_add = '';
+
+ // Verificar section-header
+ if (in_array($cell_text, $section_headers, true)) {
+ $class_to_add = 'section-header';
+ }
+ // Verificar subtotal-row
+ elseif (stripos($cell_text, 'suma de ') === 0 || stripos($cell_text, 'subtotal ') === 0) {
+ $class_to_add = 'subtotal-row';
+ }
+ // Verificar total-row
+ elseif (in_array($cell_text, $total_patterns, true)) {
+ $class_to_add = 'total-row';
+ }
+
+ // Si no hay clase que agregar, retornar sin modificar
+ if (empty($class_to_add)) {
+ return $matches[0];
+ }
+
+ // Agregar clase al |
+ if (preg_match('/class\s*=\s*["\']([^"\']*)["\']/', $tr_attrs)) {
+ // Ya tiene atributo class, agregar a las clases existentes
+ $tr_attrs = preg_replace(
+ '/class\s*=\s*["\']([^"\']*)["\']/',
+ 'class="$1 ' . $class_to_add . '"',
+ $tr_attrs
+ );
+ } else {
+ // No tiene class, agregar el atributo
+ $tr_attrs = ' class="' . $class_to_add . '"' . $tr_attrs;
+ }
+
+ return '
' . $tr_content . '
';
+ },
+ $content
+ );
+
+ return $content;
+}
+
/**
* Shortcode: [apu_table]
* Permite envolver tablas manualmente con la clase .analisis
diff --git a/Inc/enqueue-scripts.php b/Inc/enqueue-scripts.php
index ca1cfaed..e413c78c 100644
--- a/Inc/enqueue-scripts.php
+++ b/Inc/enqueue-scripts.php
@@ -578,26 +578,29 @@ add_action('wp_enqueue_scripts', 'roi_enqueue_apu_tables_styles', 5); // Priorit
/**
* Enqueue APU Tables auto-class JavaScript
*
- * Este script detecta automáticamente filas especiales en tablas .desglose y .analisis
- * y les agrega las clases CSS correspondientes (section-header, subtotal-row, total-row)
+ * DESHABILITADO: Este script causaba CLS de 0.692 en Lab Data.
+ * La manipulación del DOM con JS después de DOMContentLoaded causa Layout Shift.
*
- * Issue #132
+ * La lógica ha sido movida a PHP server-side en Inc/apu-tables.php
+ * @see roi_add_apu_row_classes()
+ *
+ * Issue #132 - Original implementation
*/
-function roi_enqueue_apu_tables_autoclass_script() {
- // APU Tables Auto-Class JS
- wp_enqueue_script(
- 'roi-apu-tables-autoclass',
- get_template_directory_uri() . '/Assets/js/apu-tables-auto-class.js',
- array(),
- ROI_VERSION,
- array(
- 'in_footer' => true,
- 'strategy' => 'defer',
- )
- );
-}
-
-add_action('wp_enqueue_scripts', 'roi_enqueue_apu_tables_autoclass_script', 15);
+// function roi_enqueue_apu_tables_autoclass_script() {
+// // APU Tables Auto-Class JS
+// wp_enqueue_script(
+// 'roi-apu-tables-autoclass',
+// get_template_directory_uri() . '/Assets/js/apu-tables-auto-class.js',
+// array(),
+// ROI_VERSION,
+// array(
+// 'in_footer' => true,
+// 'strategy' => 'defer',
+// )
+// );
+// }
+// add_action('wp_enqueue_scripts', 'roi_enqueue_apu_tables_autoclass_script', 15);
+// DISABLED: Moved to server-side PHP to prevent CLS
/**
* Enqueue CTA Box Sidebar styles (Issue #36)