COMPLETADO: Fase 1 de la migración a Clean Architecture + POO ## Estructura de Carpetas - ✓ Estructura completa de 4 capas (Domain, Application, Infrastructure, Presentation) - ✓ Carpetas de Use Cases (SaveComponent, GetComponent, DeleteComponent, SyncSchema) - ✓ Estructura de tests (Unit, Integration, E2E) - ✓ Carpetas de schemas y templates ## Composer y Autoloading - ✓ PSR-4 autoloading configurado para ROITheme namespace - ✓ Autoloader optimizado regenerado ## DI Container - ✓ DIContainer implementado con patrón Singleton - ✓ Métodos set(), get(), has() para gestión de servicios - ✓ Getters específicos para ComponentRepository, ValidationService, CacheService - ✓ Placeholders que serán implementados en Fase 5 - ✓ Prevención de clonación y deserialización ## Interfaces - ✓ ComponentRepositoryInterface (Domain) - ✓ ValidationServiceInterface (Application) - ✓ CacheServiceInterface (Application) - ✓ Component entity placeholder (Domain) ## Bootstrap - ✓ functions.php actualizado con carga de Composer autoloader - ✓ Inicialización del DIContainer - ✓ Helper function roi_container() disponible globalmente ## Tests - ✓ 10 tests unitarios para DIContainer (100% cobertura) - ✓ Total: 13 tests unitarios, 28 assertions - ✓ Suite de tests pasando correctamente ## Validación - ✓ Script de validación automatizado (48/48 checks pasados) - ✓ 100% de validaciones exitosas La arquitectura base está lista para la Fase 2. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
441 lines
15 KiB
JavaScript
441 lines
15 KiB
JavaScript
/**
|
|
* Theme Options Admin JavaScript
|
|
*
|
|
* @package ROI_Theme
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
(function($) {
|
|
'use strict';
|
|
|
|
var ROIThemeOptions = {
|
|
|
|
/**
|
|
* Initialize
|
|
*/
|
|
init: function() {
|
|
this.tabs();
|
|
this.imageUpload();
|
|
this.resetOptions();
|
|
this.exportOptions();
|
|
this.importOptions();
|
|
this.formValidation();
|
|
this.conditionalFields();
|
|
},
|
|
|
|
/**
|
|
* Tab Navigation
|
|
*/
|
|
tabs: function() {
|
|
// Tab click handler
|
|
$('.roi-tabs-nav a').on('click', function(e) {
|
|
e.preventDefault();
|
|
|
|
var tabId = $(this).attr('href');
|
|
|
|
// Update active states
|
|
$('.roi-tabs-nav li').removeClass('active');
|
|
$(this).parent().addClass('active');
|
|
|
|
// Show/hide tab content
|
|
$('.roi-tab-pane').removeClass('active');
|
|
$(tabId).addClass('active');
|
|
|
|
// Update URL hash without scrolling
|
|
if (history.pushState) {
|
|
history.pushState(null, null, tabId);
|
|
} else {
|
|
window.location.hash = tabId;
|
|
}
|
|
});
|
|
|
|
// Load tab from URL hash on page load
|
|
if (window.location.hash) {
|
|
var hash = window.location.hash;
|
|
if ($(hash).length) {
|
|
$('.roi-tabs-nav a[href="' + hash + '"]').trigger('click');
|
|
}
|
|
}
|
|
|
|
// Handle browser back/forward buttons
|
|
$(window).on('hashchange', function() {
|
|
if (window.location.hash) {
|
|
$('.roi-tabs-nav a[href="' + window.location.hash + '"]').trigger('click');
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Image Upload
|
|
*/
|
|
imageUpload: function() {
|
|
var self = this;
|
|
var mediaUploader;
|
|
|
|
// Upload button click
|
|
$(document).on('click', '.roi-upload-image', function(e) {
|
|
e.preventDefault();
|
|
|
|
var button = $(this);
|
|
var container = button.closest('.roi-image-upload');
|
|
var preview = container.find('.roi-image-preview');
|
|
var input = container.find('.roi-image-id');
|
|
var removeBtn = container.find('.roi-remove-image');
|
|
|
|
// If the media uploader already exists, reopen it
|
|
if (mediaUploader) {
|
|
mediaUploader.open();
|
|
return;
|
|
}
|
|
|
|
// Create new media uploader
|
|
mediaUploader = wp.media({
|
|
title: roiAdminOptions.strings.selectImage,
|
|
button: {
|
|
text: roiAdminOptions.strings.useImage
|
|
},
|
|
multiple: false
|
|
});
|
|
|
|
// When an image is selected
|
|
mediaUploader.on('select', function() {
|
|
var attachment = mediaUploader.state().get('selection').first().toJSON();
|
|
|
|
// Set image ID
|
|
input.val(attachment.id);
|
|
|
|
// Show preview
|
|
var imgUrl = attachment.sizes && attachment.sizes.medium ?
|
|
attachment.sizes.medium.url : attachment.url;
|
|
preview.html('<img src="' + imgUrl + '" class="roi-preview-image" />');
|
|
|
|
// Show remove button
|
|
removeBtn.show();
|
|
});
|
|
|
|
// Open the uploader
|
|
mediaUploader.open();
|
|
});
|
|
|
|
// Remove button click
|
|
$(document).on('click', '.roi-remove-image', function(e) {
|
|
e.preventDefault();
|
|
|
|
var button = $(this);
|
|
var container = button.closest('.roi-image-upload');
|
|
var preview = container.find('.roi-image-preview');
|
|
var input = container.find('.roi-image-id');
|
|
|
|
// Clear values
|
|
input.val('');
|
|
preview.empty();
|
|
button.hide();
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Reset Options
|
|
*/
|
|
resetOptions: function() {
|
|
$('#roi-reset-options').on('click', function(e) {
|
|
e.preventDefault();
|
|
|
|
if (!confirm(roiAdminOptions.strings.confirmReset)) {
|
|
return;
|
|
}
|
|
|
|
var button = $(this);
|
|
button.prop('disabled', true).addClass('updating-message');
|
|
|
|
$.ajax({
|
|
url: roiAdminOptions.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'roi_reset_options',
|
|
nonce: roiAdminOptions.nonce
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
// Show success message
|
|
ROIThemeOptions.showNotice('success', response.data.message);
|
|
|
|
// Reload page after 1 second
|
|
setTimeout(function() {
|
|
window.location.reload();
|
|
}, 1000);
|
|
} else {
|
|
ROIThemeOptions.showNotice('error', response.data.message);
|
|
button.prop('disabled', false).removeClass('updating-message');
|
|
}
|
|
},
|
|
error: function() {
|
|
ROIThemeOptions.showNotice('error', roiAdminOptions.strings.error);
|
|
button.prop('disabled', false).removeClass('updating-message');
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Export Options
|
|
*/
|
|
exportOptions: function() {
|
|
$('#roi-export-options').on('click', function(e) {
|
|
e.preventDefault();
|
|
|
|
var button = $(this);
|
|
button.prop('disabled', true).addClass('updating-message');
|
|
|
|
$.ajax({
|
|
url: roiAdminOptions.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'roi_export_options',
|
|
nonce: roiAdminOptions.nonce
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
// Create download link
|
|
var blob = new Blob([response.data.data], { type: 'application/json' });
|
|
var url = window.URL.createObjectURL(blob);
|
|
var a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = response.data.filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
window.URL.revokeObjectURL(url);
|
|
document.body.removeChild(a);
|
|
|
|
ROIThemeOptions.showNotice('success', 'Options exported successfully!');
|
|
} else {
|
|
ROIThemeOptions.showNotice('error', response.data.message);
|
|
}
|
|
button.prop('disabled', false).removeClass('updating-message');
|
|
},
|
|
error: function() {
|
|
ROIThemeOptions.showNotice('error', roiAdminOptions.strings.error);
|
|
button.prop('disabled', false).removeClass('updating-message');
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Import Options
|
|
*/
|
|
importOptions: function() {
|
|
var modal = $('#roi-import-modal');
|
|
var importData = $('#roi-import-data');
|
|
|
|
// Show modal
|
|
$('#roi-import-options').on('click', function(e) {
|
|
e.preventDefault();
|
|
modal.show();
|
|
});
|
|
|
|
// Close modal
|
|
$('.roi-modal-close, #roi-import-cancel').on('click', function() {
|
|
modal.hide();
|
|
importData.val('');
|
|
});
|
|
|
|
// Close modal on outside click
|
|
$(window).on('click', function(e) {
|
|
if ($(e.target).is(modal)) {
|
|
modal.hide();
|
|
importData.val('');
|
|
}
|
|
});
|
|
|
|
// Submit import
|
|
$('#roi-import-submit').on('click', function(e) {
|
|
e.preventDefault();
|
|
|
|
var data = importData.val().trim();
|
|
|
|
if (!data) {
|
|
alert('Please paste your import data.');
|
|
return;
|
|
}
|
|
|
|
var button = $(this);
|
|
button.prop('disabled', true).addClass('updating-message');
|
|
|
|
$.ajax({
|
|
url: roiAdminOptions.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'roi_import_options',
|
|
nonce: roiAdminOptions.nonce,
|
|
import_data: data
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
ROIThemeOptions.showNotice('success', response.data.message);
|
|
modal.hide();
|
|
importData.val('');
|
|
|
|
// Reload page after 1 second
|
|
setTimeout(function() {
|
|
window.location.reload();
|
|
}, 1000);
|
|
} else {
|
|
ROIThemeOptions.showNotice('error', response.data.message);
|
|
button.prop('disabled', false).removeClass('updating-message');
|
|
}
|
|
},
|
|
error: function() {
|
|
ROIThemeOptions.showNotice('error', roiAdminOptions.strings.error);
|
|
button.prop('disabled', false).removeClass('updating-message');
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Form Validation
|
|
*/
|
|
formValidation: function() {
|
|
$('.roi-options-form').on('submit', function(e) {
|
|
var valid = true;
|
|
var firstError = null;
|
|
|
|
// Validate required fields
|
|
$(this).find('[required]').each(function() {
|
|
if (!$(this).val()) {
|
|
valid = false;
|
|
$(this).addClass('error');
|
|
|
|
if (!firstError) {
|
|
firstError = $(this);
|
|
}
|
|
} else {
|
|
$(this).removeClass('error');
|
|
}
|
|
});
|
|
|
|
// Validate number fields
|
|
$(this).find('input[type="number"]').each(function() {
|
|
var val = $(this).val();
|
|
var min = $(this).attr('min');
|
|
var max = $(this).attr('max');
|
|
|
|
if (val && min && parseInt(val) < parseInt(min)) {
|
|
valid = false;
|
|
$(this).addClass('error');
|
|
if (!firstError) {
|
|
firstError = $(this);
|
|
}
|
|
}
|
|
|
|
if (val && max && parseInt(val) > parseInt(max)) {
|
|
valid = false;
|
|
$(this).addClass('error');
|
|
if (!firstError) {
|
|
firstError = $(this);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Validate URL fields
|
|
$(this).find('input[type="url"]').each(function() {
|
|
var val = $(this).val();
|
|
if (val && !ROIThemeOptions.isValidUrl(val)) {
|
|
valid = false;
|
|
$(this).addClass('error');
|
|
if (!firstError) {
|
|
firstError = $(this);
|
|
}
|
|
}
|
|
});
|
|
|
|
if (!valid) {
|
|
e.preventDefault();
|
|
|
|
if (firstError) {
|
|
// Scroll to first error
|
|
$('html, body').animate({
|
|
scrollTop: firstError.offset().top - 100
|
|
}, 500);
|
|
firstError.focus();
|
|
}
|
|
|
|
ROIThemeOptions.showNotice('error', 'Please fix the errors in the form.');
|
|
return false;
|
|
}
|
|
|
|
// Add saving animation
|
|
$(this).find('.submit .button-primary').addClass('updating-message');
|
|
});
|
|
|
|
// Remove error class on input
|
|
$('.roi-options-form input, .roi-options-form select, .roi-options-form textarea').on('change input', function() {
|
|
$(this).removeClass('error');
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Conditional Fields
|
|
*/
|
|
conditionalFields: function() {
|
|
// Enable/disable related posts options based on checkbox
|
|
$('#enable_related_posts').on('change', function() {
|
|
var checked = $(this).is(':checked');
|
|
var fields = $('#related_posts_count, #related_posts_taxonomy, #related_posts_title, #related_posts_columns');
|
|
|
|
fields.closest('tr').toggleClass('roi-field-dependency', !checked);
|
|
fields.prop('disabled', !checked);
|
|
}).trigger('change');
|
|
|
|
// Enable/disable breadcrumb separator based on breadcrumbs checkbox
|
|
$('#enable_breadcrumbs').on('change', function() {
|
|
var checked = $(this).is(':checked');
|
|
var field = $('#breadcrumb_separator');
|
|
|
|
field.closest('tr').toggleClass('roi-field-dependency', !checked);
|
|
field.prop('disabled', !checked);
|
|
}).trigger('change');
|
|
},
|
|
|
|
/**
|
|
* Show Notice
|
|
*/
|
|
showNotice: function(type, message) {
|
|
var notice = $('<div class="notice notice-' + type + ' is-dismissible"><p>' + message + '</p></div>');
|
|
|
|
$('.roi-theme-options h1').after(notice);
|
|
|
|
// Auto-dismiss after 5 seconds
|
|
setTimeout(function() {
|
|
notice.fadeOut(function() {
|
|
$(this).remove();
|
|
});
|
|
}, 5000);
|
|
|
|
// Scroll to top
|
|
$('html, body').animate({ scrollTop: 0 }, 300);
|
|
},
|
|
|
|
/**
|
|
* Validate URL
|
|
*/
|
|
isValidUrl: function(url) {
|
|
try {
|
|
new URL(url);
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Initialize on document ready
|
|
$(document).ready(function() {
|
|
ROIThemeOptions.init();
|
|
});
|
|
|
|
// Make it globally accessible
|
|
window.ROIThemeOptions = ROIThemeOptions;
|
|
|
|
})(jQuery);
|