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:
FrankZamora
2025-11-25 21:20:06 -06:00
parent 90de6df77c
commit 0846a3bf03
224 changed files with 21670 additions and 17816 deletions

View File

@@ -0,0 +1,180 @@
<?php
declare(strict_types=1);
namespace ROITheme\Shared\Infrastructure\Services;
use ROITheme\Shared\Domain\Contracts\CSSGeneratorInterface;
/**
* Class CSSGeneratorService
*
* Implementación concreta del generador de CSS.
* Convierte arrays de configuración de estilos en reglas CSS válidas y formateadas.
*
* Responsabilidades:
* - Generar string CSS a partir de selector y estilos
* - Convertir propiedades snake_case → kebab-case
* - Normalizar nombres de propiedades (text_color → color)
* - Formatear reglas CSS con indentación legible
* - Sanitizar valores para prevenir inyección
*
* @package ROITheme\Shared\Infrastructure\Services
*/
final class CSSGeneratorService implements CSSGeneratorInterface
{
/**
* Mapa de nombres de propiedades CSS normalizadas.
*
* @var array<string, string>
*/
private const PROPERTY_MAP = [
'text-color' => 'color',
'bg-color' => 'background-color',
];
/**
* {@inheritDoc}
*/
public function generate(string $selector, array $styles): string
{
if (empty($styles)) {
return '';
}
// Filtrar valores vacíos o null
$styles = $this->filterEmptyValues($styles);
if (empty($styles)) {
return '';
}
// Convertir array de estilos a propiedades CSS
$cssProperties = $this->buildCSSProperties($styles);
// Formatear regla CSS completa
return $this->formatCSSRule($selector, $cssProperties);
}
/**
* Filtra valores vacíos, null o que solo contienen espacios en blanco.
*
* @param array<string, mixed> $styles Array de estilos
* @return array<string, string> Array filtrado
*/
private function filterEmptyValues(array $styles): array
{
return array_filter(
$styles,
fn($value) => $value !== null && $value !== '' && trim((string)$value) !== ''
);
}
/**
* Convierte array de estilos a propiedades CSS formateadas.
*
* @param array<string, string> $styles Array de estilos
* @return array<int, string> Array de propiedades CSS formateadas
*/
private function buildCSSProperties(array $styles): array
{
$properties = [];
foreach ($styles as $property => $value) {
// Convertir snake_case a kebab-case
$cssProperty = $this->convertToKebabCase($property);
// Normalizar nombre de propiedad
$cssProperty = $this->normalizePropertyName($cssProperty);
// Sanitizar valor
$sanitizedValue = $this->sanitizeValue((string)$value);
// Agregar propiedad formateada
$properties[] = sprintf('%s: %s;', $cssProperty, $sanitizedValue);
}
return $properties;
}
/**
* Convierte snake_case a kebab-case.
*
* Ejemplos:
* - background_color → background-color
* - font_size → font-size
* - padding_top → padding-top
*
* @param string $property Nombre de propiedad en snake_case
* @return string Nombre de propiedad en kebab-case
*/
private function convertToKebabCase(string $property): string
{
return str_replace('_', '-', strtolower($property));
}
/**
* Normaliza nombres de propiedades CSS a su forma estándar.
*
* Mapea alias comunes a nombres de propiedades CSS estándar:
* - text-color → color
* - bg-color → background-color
*
* @param string $property Nombre de propiedad
* @return string Nombre de propiedad normalizado
*/
private function normalizePropertyName(string $property): string
{
return self::PROPERTY_MAP[$property] ?? $property;
}
/**
* Sanitiza valores CSS para prevenir inyección de código.
*
* Remueve tags HTML y caracteres potencialmente peligrosos,
* manteniendo valores CSS válidos como colores, unidades, etc.
*
* @param string $value Valor CSS sin sanitizar
* @return string Valor CSS sanitizado
*/
private function sanitizeValue(string $value): string
{
// Remover tags HTML
$value = strip_tags($value);
// Remover caracteres de control excepto espacios
$value = preg_replace('/[^\P{C}\s]/u', '', $value);
// Trim espacios
$value = trim($value);
return $value;
}
/**
* Formatea la regla CSS completa con selector y propiedades.
*
* Genera CSS con formato legible:
* ```css
* .selector {
* property: value;
* property2: value2;
* }
* ```
*
* @param string $selector Selector CSS
* @param array<int, string> $properties Array de propiedades formateadas
* @return string Regla CSS completa
*/
private function formatCSSRule(string $selector, array $properties): string
{
if (empty($properties)) {
return '';
}
return sprintf(
"%s {\n %s\n}",
$selector,
implode("\n ", $properties)
);
}
}