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>
This commit is contained in:
FrankZamora
2025-12-01 15:43:25 -06:00
parent 423aae062c
commit 9cb0dd1491
24 changed files with 1553 additions and 784 deletions

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace ROITheme\Admin\CustomCSSManager\Application\DTOs;
/**
* DTO para solicitud de guardado de snippet
*
* Inmutable - una vez creado no puede modificarse.
* Transporta datos desde Infrastructure (form) hacia Application (use case).
*/
final class SaveSnippetRequest
{
/**
* @param string $id ID único del snippet (nuevo o existente)
* @param string $name Nombre descriptivo
* @param string $description Descripción opcional
* @param string $css Código CSS
* @param string $type Tipo de carga: 'critical' | 'deferred'
* @param array<string> $pages Páginas donde aplicar: ['all'], ['home', 'posts'], etc.
* @param bool $enabled Si el snippet está activo
* @param int $order Orden de carga (menor = primero)
*/
public function __construct(
public readonly string $id,
public readonly string $name,
public readonly string $description,
public readonly string $css,
public readonly string $type,
public readonly array $pages,
public readonly bool $enabled,
public readonly int $order
) {}
/**
* Factory desde array (formulario o API)
*
* @param array $data Datos del formulario
* @return self
*/
public static function fromArray(array $data): self
{
return new self(
id: $data['id'] ?? '',
name: $data['name'] ?? '',
description: $data['description'] ?? '',
css: $data['css'] ?? '',
type: $data['type'] ?? 'deferred',
pages: $data['pages'] ?? ['all'],
enabled: (bool)($data['enabled'] ?? true),
order: (int)($data['order'] ?? 100)
);
}
/**
* Convierte a array para persistencia
*
* @return array
*/
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'css' => $this->css,
'type' => $this->type,
'pages' => $this->pages,
'enabled' => $this->enabled,
'order' => $this->order,
];
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace ROITheme\Admin\CustomCSSManager\Application\UseCases;
use ROITheme\Shared\Domain\Contracts\CSSSnippetRepositoryInterface;
/**
* Caso de uso: Eliminar snippet CSS
*
* SRP: Solo responsable de orquestar la eliminación
*/
final class DeleteSnippetUseCase
{
public function __construct(
private readonly CSSSnippetRepositoryInterface $repository
) {}
/**
* Ejecuta la eliminación del snippet
*
* @param string $snippetId ID del snippet a eliminar
* @return void
*/
public function execute(string $snippetId): void
{
$this->repository->delete($snippetId);
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace ROITheme\Admin\CustomCSSManager\Application\UseCases;
use ROITheme\Shared\Domain\Contracts\CSSSnippetRepositoryInterface;
/**
* Caso de uso: Obtener todos los snippets (para Admin UI)
*
* SRP: Solo responsable de obtener lista completa
*/
final class GetAllSnippetsUseCase
{
public function __construct(
private readonly CSSSnippetRepositoryInterface $repository
) {}
/**
* Ejecuta la obtención de todos los snippets
*
* @return array<array> Lista de snippets ordenados por 'order'
*/
public function execute(): array
{
return $this->repository->getAll();
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace ROITheme\Admin\CustomCSSManager\Application\UseCases;
use ROITheme\Admin\CustomCSSManager\Application\DTOs\SaveSnippetRequest;
use ROITheme\Admin\CustomCSSManager\Domain\Entities\CSSSnippet;
use ROITheme\Shared\Domain\Contracts\CSSSnippetRepositoryInterface;
/**
* Caso de uso: Guardar snippet CSS
*
* SRP: Solo responsable de orquestar el guardado
*/
final class SaveSnippetUseCase
{
public function __construct(
private readonly CSSSnippetRepositoryInterface $repository
) {}
public function execute(SaveSnippetRequest $request): void
{
// 1. Crear entidad desde DTO
$snippet = CSSSnippet::fromArray($request->toArray());
// 2. Validar en dominio
$snippet->validate();
// 3. Validar tamaño según tipo
$snippet->css()->validateForLoadType($snippet->loadType());
// 4. Persistir
$this->repository->save($snippet->toArray());
}
}