Migración completa a Clean Architecture con componentes funcionales
- Reorganización de estructura: Admin/, Public/, Shared/, Schemas/ - 12 componentes migrados: TopNotificationBar, Navbar, CtaLetsTalk, Hero, FeaturedImage, TableOfContents, CtaBoxSidebar, SocialShare, CtaPost, RelatedPost, ContactForm, Footer - Panel de administración con tabs Bootstrap 5 funcionales - Schemas JSON para configuración de componentes - Renderers dinámicos con CSSGeneratorService (cero CSS hardcodeado) - FormBuilders para UI admin con Design System consistente - Fix: Bootstrap JS cargado en header para tabs funcionales - Fix: buildTextInput maneja valores mixed (bool/string) - Eliminación de estructura legacy (src/, admin/, assets/css/componente-*) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
22
TemplateParts/content-cta-box.php
Normal file
22
TemplateParts/content-cta-box.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Part: CTA Box Sidebar
|
||||
*
|
||||
* Caja de llamada a la acción naranja en el sidebar
|
||||
* Abre el modal de contacto al hacer clic
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
?>
|
||||
|
||||
<!-- DEBUG: CTA Box Template Loaded -->
|
||||
<!-- CTA Box Sidebar -->
|
||||
<div class="cta-box-sidebar">
|
||||
<h5 class="cta-box-title">¿Listo para potenciar tus proyectos?</h5>
|
||||
<p class="cta-box-text">Accede a nuestra biblioteca completa de APUs y herramientas profesionales.</p>
|
||||
<button class="btn btn-cta-box w-100" data-bs-toggle="modal" data-bs-target="#contactModal">
|
||||
Solicitar Información
|
||||
</button>
|
||||
</div>
|
||||
<!-- DEBUG: CTA Box Template End -->
|
||||
29
TemplateParts/content-cta.php
Normal file
29
TemplateParts/content-cta.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Template part for displaying CTA with A/B testing
|
||||
*
|
||||
* Este template renderiza el Call-to-Action con A/B Testing.
|
||||
* La variante (A o B) se determina automáticamente por usuario.
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Solo mostrar en posts individuales
|
||||
if (!is_single()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Verificar si el CTA está habilitado
|
||||
$enable_cta = get_theme_mod('roi_enable_cta', true);
|
||||
if (!$enable_cta) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Usar la función del sistema de A/B testing
|
||||
roi_display_cta();
|
||||
61
TemplateParts/content-hero.php
Normal file
61
TemplateParts/content-hero.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* Hero Section Template
|
||||
*
|
||||
* Hero section con degradado azul para single posts
|
||||
*
|
||||
* @package ROI_Theme
|
||||
*/
|
||||
|
||||
if (!is_single()) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
|
||||
<section class="hero-section">
|
||||
<div class="container-fluid py-5">
|
||||
<div class="hero-content text-center">
|
||||
|
||||
<!-- Category Badges (ARRIBA del H1) -->
|
||||
<?php
|
||||
$categories = get_the_category();
|
||||
if (!empty($categories)) :
|
||||
?>
|
||||
<div class="hero-categories mb-3">
|
||||
<?php foreach ($categories as $category) : ?>
|
||||
<?php if ($category->name !== 'Uncategorized' && $category->name !== 'Sin categoría') : ?>
|
||||
<span class="hero-category-badge"><?php echo esc_html($category->name); ?></span>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- H1 Title -->
|
||||
<h1 class="hero-title"><?php the_title(); ?></h1>
|
||||
|
||||
<!-- Post Meta -->
|
||||
<div class="hero-meta">
|
||||
<span class="hero-meta-item">
|
||||
<i class="bi bi-calendar3 me-1"></i>
|
||||
<?php echo get_the_date(); ?>
|
||||
</span>
|
||||
<span class="hero-meta-separator">|</span>
|
||||
<span class="hero-meta-item">
|
||||
<i class="bi bi-person me-1"></i>
|
||||
<?php the_author(); ?>
|
||||
</span>
|
||||
<?php
|
||||
$reading_time = roi_get_reading_time();
|
||||
if ($reading_time) :
|
||||
?>
|
||||
<span class="hero-meta-separator">|</span>
|
||||
<span class="hero-meta-item">
|
||||
<i class="bi bi-clock me-1"></i>
|
||||
<?php echo esc_html($reading_time); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
56
TemplateParts/content-none.php
Normal file
56
TemplateParts/content-none.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Template part for displaying a message that posts cannot be found
|
||||
*
|
||||
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
?>
|
||||
|
||||
<section class="no-results not-found">
|
||||
|
||||
<header class="page-header">
|
||||
<h1 class="page-title"><?php esc_html_e( 'Nothing Found', 'roi-theme' ); ?></h1>
|
||||
</header><!-- .page-header -->
|
||||
|
||||
<div class="page-content">
|
||||
<?php
|
||||
if ( is_home() && current_user_can( 'publish_posts' ) ) :
|
||||
|
||||
// Message for users who can publish posts
|
||||
printf(
|
||||
'<p>' . wp_kses(
|
||||
/* translators: 1: link to WP admin new post page. */
|
||||
__( 'Ready to publish your first post? <a href="%1$s">Get started here</a>.', 'roi-theme' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
),
|
||||
)
|
||||
) . '</p>',
|
||||
esc_url( admin_url( 'post-new.php' ) )
|
||||
);
|
||||
|
||||
elseif ( is_search() ) :
|
||||
?>
|
||||
|
||||
<!-- Search returned no results -->
|
||||
<p><?php esc_html_e( 'Sorry, but nothing matched your search terms. Please try again with some different keywords.', 'roi-theme' ); ?></p>
|
||||
<?php
|
||||
get_search_form();
|
||||
|
||||
else :
|
||||
?>
|
||||
|
||||
<!-- Generic no content message -->
|
||||
<p><?php esc_html_e( 'It seems we can’t find what you’re looking for. Perhaps searching can help.', 'roi-theme' ); ?></p>
|
||||
<?php
|
||||
get_search_form();
|
||||
|
||||
endif;
|
||||
?>
|
||||
</div><!-- .page-content -->
|
||||
|
||||
</section><!-- .no-results -->
|
||||
42
TemplateParts/content-toc.php
Normal file
42
TemplateParts/content-toc.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Part: Table of Contents (TOC)
|
||||
*
|
||||
* Genera automáticamente TOC desde los H2 del post
|
||||
* HTML exacto del template original
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Solo mostrar TOC si estamos en single post
|
||||
if (!is_single()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Obtener el contenido del post actual
|
||||
global $post;
|
||||
$post_content = $post->post_content;
|
||||
|
||||
// Aplicar filtros de WordPress al contenido
|
||||
$post_content = apply_filters('the_content', $post_content);
|
||||
|
||||
// Buscar todos los H2 con ID en el contenido
|
||||
preg_match_all('/<h2[^>]*id=["\']([^"\']*)["\'][^>]*>(.*?)<\/h2>/i', $post_content, $matches);
|
||||
|
||||
// Si no hay H2 con ID, no mostrar TOC
|
||||
if (empty($matches[1])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generar el TOC con el HTML del template
|
||||
?>
|
||||
<div class="toc-container">
|
||||
<h4>Tabla de Contenido</h4>
|
||||
<ol class="list-unstyled toc-list">
|
||||
<?php foreach ($matches[1] as $index => $id) : ?>
|
||||
<?php $title = strip_tags($matches[2][$index]); ?>
|
||||
<li><a href="#<?php echo esc_attr($id); ?>"><?php echo esc_html($title); ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
</ol>
|
||||
</div>
|
||||
154
TemplateParts/content.php
Normal file
154
TemplateParts/content.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
* Template part for displaying posts
|
||||
*
|
||||
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
?>
|
||||
|
||||
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
|
||||
|
||||
<!-- Post Header -->
|
||||
<header class="entry-header">
|
||||
<?php
|
||||
// Display the post title
|
||||
if ( is_singular() ) :
|
||||
the_title( '<h1 class="entry-title">', '</h1>' );
|
||||
else :
|
||||
the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
|
||||
endif;
|
||||
|
||||
// Display post meta information
|
||||
if ( 'post' === get_post_type() ) :
|
||||
?>
|
||||
<div class="entry-meta">
|
||||
<?php
|
||||
// Posted on date
|
||||
printf(
|
||||
'<span class="posted-on"><time class="entry-date published" datetime="%1$s">%2$s</time></span>',
|
||||
esc_attr( get_the_date( 'c' ) ),
|
||||
esc_html( get_the_date() )
|
||||
);
|
||||
|
||||
// Posted by author
|
||||
printf(
|
||||
'<span class="byline"> %s <span class="author vcard"><a class="url fn n" href="%s">%s</a></span></span>',
|
||||
esc_html__( 'by', 'roi-theme' ),
|
||||
esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
|
||||
esc_html( get_the_author() )
|
||||
);
|
||||
|
||||
// Comments link
|
||||
if ( ! post_password_required() && ( comments_open() || get_comments_number() ) ) :
|
||||
echo '<span class="comments-link">';
|
||||
comments_popup_link(
|
||||
esc_html__( 'Leave a comment', 'roi-theme' ),
|
||||
esc_html__( '1 Comment', 'roi-theme' ),
|
||||
esc_html__( '% Comments', 'roi-theme' )
|
||||
);
|
||||
echo '</span>';
|
||||
endif;
|
||||
?>
|
||||
</div><!-- .entry-meta -->
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<!-- Featured Image -->
|
||||
<?php if ( has_post_thumbnail() ) : ?>
|
||||
<div class="post-thumbnail">
|
||||
<?php
|
||||
if ( is_singular() ) :
|
||||
the_post_thumbnail( 'roi-featured-large', array( 'alt' => the_title_attribute( array( 'echo' => false ) ) ) );
|
||||
else :
|
||||
?>
|
||||
<a href="<?php the_permalink(); ?>" aria-hidden="true" tabindex="-1">
|
||||
<?php the_post_thumbnail( 'roi-featured-medium', array( 'alt' => the_title_attribute( array( 'echo' => false ) ) ) ); ?>
|
||||
</a>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</div><!-- .post-thumbnail -->
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Post Content -->
|
||||
<div class="entry-content">
|
||||
<?php
|
||||
// Display content or excerpt based on context
|
||||
if ( is_singular() ) :
|
||||
the_content(
|
||||
sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s: Name of current post. Only visible to screen readers */
|
||||
__( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'roi-theme' ),
|
||||
array(
|
||||
'span' => array(
|
||||
'class' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
wp_kses_post( get_the_title() )
|
||||
)
|
||||
);
|
||||
|
||||
// Display pagination for multi-page posts
|
||||
wp_link_pages(
|
||||
array(
|
||||
'before' => '<div class="page-links">' . esc_html__( 'Pages:', 'roi-theme' ),
|
||||
'after' => '</div>',
|
||||
)
|
||||
);
|
||||
else :
|
||||
the_excerpt();
|
||||
endif;
|
||||
?>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<!-- Post Footer -->
|
||||
<footer class="entry-footer">
|
||||
<?php
|
||||
// Display categories
|
||||
$categories_list = get_the_category_list( esc_html__( ', ', 'roi-theme' ) );
|
||||
if ( $categories_list ) :
|
||||
printf(
|
||||
'<span class="cat-links">%s %s</span>',
|
||||
esc_html__( 'Categories:', 'roi-theme' ),
|
||||
$categories_list // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
endif;
|
||||
|
||||
// Display tags
|
||||
$tags_list = get_the_tag_list( '', esc_html_x( ', ', 'list item separator', 'roi-theme' ) );
|
||||
if ( $tags_list ) :
|
||||
printf(
|
||||
'<span class="tags-links">%s %s</span>',
|
||||
esc_html__( 'Tags:', 'roi-theme' ),
|
||||
$tags_list // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
endif;
|
||||
|
||||
// Edit post link
|
||||
edit_post_link(
|
||||
sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s: Name of current post. Only visible to screen readers */
|
||||
__( 'Edit <span class="screen-reader-text">%s</span>', 'roi-theme' ),
|
||||
array(
|
||||
'span' => array(
|
||||
'class' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
wp_kses_post( get_the_title() )
|
||||
),
|
||||
'<span class="edit-link">',
|
||||
'</span>'
|
||||
);
|
||||
?>
|
||||
</footer><!-- .entry-footer -->
|
||||
|
||||
</article><!-- #post-<?php the_ID(); ?> -->
|
||||
21
TemplateParts/cta-box-sidebar.php
Normal file
21
TemplateParts/cta-box-sidebar.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* CTA Box Sidebar Template
|
||||
*
|
||||
* Aparece debajo del TOC en single posts
|
||||
*
|
||||
* @package ROI_Theme
|
||||
*/
|
||||
|
||||
if (!is_single()) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="cta-box-sidebar mt-3">
|
||||
<h5 class="cta-box-title">¿Listo para potenciar tus proyectos?</h5>
|
||||
<p class="cta-box-text">Accede a nuestra biblioteca completa de APUs y herramientas profesionales.</p>
|
||||
<button class="btn btn-cta-box w-100" data-bs-toggle="modal" data-bs-target="#contactModal">
|
||||
<i class="bi bi-calendar-check me-2"></i>Solicitar Demo
|
||||
</button>
|
||||
</div>
|
||||
61
TemplateParts/modal-contact.php
Normal file
61
TemplateParts/modal-contact.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* Modal de Contacto - Bootstrap 5
|
||||
*
|
||||
* Modal activado por botón "Let's Talk" y CTA Box Sidebar
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
?>
|
||||
|
||||
<!-- Contact Modal -->
|
||||
<div class="modal fade" id="contactModal" tabindex="-1" aria-labelledby="contactModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="contactModalLabel">
|
||||
<i class="bi bi-envelope-fill me-2" style="color: #FF8600;"></i>
|
||||
<?php esc_html_e( '¿Tienes alguna pregunta?', 'roi-theme' ); ?>
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="<?php esc_attr_e( 'Cerrar', 'roi-theme' ); ?>"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="mb-4">
|
||||
<?php esc_html_e( 'Completa el formulario y nuestro equipo te responderá en menos de 24 horas.', 'roi-theme' ); ?>
|
||||
</p>
|
||||
|
||||
<form id="modalContactForm">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="modalFullName" class="form-label"><?php esc_html_e( 'Nombre completo', 'roi-theme' ); ?> *</label>
|
||||
<input type="text" class="form-control" id="modalFullName" name="fullName" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="modalCompany" class="form-label"><?php esc_html_e( 'Empresa', 'roi-theme' ); ?></label>
|
||||
<input type="text" class="form-control" id="modalCompany" name="company">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="modalWhatsapp" class="form-label"><?php esc_html_e( 'WhatsApp', 'roi-theme' ); ?> *</label>
|
||||
<input type="tel" class="form-control" id="modalWhatsapp" name="whatsapp" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="modalEmail" class="form-label"><?php esc_html_e( 'Correo electrónico', 'roi-theme' ); ?> *</label>
|
||||
<input type="email" class="form-control" id="modalEmail" name="email" required>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="modalComments" class="form-label"><?php esc_html_e( '¿En qué podemos ayudarte?', 'roi-theme' ); ?></label>
|
||||
<textarea class="form-control" id="modalComments" name="comments" rows="4"></textarea>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="bi bi-send-fill me-2"></i><?php esc_html_e( 'Enviar Mensaje', 'roi-theme' ); ?>
|
||||
</button>
|
||||
</div>
|
||||
<div id="modalFormMessage" class="col-12 mt-2 alert" style="display: none;"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
20
TemplateParts/top-notification-bar.php
Normal file
20
TemplateParts/top-notification-bar.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* Top Notification Bar Component
|
||||
*
|
||||
* Barra de notificaciones superior del sitio
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since 2.0.0
|
||||
*/
|
||||
?>
|
||||
|
||||
<div class="top-notification-bar">
|
||||
<div class="container">
|
||||
<div class="d-flex align-items-center justify-content-center">
|
||||
<i class="bi bi-megaphone-fill me-2"></i>
|
||||
<span><strong>Nuevo:</strong> Accede a más de 200,000 Análisis de Precios Unitarios actualizados para 2025.</span>
|
||||
<a href="#" class="ms-2 text-white text-decoration-underline">Ver Catálogo</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user