Files
roi-theme/Admin/Infrastructure/Ui/AdminDashboardRenderer.php
FrankZamora 9cb0dd1491 feat(custom-css-manager): implementar TIPO 3 - CSS Crítico Personalizado
Nuevo sistema de gestión de CSS personalizado con panel admin:
- Admin/CustomCSSManager: CRUD de snippets CSS (crítico/diferido)
- Public/CustomCSSManager: Inyección dinámica en frontend
- Schema JSON para configuración del componente

Migración de CSS estático a BD:
- Tablas APU (~14KB) → snippet diferido en BD
- Tablas Genéricas (~10KB) → snippet diferido en BD
- Comentadas funciones legacy en enqueue-scripts.php

Limpieza de archivos obsoletos:
- Eliminado build-bootstrap-subset.js
- Eliminado migrate-legacy-options.php
- Eliminado minify-css.php
- Eliminado purgecss.config.js

Beneficios:
- CSS editable desde admin sin tocar código
- Soporte crítico (head) y diferido (footer)
- Filtrado por scope (all/home/single/archive)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:43:25 -06:00

216 lines
6.8 KiB
PHP

<?php
declare(strict_types=1);
namespace ROITheme\Admin\Infrastructure\Ui;
use ROITheme\Admin\Domain\Contracts\DashboardRendererInterface;
use ROITheme\Shared\Application\UseCases\GetComponentSettings\GetComponentSettingsUseCase;
/**
* Renderiza el dashboard del panel de administración
*
* Infrastructure - Implementación con WordPress
*/
final class AdminDashboardRenderer implements DashboardRendererInterface
{
private const SUPPORTED_VIEWS = ['dashboard'];
/**
* @param GetComponentSettingsUseCase|null $getComponentSettingsUseCase
* @param ComponentGroupRegistry|null $groupRegistry Registro de grupos de componentes
* @param array<string, mixed> $components Componentes disponibles
*/
public function __construct(
private readonly ?GetComponentSettingsUseCase $getComponentSettingsUseCase = null,
private readonly ?ComponentGroupRegistry $groupRegistry = null,
private readonly array $components = []
) {
}
public function render(): string
{
ob_start();
require __DIR__ . '/Views/dashboard.php';
return ob_get_clean();
}
public function supports(string $viewType): bool
{
return in_array($viewType, self::SUPPORTED_VIEWS, true);
}
/**
* Obtiene los componentes disponibles
*
* @return array<string, array<string, string>>
*/
public function getComponents(): array
{
return [
'top-notification-bar' => [
'id' => 'top-notification-bar',
'label' => 'TopBar',
'icon' => 'bi-megaphone-fill',
],
'navbar' => [
'id' => 'navbar',
'label' => 'Navbar',
'icon' => 'bi-list',
],
'cta-lets-talk' => [
'id' => 'cta-lets-talk',
'label' => "Let's Talk",
'icon' => 'bi-lightning-charge-fill',
],
'hero' => [
'id' => 'hero',
'label' => 'Hero Section',
'icon' => 'bi-image',
],
'featured-image' => [
'id' => 'featured-image',
'label' => 'Featured Image',
'icon' => 'bi-card-image',
],
'table-of-contents' => [
'id' => 'table-of-contents',
'label' => 'Table of Contents',
'icon' => 'bi-list-nested',
],
'cta-box-sidebar' => [
'id' => 'cta-box-sidebar',
'label' => 'CTA Sidebar',
'icon' => 'bi-megaphone',
],
'social-share' => [
'id' => 'social-share',
'label' => 'Social Share',
'icon' => 'bi-share',
],
'cta-post' => [
'id' => 'cta-post',
'label' => 'CTA Post',
'icon' => 'bi-megaphone-fill',
],
'related-post' => [
'id' => 'related-post',
'label' => 'Related Posts',
'icon' => 'bi-grid-3x3-gap',
],
'contact-form' => [
'id' => 'contact-form',
'label' => 'Contact Form',
'icon' => 'bi-envelope-paper',
],
'footer' => [
'id' => 'footer',
'label' => 'Footer',
'icon' => 'bi-layout-text-window-reverse',
],
'theme-settings' => [
'id' => 'theme-settings',
'label' => 'Theme Settings',
'icon' => 'bi-gear',
],
'adsense-placement' => [
'id' => 'adsense-placement',
'label' => 'AdSense',
'icon' => 'bi-megaphone',
],
'custom-css-manager' => [
'id' => 'custom-css-manager',
'label' => 'CSS Personalizado',
'icon' => 'bi-file-earmark-code',
],
];
}
/**
* Obtiene las configuraciones de un componente
*
* @param string $componentName Nombre del componente
* @return array<string, array<string, mixed>> Configuraciones agrupadas por grupo
*/
public function getComponentSettings(string $componentName): array
{
if ($this->getComponentSettingsUseCase === null) {
return [];
}
return $this->getComponentSettingsUseCase->execute($componentName);
}
/**
* Obtiene el valor de un campo de configuración
*
* @param string $componentName Nombre del componente
* @param string $groupName Nombre del grupo
* @param string $attributeName Nombre del atributo
* @param mixed $default Valor por defecto si no existe
* @return mixed
*/
public function getFieldValue(string $componentName, string $groupName, string $attributeName, mixed $default = null): mixed
{
$settings = $this->getComponentSettings($componentName);
return $settings[$groupName][$attributeName] ?? $default;
}
/**
* Obtiene la clase del FormBuilder para un componente
*
* @param string $componentId ID del componente en kebab-case (ej: 'top-notification-bar')
* @return string Namespace completo del FormBuilder
*/
public function getFormBuilderClass(string $componentId): string
{
// Mapeo especial para componentes con acrónimos (CSS, API, etc.)
$specialMappings = [
'custom-css-manager' => 'CustomCSSManager',
];
// Usar mapeo especial si existe, sino convertir kebab-case a PascalCase
if (isset($specialMappings[$componentId])) {
$className = $specialMappings[$componentId];
} else {
// 'top-notification-bar' → 'TopNotificationBar'
$className = str_replace('-', '', ucwords($componentId, '-'));
}
// Construir namespace completo
// ROITheme\Admin\TopNotificationBar\Infrastructure\Ui\TopNotificationBarFormBuilder
return "ROITheme\\Admin\\{$className}\\Infrastructure\\Ui\\{$className}FormBuilder";
}
/**
* Obtiene los grupos de componentes
*
* @return array<string, array<string, mixed>>
*/
public function getComponentGroups(): array
{
if ($this->groupRegistry === null) {
return [];
}
return $this->groupRegistry->getGroups();
}
/**
* Obtiene el grupo al que pertenece un componente
*
* @param string $componentId ID del componente
* @return string|null ID del grupo o null
*/
public function getGroupForComponent(string $componentId): ?string
{
if ($this->groupRegistry === null) {
return null;
}
return $this->groupRegistry->getGroupForComponent($componentId);
}
}