diff --git a/Admin/Infrastructure/Ui/AdminDashboardRenderer.php b/Admin/Infrastructure/Ui/AdminDashboardRenderer.php index 905f5725..67391190 100644 --- a/Admin/Infrastructure/Ui/AdminDashboardRenderer.php +++ b/Admin/Infrastructure/Ui/AdminDashboardRenderer.php @@ -18,10 +18,12 @@ final class AdminDashboardRenderer implements DashboardRendererInterface /** * @param GetComponentSettingsUseCase|null $getComponentSettingsUseCase + * @param ComponentGroupRegistry|null $groupRegistry Registro de grupos de componentes * @param array $components Componentes disponibles */ public function __construct( private readonly ?GetComponentSettingsUseCase $getComponentSettingsUseCase = null, + private readonly ?ComponentGroupRegistry $groupRegistry = null, private readonly array $components = [] ) { } @@ -167,4 +169,33 @@ final class AdminDashboardRenderer implements DashboardRendererInterface return "ROITheme\\Admin\\{$className}\\Infrastructure\\Ui\\{$className}FormBuilder"; } + /** + * Obtiene los grupos de componentes + * + * @return array> + */ + 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); + } + } diff --git a/Admin/Infrastructure/Ui/Assets/Css/admin-dashboard.css b/Admin/Infrastructure/Ui/Assets/Css/admin-dashboard.css index d30f2791..ec0198d3 100644 --- a/Admin/Infrastructure/Ui/Assets/Css/admin-dashboard.css +++ b/Admin/Infrastructure/Ui/Assets/Css/admin-dashboard.css @@ -1,17 +1,53 @@ /** - * Estilos para el Dashboard del Panel de Administración ROI Theme - * Siguiendo especificaciones del Design System + * ROI Theme Admin Dashboard - Improved Version + * Basado en improved-panel.css del Design System */ +/* ================================================ + CSS VARIABLES - DESIGN SYSTEM + ================================================ */ + +:root { + /* Navy Colors */ + --roi-navy-dark: #0E2337; + --roi-navy-primary: #1e3a5f; + --roi-navy-light: #2c5282; + + /* Orange Colors */ + --roi-orange-primary: #FF8600; + --roi-orange-hover: #FF6B35; + --roi-orange-light: #FFB800; + + /* Neutral Colors */ + --roi-neutral-50: #f8f9fa; + --roi-neutral-100: #e9ecef; + --roi-neutral-600: #495057; + --roi-neutral-700: #6c757d; + + /* Shadows */ + --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12); + --shadow-xl: 0 12px 32px rgba(0, 0, 0, 0.15); + --shadow-orange: 0 6px 20px rgba(255, 134, 0, 0.15); + + /* Transitions */ + --transition-base: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); + --transition-fast: all 0.15s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* ================================================ + WORDPRESS ADMIN OVERRIDES + ================================================ */ + /* Sobrescribir max-width de .card de WordPress */ .wrap.roi-admin-panel .card { max-width: none !important; } -/* Fix para switches de Bootstrap - resetear completamente estilos de WordPress */ +/* Fix para switches de Bootstrap */ .wrap.roi-admin-panel .form-switch .form-check-input { all: unset !important; - /* Restaurar estilos necesarios de Bootstrap */ width: 2em !important; height: 1em !important; margin-left: -2.5em !important; @@ -49,7 +85,6 @@ box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25) !important; } -/* Alinear verticalmente los labels con los switches */ .wrap.roi-admin-panel .form-check { display: flex !important; align-items: center !important; @@ -62,7 +97,10 @@ padding-top: 0 !important; } -/* Tabs Navigation */ +/* ================================================ + TABS NAVIGATION (Legacy) + ================================================ */ + .nav-tabs-admin { border-bottom: 2px solid #e9ecef; } @@ -113,8 +151,394 @@ } } -/* Responsive */ +/* ================================================ + HEADER MEJORADO + ================================================ */ + +.roi-home-header { + position: relative; + padding: 2.5rem 2rem; + background: var(--roi-navy-dark); + border-radius: 12px; + overflow: hidden; + box-shadow: var(--shadow-lg); + border: none; + margin-bottom: 2.5rem; +} + +/* Patrón de fondo sutil */ +.roi-home-header__pattern { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + radial-gradient(circle at 20% 50%, rgba(255, 134, 0, 0.05) 0%, transparent 50%), + radial-gradient(circle at 80% 50%, rgba(44, 82, 130, 0.08) 0%, transparent 50%); + pointer-events: none; +} + +.roi-home-header__content { + position: relative; + z-index: 1; + display: flex; + align-items: center; + gap: 1.5rem; +} + +.roi-home-header__icon-wrapper { + width: 64px; + height: 64px; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, var(--roi-orange-primary), var(--roi-orange-light)); + border-radius: 16px; + flex-shrink: 0; + box-shadow: 0 4px 16px rgba(255, 134, 0, 0.3); +} + +.roi-home-header__icon { + font-size: 2rem; + color: white; +} + +.roi-home-header__text { + flex: 1; +} + +.roi-home-header__title { + font-size: 1.8rem; + font-weight: 700; + color: white; + margin: 0 0 0.5rem 0; + line-height: 1.2; +} + +.roi-home-header__subtitle { + font-size: 0.95rem; + color: rgba(255, 255, 255, 0.9); + margin: 0; + font-weight: 400; +} + +/* ================================================ + GRID DE GRUPOS + ================================================ */ + +.roi-groups-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 2rem; +} + @media (max-width: 991px) { + .roi-groups-grid { + grid-template-columns: 1fr; + gap: 1.5rem; + } +} + +/* ================================================ + GROUP CARDS MEJORADOS + ================================================ */ + +.roi-group-card { + position: relative; + background: white; + border-radius: 12px; + padding: 2rem; + box-shadow: var(--shadow-sm); + border: 1px solid var(--roi-neutral-100); + transition: var(--transition-base); + animation: fadeInUp 0.4s cubic-bezier(0.4, 0, 0.2, 1) both; +} + +/* Efecto glow en hover */ +.roi-group-card__glow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 12px; + opacity: 0; + transition: opacity 0.3s ease; + pointer-events: none; + box-shadow: 0 0 0 1px var(--roi-orange-primary); +} + +.roi-group-card:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-lg); + border-color: var(--roi-orange-primary); +} + +.roi-group-card:hover .roi-group-card__glow { + opacity: 0.3; +} + +/* Header del grupo */ +.roi-group-card__header { + display: flex; + align-items: flex-start; + gap: 1rem; + margin-bottom: 1.5rem; + padding-bottom: 1.25rem; + border-bottom: 2px solid var(--roi-neutral-50); +} + +.roi-group-card__icon-wrapper { + width: 50px; + height: 50px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 134, 0, 0.1); + border-radius: 12px; + flex-shrink: 0; + transition: var(--transition-base); +} + +.roi-group-card:hover .roi-group-card__icon-wrapper { + background: rgba(255, 134, 0, 0.15); + transform: scale(1.05); +} + +.roi-group-card__icon { + font-size: 1.75rem; + color: var(--roi-orange-primary); +} + +.roi-group-card__header-text { + flex: 1; +} + +.roi-group-card__title { + font-size: 1.25rem; + font-weight: 600; + margin: 0 0 0.35rem 0; + color: var(--roi-navy-primary); + line-height: 1.3; +} + +.roi-group-card__description { + font-size: 0.9rem; + margin: 0; + color: var(--roi-neutral-700); + line-height: 1.5; +} + +/* ================================================ + COMPONENTS GRID + ================================================ */ + +.roi-components-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 1rem; +} + +/* ================================================ + MINI CARDS MEJORADOS + ================================================ */ + +.roi-component-minicard { + position: relative; + background: white; + border: 1px solid var(--roi-neutral-100); + border-radius: 10px; + padding: 1.25rem 1rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.75rem; + cursor: pointer; + transition: var(--transition-base); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); + text-align: center; + overflow: hidden; +} + +.roi-component-minicard::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient(90deg, var(--roi-orange-primary), var(--roi-orange-light)); + transform: scaleX(0); + transform-origin: left; + transition: transform 0.3s ease; +} + +.roi-component-minicard:hover::before { + transform: scaleX(1); +} + +.roi-component-minicard:hover { + transform: translateY(-3px) scale(1.02); + box-shadow: var(--shadow-orange); + border-color: var(--roi-orange-primary); +} + +.roi-component-minicard:active { + transform: translateY(-2px) scale(0.98); +} + +.roi-component-minicard:focus { + outline: 2px solid var(--roi-orange-primary); + outline-offset: 3px; +} + +/* Icono del mini card */ +.roi-component-minicard__icon-bg { + width: 46px; + height: 46px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 134, 0, 0.08); + border-radius: 10px; + transition: var(--transition-base); +} + +.roi-component-minicard:hover .roi-component-minicard__icon-bg { + background: rgba(255, 134, 0, 0.15); + transform: scale(1.1) rotate(5deg); +} + +.roi-component-minicard__icon { + font-size: 1.5rem; + color: var(--roi-orange-primary); +} + +.roi-component-minicard__label { + font-size: 0.85rem; + font-weight: 600; + color: var(--roi-navy-dark); + line-height: 1.3; + letter-spacing: -0.01em; +} + +.roi-component-minicard:hover .roi-component-minicard__label { + color: var(--roi-orange-primary); +} + +/* ================================================ + BREADCRUMB + ================================================ */ + +.roi-breadcrumb { + padding: 1rem 1.5rem; + background: var(--roi-neutral-50); + border-radius: 8px; + border-left: 4px solid var(--roi-orange-primary); + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; + margin-bottom: 1.5rem; +} + +.roi-breadcrumb__nav { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.roi-breadcrumb__separator { + color: var(--roi-neutral-700); +} + +.roi-breadcrumb__group { + color: var(--roi-neutral-700); + font-size: 0.9rem; +} + +.roi-breadcrumb__current { + font-size: 0.9rem; + font-weight: 600; + color: var(--roi-navy-primary); +} + +/* Botón Volver */ +.roi-back-to-home { + border-color: var(--roi-navy-primary); + color: var(--roi-navy-primary); +} + +.roi-back-to-home:hover { + background-color: var(--roi-navy-primary); + border-color: var(--roi-navy-primary); + color: white; +} + +/* ================================================ + COMPONENT FORM CONTAINER + ================================================ */ + +.roi-component-form-container { + animation: fadeIn 0.3s ease-in; +} + +/* ================================================ + ANIMATIONS + ================================================ */ + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +#roi-home-view, +#roi-component-view { + animation: fadeIn 0.4s ease-in; +} + +/* ================================================ + UTILITY CLASSES + ================================================ */ + +.roi-hidden { + display: none !important; +} + +/* ================================================ + RESPONSIVE + ================================================ */ + +@media (max-width: 991px) { + .roi-group-card { + padding: 1.5rem; + } + + .roi-home-header { + padding: 2rem 1.5rem; + } + + .roi-home-header__icon-wrapper { + width: 56px; + height: 56px; + } + + .roi-home-header__icon { + font-size: 1.75rem; + } + + .roi-home-header__title { + font-size: 1.5rem; + } + .nav-tabs-admin { flex-wrap: wrap; } @@ -125,7 +549,40 @@ } } -@media (max-width: 767px) { +@media (max-width: 768px) { + .roi-home-header__content { + flex-direction: column; + text-align: center; + gap: 1rem; + } + + .roi-components-grid { + grid-template-columns: repeat(2, 1fr); + gap: 0.75rem; + } + + .roi-component-minicard { + padding: 1rem 0.75rem; + } + + .roi-component-minicard__icon-bg { + width: 40px; + height: 40px; + } + + .roi-component-minicard__icon { + font-size: 1.25rem; + } + + .roi-group-card__icon-wrapper { + width: 44px; + height: 44px; + } + + .roi-group-card__icon { + font-size: 1.5rem; + } + .nav-tabs-admin { overflow-x: auto; flex-wrap: nowrap; @@ -135,3 +592,39 @@ white-space: nowrap; } } + +@media (max-width: 576px) { + .roi-groups-grid { + gap: 1rem; + } + + .roi-home-header { + padding: 1.5rem 1rem; + } + + .roi-home-header__title { + font-size: 1.3rem; + } + + .roi-home-header__subtitle { + font-size: 0.875rem; + } + + .roi-group-card { + padding: 1.25rem; + } + + .roi-group-card__title { + font-size: 1.1rem; + } + + .roi-components-grid { + grid-template-columns: 1fr; + } + + .roi-component-minicard { + flex-direction: row; + text-align: left; + padding: 1rem; + } +} diff --git a/Admin/Infrastructure/Ui/Assets/Js/admin-dashboard.js b/Admin/Infrastructure/Ui/Assets/Js/admin-dashboard.js index 2cbf34f6..8c175645 100644 --- a/Admin/Infrastructure/Ui/Assets/Js/admin-dashboard.js +++ b/Admin/Infrastructure/Ui/Assets/Js/admin-dashboard.js @@ -10,12 +10,73 @@ * Inicializa el dashboard cuando el DOM está listo */ document.addEventListener('DOMContentLoaded', function() { - initializeTabs(); + // Nueva navegación por Cards/Grupos + initializeCardNavigation(); + + // Funcionalidad existente (solo si hay tabs visibles) + if (document.querySelector('.nav-tabs-admin')) { + initializeTabs(); + } + initializeFormValidation(); initializeButtons(); initializeColorPickers(); }); + /** + * Inicializa la navegación por Cards/Grupos (App-Style) + */ + function initializeCardNavigation() { + // Verificar que estamos en el panel correcto + const adminPanel = document.querySelector('.roi-admin-panel'); + if (!adminPanel) { + return; + } + + // Delegación de eventos para mini-cards + document.addEventListener('click', function(e) { + const minicard = e.target.closest('.roi-component-minicard'); + if (minicard) { + e.preventDefault(); + const componentId = minicard.getAttribute('data-component-id'); + if (componentId) { + navigateToComponent(componentId); + } + } + }); + + // Botón volver al home + document.addEventListener('click', function(e) { + if (e.target.closest('.roi-back-to-home')) { + e.preventDefault(); + navigateToHome(); + } + }); + } + + /** + * Navega a un componente específico + * + * @param {string} componentId ID del componente en kebab-case + */ + function navigateToComponent(componentId) { + const url = new URL(window.location.href); + url.searchParams.set('component', componentId); + // Eliminar el parámetro admin-tab si existe (legacy) + url.searchParams.delete('admin-tab'); + window.location.href = url.toString(); + } + + /** + * Navega de vuelta al home (vista de grupos) + */ + function navigateToHome() { + const url = new URL(window.location.href); + url.searchParams.delete('component'); + url.searchParams.delete('admin-tab'); + window.location.href = url.toString(); + } + /** * Inicializa el sistema de tabs con persistencia en URL */ diff --git a/Admin/Infrastructure/Ui/ComponentGroupRegistry.php b/Admin/Infrastructure/Ui/ComponentGroupRegistry.php new file mode 100644 index 00000000..a6da61f3 --- /dev/null +++ b/Admin/Infrastructure/Ui/ComponentGroupRegistry.php @@ -0,0 +1,104 @@ +> + */ + public function getGroups(): array + { + // Design System: Todos los grupos usan el mismo gradiente Navy (#0E2337 → #1e3a5f) + // No se requiere propiedad 'color' ya que está definido en CSS + $defaultGroups = [ + 'header-navigation' => [ + 'label' => __('Header & Navegación', 'roi-theme'), + 'icon' => 'bi-layout-text-window', + 'description' => __('Barras superiores, menú y pie de página', 'roi-theme'), + 'components' => ['top-notification-bar', 'navbar', 'footer'] + ], + 'main-content' => [ + 'label' => __('Contenido Principal', 'roi-theme'), + 'icon' => 'bi-file-richtext', + 'description' => __('Secciones principales de páginas y posts', 'roi-theme'), + 'components' => ['hero', 'featured-image', 'table-of-contents', 'related-post'] + ], + 'ctas-conversion' => [ + 'label' => __('CTAs & Conversión', 'roi-theme'), + 'icon' => 'bi-lightning-charge', + 'description' => __('Llamadas a la acción y elementos de conversión', 'roi-theme'), + 'components' => ['cta-lets-talk', 'cta-box-sidebar', 'cta-post'] + ], + 'engagement' => [ + 'label' => __('Engagement', 'roi-theme'), + 'icon' => 'bi-share', + 'description' => __('Interacción social y compartir', 'roi-theme'), + 'components' => ['social-share'] + ], + 'forms' => [ + 'label' => __('Formularios', 'roi-theme'), + 'icon' => 'bi-envelope-paper', + 'description' => __('Formularios de contacto y captura', 'roi-theme'), + 'components' => ['contact-form'] + ], + 'settings' => [ + 'label' => __('Configuración', 'roi-theme'), + 'icon' => 'bi-gear', + 'description' => __('Ajustes globales del tema y monetización', 'roi-theme'), + 'components' => ['theme-settings', 'adsense-placement'] + ], + ]; + + /** + * Filtro para extender o modificar los grupos de componentes + * + * @param array> $groups Grupos por defecto + * @return array> Grupos modificados + */ + return apply_filters('roi_theme_component_groups', $defaultGroups); + } + + /** + * Obtiene el grupo al que pertenece un componente + * + * @param string $componentId ID del componente en kebab-case + * @return string|null ID del grupo o null si no pertenece a ninguno + */ + public function getGroupForComponent(string $componentId): ?string + { + foreach ($this->getGroups() as $groupId => $group) { + if (in_array($componentId, $group['components'], true)) { + return $groupId; + } + } + return null; + } + + /** + * Obtiene la información de un grupo específico + * + * @param string $groupId ID del grupo + * @return array|null Datos del grupo o null + */ + public function getGroup(string $groupId): ?array + { + $groups = $this->getGroups(); + return $groups[$groupId] ?? null; + } +} diff --git a/Admin/Infrastructure/Ui/Views/dashboard.php b/Admin/Infrastructure/Ui/Views/dashboard.php index 64608928..fab45cea 100644 --- a/Admin/Infrastructure/Ui/Views/dashboard.php +++ b/Admin/Infrastructure/Ui/Views/dashboard.php @@ -2,6 +2,8 @@ /** * ROI Theme - Panel de Administración Principal * + * Nueva UI con sistema de Cards/Grupos (App-Style Navigation) + * * @var AdminDashboardRenderer $this */ @@ -13,76 +15,34 @@ if (!defined('ABSPATH')) { } $components = $this->getComponents(); +$groups = $this->getComponentGroups(); -// Determinar tab activo: desde URL o primer componente -$activeComponentId = array_key_first($components); - -// Leer parametro admin-tab de la URL con sanitizacion -// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Solo lectura de parametro para UI -if (isset($_GET['admin-tab'])) { - $requestedTab = sanitize_text_field(wp_unslash($_GET['admin-tab'])); +// ===================================================== +// SANITIZACIÓN OBLIGATORIA según estándares WordPress +// ===================================================== +// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Solo lectura de parámetro para UI +$activeComponent = null; +if (isset($_GET['component'])) { + $requestedComponent = sanitize_text_field(wp_unslash($_GET['component'])); // Validar que el componente exista - if (array_key_exists($requestedTab, $components)) { - $activeComponentId = $requestedTab; + if (array_key_exists($requestedComponent, $components)) { + $activeComponent = $requestedComponent; } } ?>
- - - -
- $component): - $isActive = ($componentId === $activeComponentId); - $componentSettings = $this->getComponentSettings($componentId); - ?> - -
- - getFormBuilderClass($componentId); - if (class_exists($formBuilderClass)) { - $formBuilder = new $formBuilderClass($this); - echo $formBuilder->buildForm($componentId); - } else { - echo '

FormBuilder no encontrado: ' . esc_html($formBuilderClass) . '

'; - } - ?> - -
- -
- - -
- - -
+ + + + + + +
diff --git a/Admin/Infrastructure/Ui/Views/partials/breadcrumb.php b/Admin/Infrastructure/Ui/Views/partials/breadcrumb.php new file mode 100644 index 00000000..b288c300 --- /dev/null +++ b/Admin/Infrastructure/Ui/Views/partials/breadcrumb.php @@ -0,0 +1,48 @@ +> $groups Grupos de componentes + * @var array> $components Componentes disponibles + * @var array|null $group Grupo del componente activo + * @var array|null $component Datos del componente activo + */ + +declare(strict_types=1); + +if (!defined('ABSPATH')) { + exit; +} +?> + + diff --git a/Admin/Infrastructure/Ui/Views/partials/component-view.php b/Admin/Infrastructure/Ui/Views/partials/component-view.php new file mode 100644 index 00000000..e6fb0493 --- /dev/null +++ b/Admin/Infrastructure/Ui/Views/partials/component-view.php @@ -0,0 +1,74 @@ +> $groups Grupos de componentes + * @var array> $components Componentes disponibles + */ + +declare(strict_types=1); + +if (!defined('ABSPATH')) { + exit; +} + +$component = $components[$activeComponent] ?? null; +$groupId = $this->getGroupForComponent($activeComponent); +$group = $groupId && isset($groups[$groupId]) ? $groups[$groupId] : null; +?> + +
+ + + + + +
+
+ +
+ getFormBuilderClass($activeComponent); + if (class_exists($formBuilderClass)) { + $formBuilder = new $formBuilderClass($this); + echo $formBuilder->buildForm($activeComponent); + } else { + ?> +
+ + +
+ +
+ +
+
+ + +
+ + +
+
diff --git a/Admin/Infrastructure/Ui/Views/partials/groups-home.php b/Admin/Infrastructure/Ui/Views/partials/groups-home.php new file mode 100644 index 00000000..d4e03a60 --- /dev/null +++ b/Admin/Infrastructure/Ui/Views/partials/groups-home.php @@ -0,0 +1,92 @@ +> $groups Grupos de componentes + * @var array> $components Componentes disponibles + */ + +declare(strict_types=1); + +if (!defined('ABSPATH')) { + exit; +} +?> + +
+ +
+
+
+
+ +
+
+

+ +

+

+ +

+
+
+
+ + +
+ $group): + ?> +
+
+ +
+
+ +
+
+

+ +

+

+ +

+
+
+ +
+ + + + + + +
+ +
+ +
+
diff --git a/functions.php b/functions.php index c8a0fb28..550a7f6f 100644 --- a/functions.php +++ b/functions.php @@ -111,8 +111,11 @@ try { position: 60 ); - // Crear renderer del dashboard con inyección del Use Case - $dashboardRenderer = new AdminDashboardRenderer($getComponentSettingsUseCase); + // Crear GroupRegistry para la nueva UI de Cards/Grupos + $groupRegistry = new \ROITheme\Admin\Infrastructure\Ui\ComponentGroupRegistry(); + + // Crear renderer del dashboard con inyección del Use Case y GroupRegistry + $dashboardRenderer = new AdminDashboardRenderer($getComponentSettingsUseCase, $groupRegistry); // Crear caso de uso para renderizar $renderDashboardUseCase = new RenderDashboardUseCase($dashboardRenderer);