Files
roi-theme/Admin/Shared/Infrastructure/Api/WordPress/AdminAjaxHandler.php
FrankZamora c28fedd6e7 feat(exclusions): Integrate exclusion UI in CtaBoxSidebar component
- Add ExclusionFormPartial to CtaBoxSidebarFormBuilder
- Add _exclusions fields to CtaBoxSidebarFieldMapper with types
- Update AdminAjaxHandler to process json_array types via ExclusionFieldProcessor
- Enqueue exclusion-toggle.js in AdminAssetEnqueuer

The exclusion UI now appears in CTA Box Sidebar visibility section,
allowing admins to exclude component from specific categories,
post IDs, or URL patterns.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 11:05:52 -06:00

159 lines
5.4 KiB
PHP

<?php
declare(strict_types=1);
namespace ROITheme\Admin\Shared\Infrastructure\Api\WordPress;
use ROITheme\Shared\Application\UseCases\SaveComponentSettings\SaveComponentSettingsUseCase;
use ROITheme\Admin\Shared\Infrastructure\FieldMapping\FieldMapperRegistry;
use ROITheme\Admin\Shared\Infrastructure\Services\ExclusionFieldProcessor;
/**
* Handler para peticiones AJAX del panel de administracion
*
* RESPONSABILIDAD:
* - Manejar HTTP (request/response)
* - Delegar mapeo a FieldMapperRegistry
* - NO contiene logica de mapeo
*
* PRINCIPIOS:
* - SRP: Solo maneja HTTP
* - OCP: Nuevos componentes no requieren modificar esta clase
* - DIP: Depende de abstracciones (FieldMapperRegistry)
*/
final class AdminAjaxHandler
{
public function __construct(
private readonly ?SaveComponentSettingsUseCase $saveComponentSettingsUseCase = null,
private readonly ?FieldMapperRegistry $fieldMapperRegistry = null
) {}
public function register(): void
{
add_action('wp_ajax_roi_save_component_settings', [$this, 'saveComponentSettings']);
add_action('wp_ajax_roi_reset_component_defaults', [$this, 'resetComponentDefaults']);
}
public function saveComponentSettings(): void
{
check_ajax_referer('roi_admin_dashboard', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => 'No tienes permisos para realizar esta accion.']);
}
$component = sanitize_text_field($_POST['component'] ?? '');
$settings = json_decode(stripslashes($_POST['settings'] ?? '{}'), true);
if (empty($component) || empty($settings)) {
wp_send_json_error(['message' => 'Datos incompletos.']);
}
// Obtener mapper del modulo correspondiente
if ($this->fieldMapperRegistry === null || !$this->fieldMapperRegistry->hasMapper($component)) {
wp_send_json_error([
'message' => "No existe mapper para el componente: {$component}"
]);
}
$mapper = $this->fieldMapperRegistry->getMapper($component);
$fieldMapping = $mapper->getFieldMapping();
// Mapear settings usando el mapper del modulo
$mappedSettings = $this->mapSettings($settings, $fieldMapping);
// Guardar usando Use Case
if ($this->saveComponentSettingsUseCase !== null) {
$updated = $this->saveComponentSettingsUseCase->execute($component, $mappedSettings);
wp_send_json_success([
'message' => sprintf('Se guardaron %d campos correctamente.', $updated)
]);
} else {
wp_send_json_error(['message' => 'Error: Use Case no disponible.']);
}
}
/**
* Mapea settings de field IDs a grupos/atributos
*
* Soporta tipos especiales para campos de exclusion:
* - json_array: Convierte "a, b, c" a ["a", "b", "c"]
* - json_array_int: Convierte "1, 2, 3" a [1, 2, 3]
* - json_array_lines: Convierte lineas a array
*/
private function mapSettings(array $settings, array $fieldMapping): array
{
$mappedSettings = [];
$fieldProcessor = new ExclusionFieldProcessor();
foreach ($settings as $fieldId => $value) {
if (!isset($fieldMapping[$fieldId])) {
continue;
}
$mapping = $fieldMapping[$fieldId];
$groupName = $mapping['group'];
$attributeName = $mapping['attribute'];
$type = $mapping['type'] ?? null;
if (!isset($mappedSettings[$groupName])) {
$mappedSettings[$groupName] = [];
}
// Procesar valor segun tipo
if ($type !== null && is_string($value)) {
$value = $fieldProcessor->process($value, $type);
}
$mappedSettings[$groupName][$attributeName] = $value;
}
return $mappedSettings;
}
public function resetComponentDefaults(): void
{
// Verificar nonce
check_ajax_referer('roi_admin_dashboard', 'nonce');
// Verificar permisos
if (!current_user_can('manage_options')) {
wp_send_json_error([
'message' => 'No tienes permisos para realizar esta accion.'
]);
}
// Obtener componente
$component = sanitize_text_field($_POST['component'] ?? '');
if (empty($component)) {
wp_send_json_error([
'message' => 'Componente no especificado.'
]);
}
// Ruta al schema JSON
$schemaPath = get_template_directory() . '/Schemas/' . $component . '.json';
if (!file_exists($schemaPath)) {
wp_send_json_error([
'message' => 'Schema del componente no encontrado.'
]);
}
// Usar repositorio para restaurar valores
if ($this->saveComponentSettingsUseCase !== null) {
global $wpdb;
$repository = new \ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressComponentSettingsRepository($wpdb);
$updated = $repository->resetToDefaults($component, $schemaPath);
wp_send_json_success([
'message' => sprintf('Se restauraron %d campos a sus valores por defecto.', $updated)
]);
} else {
wp_send_json_error([
'message' => 'Error: Repositorio no disponible.'
]);
}
}
}