/** * Admin JavaScript for Studiou WC Product Category Manager */ (function($) { 'use strict'; // Safe init wrapper - prevents one form's error from breaking others function safeInit(name, selector, initFn) { try { if ($(selector).length) { console.log('STUDIOU WC: ' + name + ' found, initializing'); initFn(); } } catch (e) { console.error('STUDIOU WC: Error initializing ' + name + ':', e); } } // Initialize admin scripts $(document).ready(function() { // Debug: Log that script is loaded console.log('STUDIOU WC: Admin script loaded v1.7.2'); if (typeof studiouWcpcm === 'undefined') { console.error('STUDIOU WC: studiouWcpcm object is NOT defined - AJAX will not work'); return; } console.log('STUDIOU WC: studiouWcpcm object:', studiouWcpcm); safeInit('Import form', '#studiou-wcpcm-import-form', initImportForm); safeInit('Export form', '#studiou-wcpcm-export-form', initExportForm); safeInit('Sample download', '#studiou-wcpcm-download-sample', initSampleDownload); safeInit('Product import form', '#studiou-wcpcm-product-import-form', initProductImportForm); safeInit('Product export form', '#studiou-wcpcm-product-export-form', initProductExportForm); // Category Manipulations page: the manipulation forms live inside // lazily-loaded tab partials, so they are initialised by the tab // controller after their partial is injected (see initManipulationTabs). safeInit('Manipulation tabs', '.studiou-wcpcm-tabs', initManipulationTabs); }); /** * Initialize the tabbed Category Manipulations page. * * Each tab's markup is fetched on demand via the studiou_wcpcm_load_tab * AJAX handler. The first time a tab is opened its partial is rendered into * its own panel and its form handlers are wired; the panel is then kept in * the DOM (hidden) so switching tabs preserves both state and event * handlers - no re-fetch and no re-init. */ function initManipulationTabs() { // Maps a tab slug to the init function that wires its partial's handlers. var TAB_INIT = { 'move': initMoveForm, 'batch-description': initBatchDescriptionForm, 'delete-products': initDeleteProductsForm, 'clear-media': initClearMediaForm, 'remove-unassigned': initRemoveUnassignedMediaForm, 'upvp': initUpdateVariantPricesForm, 'notes': null // static, nothing to wire }; var i18n = (studiouWcpcm && studiouWcpcm.i18n) ? studiouWcpcm.i18n : {}; var $tabs = $('.studiou-wcpcm-tabs .nav-tab'); var $content = $('#studiou-wcpcm-tab-content'); var loaded = {}; // slug -> true once its panel exists in the DOM var allowed = $tabs.map(function() { return $(this).data('tab'); }).get(); function panelFor(slug) { return $content.children('.studiou-wcpcm-tab-panel[data-tab="' + slug + '"]'); } function activate(slug) { if (allowed.indexOf(slug) === -1) { slug = allowed[0]; } $tabs.removeClass('nav-tab-active') .filter('[data-tab="' + slug + '"]').addClass('nav-tab-active'); if (window.history && history.replaceState) { history.replaceState(null, '', '#' + slug); } else { window.location.hash = slug; } // Drop any stale loading / error placeholders, hide every panel. $content.find('.studiou-wcpcm-tab-loading, .studiou-wcpcm-tab-error').remove(); $content.children('.studiou-wcpcm-tab-panel').hide(); // Already loaded - just reveal its panel (handlers + state intact). if (loaded[slug]) { panelFor(slug).show(); return; } $content.append('
' + (i18n.tabLoading || 'Loading...') + '
'); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_load_tab', nonce: studiouWcpcm.nonce, tab: slug }, success: function(response) { $content.find('.studiou-wcpcm-tab-loading').remove(); if (response.success && response.data && typeof response.data.html === 'string') { // Stale click guard: only reveal if this tab is still active. var stillActive = ($tabs.filter('.nav-tab-active').data('tab') === slug); var $panel = $('
') .attr('data-tab', slug) .html(response.data.html); $content.append($panel); loaded[slug] = true; var initFn = TAB_INIT[slug]; if (typeof initFn === 'function') { try { initFn(); } catch (e) { console.error('STUDIOU WC: tab init "' + slug + '" failed', e); } } if (stillActive) { $content.children('.studiou-wcpcm-tab-panel').hide(); $panel.show(); } else { $panel.hide(); } } else { var msg = (response.data && response.data.message) || (i18n.tabLoadError || 'Failed to load tab'); $content.append('
' + msg + '
'); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: load_tab AJAX error:', status, error); $content.find('.studiou-wcpcm-tab-loading').remove(); $content.append('
' + (i18n.tabLoadError || 'Failed to load tab') + ': ' + error + '
'); } }); } $tabs.on('click', function(e) { e.preventDefault(); activate($(this).data('tab')); }); // Initial tab: a valid URL hash, otherwise the first tab. var initial = (window.location.hash || '').replace(/^#/, ''); activate(allowed.indexOf(initial) !== -1 ? initial : allowed[0]); } /** * Initialize import form */ function initImportForm() { // v1.7.0 — M1: batched category import. The flow is parse → loop // batches → summarise, mirroring the product importer. Removes the // single-shot ~60s timeout on large category trees. The legacy // single-shot endpoint stays registered for back-compat but JS no // longer calls it. $('#studiou-wcpcm-import-form').on('submit', function(e) { e.preventDefault(); var $form = $(this); var $submitBtn = $form.find('#studiou-wcpcm-import-button'); var $spinner = $form.find('.spinner'); $submitBtn.prop('disabled', true); $spinner.css('visibility', 'visible'); $('.studiou-wcpcm-notice-area').empty(); $('.studiou-wcpcm-import-results').hide(); $('#studiou-wcpcm-import-results').empty(); // Step 1 — parse the CSV server-side, stage to disk, get a token. var formData = new FormData(this); formData.append('action', 'studiou_wcpcm_import_parse'); formData.append('nonce', studiouWcpcm.nonce); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: formData, processData: false, contentType: false, success: function(response) { if (!response.success) { showNotice('error', response.data && response.data.message ? response.data.message : 'Error'); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); return; } runImportBatches(response.data.staging_key, response.data.total, $submitBtn, $spinner); }, error: function() { showNotice('error', studiouWcpcm.i18n.importError || 'Import error'); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); }); function runImportBatches(stagingKey, total, $submitBtn, $spinner) { var processed = 0; var totalSuccess = 0; var totalErrors = 0; var allMessages = []; // Show or build a simple progress indicator. The view doesn't // require a dedicated progress-bar element; we render into the // existing results container. var $results = $('#studiou-wcpcm-import-results'); $results.empty(); $('.studiou-wcpcm-import-results').show(); $results.html('

0 / ' + total + '

'); function nextBatch() { $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_import_batch', nonce: studiouWcpcm.nonce, staging_key: stagingKey, offset: processed }, success: function(response) { if (!response.success) { showNotice('error', response.data && response.data.message ? response.data.message : 'Error'); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); return; } processed = response.data.processed; totalSuccess += response.data.success || 0; totalErrors += response.data.errors || 0; if (Array.isArray(response.data.messages)) { allMessages = allMessages.concat(response.data.messages); } $results.html('

' + processed + ' / ' + total + '

'); if (response.data.done) { var summary = '

' + (totalSuccess + totalErrors) + ' items processed: ' + totalSuccess + ' OK, ' + totalErrors + ' errors.

'; if (allMessages.length) { summary += '
' + allMessages.map(function(m){return $('
').text(m).html();}).join('\n') + '
'; } $results.html(summary); showNotice(totalErrors === 0 ? 'success' : 'warning', studiouWcpcm.i18n.importSuccess || 'Import complete'); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } else { nextBatch(); } }, error: function() { showNotice('error', studiouWcpcm.i18n.importError || 'Import error'); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); } nextBatch(); } } /** * Initialize export form */ function initExportForm() { $('#studiou-wcpcm-export-form').on('submit', function(e) { e.preventDefault(); // Show spinner var $submitBtn = $(this).find('#studiou-wcpcm-export-button'); var $spinner = $(this).find('.spinner'); $submitBtn.prop('disabled', true); $spinner.css('visibility', 'visible'); // Clear previous notices $('.studiou-wcpcm-notice-area').empty(); // Send AJAX request $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_export', nonce: studiouWcpcm.nonce }, success: function(response) { if (response.success) { showNotice('success', studiouWcpcm.i18n.exportSuccess); // Show download link $('#studiou-wcpcm-export-message').text(response.data.message); $('#studiou-wcpcm-download-export').attr('href', response.data.download_url); $('.studiou-wcpcm-export-results').show(); } else { showNotice('error', response.data.message); } }, error: function() { showNotice('error', studiouWcpcm.i18n.exportError); }, complete: function() { // Hide spinner $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); }); } /** * Initialize move products form */ function initMoveForm() { // v1.7.0 — M2: batched product move. Step 1 (start) returns the // list of product IDs in the source category. Step 2 (chunk) moves // 25 at a time. JS drives the chunking + progress bar. Removes the // single-shot ~60s PHP-timeout cliff on large source categories. $('#studiou-wcpcm-move-form').on('submit', function(e) { e.preventDefault(); var sourceCategory = $('#source_category').val(); var targetCategory = $('#target_category').val(); if (!sourceCategory || !targetCategory) { showNotice('error', studiouWcpcm.i18n.selectBothCategories || 'Please select both source and target categories'); return; } if (sourceCategory === targetCategory) { showNotice('error', studiouWcpcm.i18n.selectDifferentCategories); return; } var $form = $(this); var $submitBtn = $form.find('#studiou-wcpcm-move-button'); var $spinner = $form.find('.spinner'); $submitBtn.prop('disabled', true); $spinner.css('visibility', 'visible'); $('.studiou-wcpcm-notice-area').empty(); $('.studiou-wcpcm-move-results').hide(); // Step 1 — get the product IDs to move. $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_move_products_start', source_category: sourceCategory, target_category: targetCategory, nonce: studiouWcpcm.nonce }, success: function(response) { if (!response.success) { showNotice('error', response.data && response.data.message ? response.data.message : (studiouWcpcm.i18n.moveError || 'Error')); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); return; } var ids = response.data.product_ids || []; var total = response.data.total || 0; var sourceName = response.data.source_name || ''; var targetName = response.data.target_name || ''; if (total === 0) { $('#studiou-wcpcm-move-message').html('

' + (studiouWcpcm.i18n.moveNoProducts || 'No products to move.') + '

'); $('.studiou-wcpcm-move-results').show(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); return; } runMoveChunks(ids, total, sourceCategory, targetCategory, sourceName, targetName, $submitBtn, $spinner); }, error: function() { showNotice('error', studiouWcpcm.i18n.moveError || 'Move error'); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); }); function runMoveChunks(allIds, total, sourceId, targetId, sourceName, targetName, $submitBtn, $spinner) { var CHUNK = 25; var idx = 0; var movedTotal = 0; var failedTotal = 0; var $msg = $('#studiou-wcpcm-move-message'); $msg.html('

0 / ' + total + '

'); $('.studiou-wcpcm-move-results').show(); function nextChunk() { var chunk = allIds.slice(idx, idx + CHUNK); if (chunk.length === 0) { return; } $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_move_products_chunk', nonce: studiouWcpcm.nonce, source_category: sourceId, target_category: targetId, product_ids: chunk }, success: function(response) { if (!response.success) { showNotice('error', response.data && response.data.message ? response.data.message : (studiouWcpcm.i18n.moveError || 'Error')); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); return; } movedTotal += response.data.moved || 0; failedTotal += response.data.failed || 0; idx += chunk.length; $msg.html('

' + idx + ' / ' + total + '

'); if (idx >= total) { var summary = '

' + movedTotal + ' moved from "' + sourceName + '" to "' + targetName + '"'; if (failedTotal > 0) { summary += ', ' + failedTotal + ' failed'; } summary += '.

'; $msg.html(summary); showNotice(failedTotal === 0 ? 'success' : 'warning', studiouWcpcm.i18n.moveSuccess || 'Move complete'); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); setTimeout(function() { location.reload(); }, 2000); } else { nextChunk(); } }, error: function() { showNotice('error', studiouWcpcm.i18n.moveError || 'Move error'); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); } nextChunk(); } } /** * Initialize batch description form */ function initBatchDescriptionForm() { console.log('STUDIOU WC: Initializing batch description form handlers'); // Check all button handler $('#studiou-wcpcm-check-all').on('click', function(e) { e.preventDefault(); console.log('STUDIOU WC: Check all button clicked'); $('#categories_list input[type="checkbox"]').prop('checked', true); }); // Uncheck all button handler $('#studiou-wcpcm-uncheck-all').on('click', function(e) { e.preventDefault(); console.log('STUDIOU WC: Uncheck all button clicked'); $('#categories_list input[type="checkbox"]').prop('checked', false); }); // Form submit handler $('#studiou-wcpcm-batch-description-form').on('submit', function(e) { e.preventDefault(); console.log('STUDIOU WC: Batch description form submitted'); // Get selected categories var selectedCategories = []; $('#categories_list input[type="checkbox"]:checked').each(function() { selectedCategories.push($(this).val()); }); var description = $('#category_description').val(); console.log('STUDIOU WC: Selected categories:', selectedCategories); console.log('STUDIOU WC: Description:', description); // Validate input if (selectedCategories.length === 0) { console.log('STUDIOU WC: No categories selected'); showNotice('error', 'Please select at least one category'); return; } // Show spinner var $submitBtn = $(this).find('#studiou-wcpcm-batch-description-button'); var $spinner = $(this).find('.spinner'); $submitBtn.prop('disabled', true); $spinner.css('visibility', 'visible'); // Clear previous notices $('.studiou-wcpcm-notice-area').empty(); // Debug: Log AJAX request details console.log('STUDIOU WC: Sending batch description AJAX request to:', studiouWcpcm.ajaxUrl); console.log('STUDIOU WC: Nonce:', studiouWcpcm.nonce); // Send AJAX request $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_batch_description', selected_categories: selectedCategories, description: description, nonce: studiouWcpcm.nonce }, success: function(response) { console.log('STUDIOU WC: Batch description AJAX response:', response); if (response.success) { showNotice('success', studiouWcpcm.i18n.batchDescriptionSuccess || 'Batch description operation successful'); // Show results $('#studiou-wcpcm-batch-description-message').html('

' + response.data.message + '

'); $('.studiou-wcpcm-batch-description-results').show(); } else { showNotice('error', response.data.message); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: Batch description AJAX error:', status, error); console.error('STUDIOU WC: Response text:', xhr.responseText); showNotice('error', (studiouWcpcm.i18n.batchDescriptionError || 'Batch description operation error') + ': ' + error); }, complete: function() { // Hide spinner $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); }); } /** * Initialize sample CSV download */ function initSampleDownload() { $('#studiou-wcpcm-download-sample').on('click', function(e) { e.preventDefault(); // Sample CSV content var csvContent = 'name,url-name,parent-name,description,display-type,visibility,protected-passwords,operation\n' + '"Clothing","clothing","","Top quality clothing","default","public","","A"\n' + '"Men","men","Clothing","Men\'s clothing","products","public","","A"\n' + '"Women","women","Clothing","Women\'s clothing","products","protected","password123","A"\n' + '"Kids","kids","Clothing","Kids clothing","products","public","","U"\n' + '"Accessories","accessories","","All accessories","default","public","","D"'; // Create download link var blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); var url = URL.createObjectURL(blob); var link = document.createElement('a'); link.setAttribute('href', url); link.setAttribute('download', 'sample-product-categories.csv'); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); }); } /** * Initialize delete products form */ function initDeleteProductsForm() { console.log('STUDIOU WC: Initializing delete products form handlers'); console.log('STUDIOU WC: Form element:', $('#studiou-wcpcm-delete-products-form')); console.log('STUDIOU WC: Check all button:', $('#studiou-wcpcm-delete-check-all')); console.log('STUDIOU WC: Delete button:', $('#studiou-wcpcm-delete-products-button')); // Check all button handler $('#studiou-wcpcm-delete-check-all').on('click', function(e) { e.preventDefault(); console.log('STUDIOU WC: Delete check all button clicked'); $('#delete_categories_list input[type="checkbox"]').prop('checked', true); }); // Uncheck all button handler $('#studiou-wcpcm-delete-uncheck-all').on('click', function(e) { e.preventDefault(); console.log('STUDIOU WC: Delete uncheck all button clicked'); $('#delete_categories_list input[type="checkbox"]').prop('checked', false); }); // Form submit handler $('#studiou-wcpcm-delete-products-form').on('submit', function(e) { e.preventDefault(); console.log('STUDIOU WC: Delete products form submitted'); // Get selected categories var selectedCategories = []; $('#delete_categories_list input[type="checkbox"]:checked').each(function() { selectedCategories.push($(this).val()); }); console.log('STUDIOU WC: Selected categories:', selectedCategories); // Validate input if (selectedCategories.length === 0) { console.log('STUDIOU WC: No categories selected'); showNotice('error', studiouWcpcm.i18n.selectAtLeastOneCategory || 'Please select at least one category'); return; } // Confirm deletion if (!confirm(studiouWcpcm.i18n.confirmDelete || 'Are you sure you want to permanently delete ALL products in the selected categories? This action cannot be undone!')) { console.log('STUDIOU WC: User cancelled deletion'); return; } // Show spinner var $submitBtn = $(this).find('#studiou-wcpcm-delete-products-button'); var $spinner = $(this).find('.spinner'); $submitBtn.prop('disabled', true); $spinner.css('visibility', 'visible'); // Clear previous notices $('.studiou-wcpcm-notice-area').empty(); // Hide results, show progress $('.studiou-wcpcm-delete-results').hide(); $('.studiou-wcpcm-delete-progress').show(); $('#studiou-wcpcm-delete-progress-text').text('Initializing...'); updateProgressBar(0); // Debug: Log AJAX request details console.log('STUDIOU WC: Sending delete products AJAX request to:', studiouWcpcm.ajaxUrl); console.log('STUDIOU WC: Nonce:', studiouWcpcm.nonce); // Send AJAX request to get product IDs $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_delete_products', delete_categories: selectedCategories, nonce: studiouWcpcm.nonce }, success: function(response) { console.log('STUDIOU WC: Delete products AJAX response:', response); if (response.success) { var productIds = response.data.product_ids; var totalCount = response.data.total_count; console.log('STUDIOU WC: Total products to delete:', totalCount); if (totalCount === 0) { showNotice('success', studiouWcpcm.i18n.noProductsFound || 'No products found in selected categories'); $('.studiou-wcpcm-delete-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); return; } // Process products in batches processDeletionBatches(productIds, totalCount, $submitBtn, $spinner); } else { showNotice('error', response.data.message); $('.studiou-wcpcm-delete-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: Delete products AJAX error:', status, error); console.error('STUDIOU WC: Response text:', xhr.responseText); showNotice('error', (studiouWcpcm.i18n.deleteError || 'Delete products operation error') + ': ' + error); $('.studiou-wcpcm-delete-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); }); } /** * Process deletion in batches */ function processDeletionBatches(productIds, totalCount, $submitBtn, $spinner) { var batchSize = 10; // Process 10 products at a time var batches = []; var totalDeleted = 0; var totalFailed = 0; var failedProducts = []; // Split products into batches for (var i = 0; i < productIds.length; i += batchSize) { batches.push(productIds.slice(i, i + batchSize)); } console.log('STUDIOU WC: Processing ' + batches.length + ' batches of products'); var currentBatch = 0; function processBatch() { if (currentBatch >= batches.length) { // All batches completed console.log('STUDIOU WC: All batches completed'); onDeletionComplete(totalDeleted, totalFailed, failedProducts, $submitBtn, $spinner); return; } var batch = batches[currentBatch]; var progress = Math.round((currentBatch / batches.length) * 100); $('#studiou-wcpcm-delete-progress-text').text( 'Deleting products... ' + (currentBatch * batchSize) + ' of ' + totalCount + ' processed' ); updateProgressBar(progress); // Send batch delete request $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_delete_products_batch', product_ids: batch, nonce: studiouWcpcm.nonce }, success: function(response) { if (response.success) { totalDeleted += response.data.deleted_count; totalFailed += response.data.failed_count; failedProducts = failedProducts.concat(response.data.failed_products); console.log('STUDIOU WC: Batch ' + currentBatch + ' completed - Deleted: ' + response.data.deleted_count + ', Failed: ' + response.data.failed_count); // Process next batch currentBatch++; processBatch(); } else { showNotice('error', response.data.message); $('.studiou-wcpcm-delete-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: Batch delete AJAX error:', status, error); showNotice('error', 'Error deleting batch: ' + error); $('.studiou-wcpcm-delete-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); } // Start processing batches processBatch(); } /** * Handle deletion completion */ function onDeletionComplete(totalDeleted, totalFailed, failedProducts, $submitBtn, $spinner) { // Update progress bar to 100% updateProgressBar(100); $('#studiou-wcpcm-delete-progress-text').text('Deletion complete!'); // Show results var message = '

Deletion Summary:

'; message += ''; $('#studiou-wcpcm-delete-message').html(message); $('.studiou-wcpcm-delete-results').show(); // Hide progress after a delay setTimeout(function() { $('.studiou-wcpcm-delete-progress').fadeOut(); }, 2000); if (totalDeleted > 0) { showNotice('success', (studiouWcpcm.i18n.deleteSuccess || 'Successfully deleted') + ' ' + totalDeleted + ' products'); // Reload page after 3 seconds setTimeout(function() { location.reload(); }, 3000); } else { showNotice('error', studiouWcpcm.i18n.deleteError || 'Failed to delete any products'); } $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } /** * Update progress bar */ function updateProgressBar(percentage) { $('#studiou-wcpcm-delete-progress-bar').css('width', percentage + '%'); $('#studiou-wcpcm-delete-progress-bar').attr('data-progress', percentage + '%'); } /** * Initialize clear media categories form */ function initClearMediaForm() { console.log('STUDIOU WC: Initializing clear media form handlers'); // Check all button handler $('#studiou-wcpcm-clear-media-check-all').on('click', function(e) { e.preventDefault(); console.log('STUDIOU WC: Clear media check all button clicked'); $('#clear_media_categories_list input[type="checkbox"]').prop('checked', true); }); // Uncheck all button handler $('#studiou-wcpcm-clear-media-uncheck-all').on('click', function(e) { e.preventDefault(); console.log('STUDIOU WC: Clear media uncheck all button clicked'); $('#clear_media_categories_list input[type="checkbox"]').prop('checked', false); }); // Form submit handler $('#studiou-wcpcm-clear-media-form').on('submit', function(e) { e.preventDefault(); console.log('STUDIOU WC: Clear media form submitted'); // Get selected categories var selectedCategories = []; $('#clear_media_categories_list input[type="checkbox"]:checked').each(function() { selectedCategories.push($(this).val()); }); console.log('STUDIOU WC: Selected media categories:', selectedCategories); // Validate input if (selectedCategories.length === 0) { console.log('STUDIOU WC: No media categories selected'); showNotice('error', studiouWcpcm.i18n.selectAtLeastOneCategory || 'Please select at least one category'); return; } // Confirm deletion if (!confirm(studiouWcpcm.i18n.confirmClearMedia || 'Are you sure you want to permanently delete ALL media files in the selected media categories? This action cannot be undone!')) { console.log('STUDIOU WC: User cancelled clear media'); return; } // Show spinner var $submitBtn = $(this).find('#studiou-wcpcm-clear-media-button'); var $spinner = $(this).find('.spinner'); $submitBtn.prop('disabled', true); $spinner.css('visibility', 'visible'); // Clear previous notices $('.studiou-wcpcm-notice-area').empty(); // Hide results, show progress $('.studiou-wcpcm-clear-media-results').hide(); $('.studiou-wcpcm-clear-media-progress').show(); $('#studiou-wcpcm-clear-media-progress-text').text('Initializing...'); updateClearMediaProgressBar(0); // Send AJAX request to get media IDs $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_clear_media', media_categories: selectedCategories, nonce: studiouWcpcm.nonce }, success: function(response) { console.log('STUDIOU WC: Clear media AJAX response:', response); if (response.success) { var mediaIds = response.data.media_ids; var totalCount = response.data.total_count; console.log('STUDIOU WC: Total media files to delete:', totalCount); if (totalCount === 0) { showNotice('success', studiouWcpcm.i18n.noMediaFound || 'No media files found in selected categories'); $('.studiou-wcpcm-clear-media-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); return; } // Process media in batches processMediaDeletionBatches(mediaIds, totalCount, $submitBtn, $spinner); } else { showNotice('error', response.data.message); $('.studiou-wcpcm-clear-media-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: Clear media AJAX error:', status, error); console.error('STUDIOU WC: Response text:', xhr.responseText); showNotice('error', (studiouWcpcm.i18n.clearMediaError || 'Clear media operation error') + ': ' + error); $('.studiou-wcpcm-clear-media-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); }); } /** * Process media deletion in batches */ function processMediaDeletionBatches(mediaIds, totalCount, $submitBtn, $spinner) { var batchSize = 10; var batches = []; var totalDeleted = 0; var totalFailed = 0; var failedMedia = []; // Split media into batches for (var i = 0; i < mediaIds.length; i += batchSize) { batches.push(mediaIds.slice(i, i + batchSize)); } console.log('STUDIOU WC: Processing ' + batches.length + ' batches of media files'); var currentBatch = 0; function processBatch() { if (currentBatch >= batches.length) { console.log('STUDIOU WC: All media batches completed'); onMediaDeletionComplete(totalDeleted, totalFailed, failedMedia, $submitBtn, $spinner); return; } var batch = batches[currentBatch]; var progress = Math.round((currentBatch / batches.length) * 100); $('#studiou-wcpcm-clear-media-progress-text').text( 'Deleting media files... ' + (currentBatch * batchSize) + ' of ' + totalCount + ' processed' ); updateClearMediaProgressBar(progress); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_clear_media_batch', media_ids: batch, nonce: studiouWcpcm.nonce }, success: function(response) { if (response.success) { totalDeleted += response.data.deleted_count; totalFailed += response.data.failed_count; failedMedia = failedMedia.concat(response.data.failed_media); console.log('STUDIOU WC: Media batch ' + currentBatch + ' completed - Deleted: ' + response.data.deleted_count + ', Failed: ' + response.data.failed_count); currentBatch++; processBatch(); } else { showNotice('error', response.data.message); $('.studiou-wcpcm-clear-media-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: Media batch delete AJAX error:', status, error); showNotice('error', 'Error deleting media batch: ' + error); $('.studiou-wcpcm-clear-media-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); } processBatch(); } /** * Handle media deletion completion */ function onMediaDeletionComplete(totalDeleted, totalFailed, failedMedia, $submitBtn, $spinner) { updateClearMediaProgressBar(100); $('#studiou-wcpcm-clear-media-progress-text').text('Deletion complete!'); var message = '

Deletion Summary:

'; message += ''; $('#studiou-wcpcm-clear-media-message').html(message); $('.studiou-wcpcm-clear-media-results').show(); setTimeout(function() { $('.studiou-wcpcm-clear-media-progress').fadeOut(); }, 2000); if (totalDeleted > 0) { showNotice('success', (studiouWcpcm.i18n.clearMediaSuccess || 'Successfully deleted') + ' ' + totalDeleted + ' media files'); setTimeout(function() { location.reload(); }, 3000); } else { showNotice('error', studiouWcpcm.i18n.clearMediaError || 'Failed to delete any media files'); } $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } /** * Update clear media progress bar */ function updateClearMediaProgressBar(percentage) { $('#studiou-wcpcm-clear-media-progress-bar').css('width', percentage + '%'); $('#studiou-wcpcm-clear-media-progress-bar').attr('data-progress', percentage + '%'); } /** * Initialize remove unassigned media form */ function initRemoveUnassignedMediaForm() { console.log('STUDIOU WC: Initializing remove unassigned media form handlers'); $('#studiou-wcpcm-remove-unassigned-media-form').on('submit', function(e) { e.preventDefault(); console.log('STUDIOU WC: Remove unassigned media form submitted'); if (!confirm(studiouWcpcm.i18n.confirmRemoveUnassigned || 'Are you sure you want to permanently delete ALL unused uncategorized media files? This action cannot be undone!')) { return; } var $submitBtn = $(this).find('#studiou-wcpcm-remove-unassigned-media-button'); var $spinner = $(this).find('.spinner'); $submitBtn.prop('disabled', true); $spinner.css('visibility', 'visible'); $('.studiou-wcpcm-notice-area').empty(); $('.studiou-wcpcm-remove-unassigned-media-results').hide(); $('.studiou-wcpcm-remove-unassigned-media-progress').show(); $('#studiou-wcpcm-remove-unassigned-media-progress-text').text('Initializing...'); updateRemoveUnassignedProgressBar(0); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_remove_unassigned_media', nonce: studiouWcpcm.nonce }, success: function(response) { console.log('STUDIOU WC: Remove unassigned media response:', response); if (response.success) { var mediaIds = response.data.media_ids; var totalCount = response.data.total_count; if (totalCount === 0) { showNotice('success', studiouWcpcm.i18n.noUnassignedMedia || 'No unused uncategorized media files found'); $('.studiou-wcpcm-remove-unassigned-media-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); return; } processUnassignedMediaBatches(mediaIds, totalCount, $submitBtn, $spinner); } else { showNotice('error', response.data.message); $('.studiou-wcpcm-remove-unassigned-media-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: Remove unassigned media error:', status, error); showNotice('error', (studiouWcpcm.i18n.removeUnassignedError || 'Error') + ': ' + error); $('.studiou-wcpcm-remove-unassigned-media-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); }); } /** * Process unassigned media deletion in batches */ function processUnassignedMediaBatches(mediaIds, totalCount, $submitBtn, $spinner) { var batchSize = 10; var batches = []; var totalDeleted = 0; var totalFailed = 0; var failedMedia = []; for (var i = 0; i < mediaIds.length; i += batchSize) { batches.push(mediaIds.slice(i, i + batchSize)); } var currentBatch = 0; function processBatch() { if (currentBatch >= batches.length) { onUnassignedMediaComplete(totalDeleted, totalFailed, failedMedia, $submitBtn, $spinner); return; } var batch = batches[currentBatch]; var progress = Math.round((currentBatch / batches.length) * 100); $('#studiou-wcpcm-remove-unassigned-media-progress-text').text( 'Deleting media files... ' + (currentBatch * batchSize) + ' of ' + totalCount + ' processed' ); updateRemoveUnassignedProgressBar(progress); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_remove_unassigned_media_batch', media_ids: batch, nonce: studiouWcpcm.nonce }, success: function(response) { if (response.success) { totalDeleted += response.data.deleted_count; totalFailed += response.data.failed_count; failedMedia = failedMedia.concat(response.data.failed_media); currentBatch++; processBatch(); } else { showNotice('error', response.data.message); $('.studiou-wcpcm-remove-unassigned-media-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: Unassigned media batch error:', status, error); showNotice('error', 'Error deleting batch: ' + error); $('.studiou-wcpcm-remove-unassigned-media-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); } processBatch(); } /** * Handle unassigned media deletion completion */ function onUnassignedMediaComplete(totalDeleted, totalFailed, failedMedia, $submitBtn, $spinner) { updateRemoveUnassignedProgressBar(100); $('#studiou-wcpcm-remove-unassigned-media-progress-text').text('Deletion complete!'); var message = '

Deletion Summary:

'; message += ''; $('#studiou-wcpcm-remove-unassigned-media-message').html(message); $('.studiou-wcpcm-remove-unassigned-media-results').show(); setTimeout(function() { $('.studiou-wcpcm-remove-unassigned-media-progress').fadeOut(); }, 2000); if (totalDeleted > 0) { showNotice('success', (studiouWcpcm.i18n.removeUnassignedSuccess || 'Successfully deleted') + ' ' + totalDeleted + ' media files'); setTimeout(function() { location.reload(); }, 3000); } else { showNotice('error', studiouWcpcm.i18n.removeUnassignedError || 'Failed to delete any media files'); } $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } /** * Update remove unassigned media progress bar */ function updateRemoveUnassignedProgressBar(percentage) { $('#studiou-wcpcm-remove-unassigned-media-progress-bar').css('width', percentage + '%'); $('#studiou-wcpcm-remove-unassigned-media-progress-bar').attr('data-progress', percentage + '%'); } /** * Show notice message * * @param {string} type Notice type (success, error) * @param {string} message Notice message */ function showNotice(type, message) { var $notice = $('
').text(message); $('.studiou-wcpcm-notice-area').append($notice); // Scroll to notice $('html, body').animate({ scrollTop: $('.studiou-wcpcm-notice-area').offset().top - 50 }, 500); } /** * Initialize product import form */ function initProductImportForm() { console.log('STUDIOU WC: Initializing product import form handlers'); // Global variables for batch processing var importTransientKey = null; var importTotal = 0; var importProcessed = 0; var importSuccess = 0; var importFailed = 0; var importSkipped = 0; var importMessages = []; var importFailedCsv = ''; $('#studiou-wcpcm-product-import-form').on('submit', function(e) { e.preventDefault(); console.log('STUDIOU WC: Product import form submitted'); // Validate file is selected var fileInput = $('#import_file')[0]; if (!fileInput.files || !fileInput.files[0]) { showNotice('error', 'Please select a CSV file to import'); return; } // Show spinner and progress var $submitBtn = $(this).find('#studiou-wcpcm-product-import-button'); var $spinner = $(this).find('.spinner'); $submitBtn.prop('disabled', true); $spinner.css('visibility', 'visible'); // Clear previous results $('.studiou-wcpcm-notice-area').empty(); $('.studiou-wcpcm-product-import-results').hide(); $('.studiou-wcpcm-product-import-failed').hide(); // Reset counters importTransientKey = null; importTotal = 0; importProcessed = 0; importSuccess = 0; importFailed = 0; importSkipped = 0; importMessages = []; importFailedCsv = ''; // Show progress bar $('.studiou-wcpcm-product-import-progress').show(); $('#studiou-wcpcm-product-import-progress-text').text('Parsing CSV file...'); updateProductImportProgressBar(0); // Step 1: Parse CSV file var formData = new FormData(this); formData.append('action', 'studiou_wcpcm_product_import_parse'); formData.append('nonce', studiouWcpcm.nonce); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: formData, processData: false, contentType: false, success: function(response) { console.log('STUDIOU WC: Product import parse response:', response); if (response.success) { importTransientKey = response.data.transient_key; importTotal = response.data.total; // Start batch processing processBatch(0, $submitBtn, $spinner); } else { showNotice('error', response.data.message || 'Parse failed'); $('.studiou-wcpcm-product-import-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: Product import parse error:', status, error); console.error('STUDIOU WC: Response text:', xhr.responseText); showNotice('error', 'Product import error: ' + error); $('.studiou-wcpcm-product-import-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); }); // Process batch of products function processBatch(offset, $submitBtn, $spinner) { console.log('STUDIOU WC: Processing batch at offset', offset); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_product_import_batch', nonce: studiouWcpcm.nonce, transient_key: importTransientKey, offset: offset }, success: function(response) { console.log('STUDIOU WC: Batch response:', response); if (response.success) { // Accumulate results importSuccess += response.data.success; importFailed += response.data.failed; importSkipped += response.data.skipped; importProcessed = response.data.processed; importTotal = response.data.total; importMessages = importMessages.concat(response.data.messages); if (response.data.failed_csv) { importFailedCsv = response.data.failed_csv; } // Update progress var percent = (importProcessed / importTotal) * 100; updateProductImportProgressBar(percent); $('#studiou-wcpcm-product-import-progress-text').text( 'Processing: ' + importProcessed + ' / ' + importTotal + ' products' ); // Check if complete if (importProcessed >= importTotal) { // Import complete $('#studiou-wcpcm-product-import-progress-text').text('Complete!'); showNotice('success', 'Product import completed!'); // Show results var message = '

Import Summary:

'; message += ''; $('#studiou-wcpcm-product-import-message').html(message); // Show detailed messages if (importMessages.length > 0) { var details = '
' + importMessages.join('\n') + '
'; $('#studiou-wcpcm-product-import-details-content').html(details); } $('.studiou-wcpcm-product-import-results').show(); // Show save failed button if there are failed items if (importFailed > 0 && importFailedCsv) { window.studiouWcpcmFailedCsv = importFailedCsv; $('.studiou-wcpcm-product-import-failed').show(); } // Hide progress after delay setTimeout(function() { $('.studiou-wcpcm-product-import-progress').fadeOut(); }, 2000); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } else { // Process next batch processBatch(importProcessed, $submitBtn, $spinner); } } else { showNotice('error', response.data.message || 'Batch processing failed'); $('.studiou-wcpcm-product-import-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: Batch processing error:', status, error); showNotice('error', 'Batch processing error: ' + error); $('.studiou-wcpcm-product-import-progress').hide(); $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); } // Save failed products button handler $('#studiou-wcpcm-save-failed-products').on('click', function(e) { e.preventDefault(); if (window.studiouWcpcmFailedCsv) { var blob = new Blob([window.studiouWcpcmFailedCsv], { type: 'text/csv;charset=utf-8;' }); var url = URL.createObjectURL(blob); var link = document.createElement('a'); link.setAttribute('href', url); link.setAttribute('download', 'failed-products-' + Date.now() + '.csv'); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } }); } /** * Initialize product export form */ function initProductExportForm() { console.log('STUDIOU WC: Initializing product export form handlers'); // Select all button handler $('#studiou-wcpcm-export-select-all').on('click', function(e) { e.preventDefault(); console.log('STUDIOU WC: Export select all button clicked'); $('#export_categories_list input[type="checkbox"]').prop('checked', true); }); // Deselect all button handler $('#studiou-wcpcm-export-deselect-all').on('click', function(e) { e.preventDefault(); console.log('STUDIOU WC: Export deselect all button clicked'); $('#export_categories_list input[type="checkbox"]').prop('checked', false); }); // Form submit handler $('#studiou-wcpcm-product-export-form').on('submit', function(e) { e.preventDefault(); console.log('STUDIOU WC: Product export form submitted'); // Get selected categories var selectedCategories = []; $('#export_categories_list input[type="checkbox"]:checked').each(function() { selectedCategories.push($(this).val()); }); console.log('STUDIOU WC: Selected categories:', selectedCategories); // Validate input if (selectedCategories.length === 0) { console.log('STUDIOU WC: No categories selected'); showNotice('error', 'Please select at least one category'); return; } // Show spinner var $submitBtn = $(this).find('#studiou-wcpcm-product-export-button'); var $spinner = $(this).find('.spinner'); $submitBtn.prop('disabled', true); $spinner.css('visibility', 'visible'); // Clear previous notices $('.studiou-wcpcm-notice-area').empty(); $('.studiou-wcpcm-product-export-results').hide(); // Debug: Log AJAX request details console.log('STUDIOU WC: Sending product export AJAX request to:', studiouWcpcm.ajaxUrl); console.log('STUDIOU WC: Nonce:', studiouWcpcm.nonce); // Send AJAX request $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_product_export', export_categories: selectedCategories, nonce: studiouWcpcm.nonce }, success: function(response) { console.log('STUDIOU WC: Product export AJAX response:', response); if (response.success) { showNotice('success', 'Export successful!'); // Show results $('#studiou-wcpcm-product-export-message').html('

' + response.data.message + '

'); $('#studiou-wcpcm-download-product-export').attr('href', response.data.download_url); $('.studiou-wcpcm-product-export-results').show(); } else { showNotice('error', response.data.message || 'Export failed'); } }, error: function(xhr, status, error) { console.error('STUDIOU WC: Product export AJAX error:', status, error); console.error('STUDIOU WC: Response text:', xhr.responseText); showNotice('error', 'Product export error: ' + error); }, complete: function() { // Hide spinner $submitBtn.prop('disabled', false); $spinner.css('visibility', 'hidden'); } }); }); } /** * Update product import progress bar */ function updateProductImportProgressBar(percentage) { $('#studiou-wcpcm-product-import-progress-bar').css('width', percentage + '%'); $('#studiou-wcpcm-product-import-progress-bar').attr('data-progress', Math.round(percentage) + '%'); } // Additional debug: Check page state after DOM load $(window).on('load', function() { console.log('STUDIOU WC: Window loaded'); console.log('STUDIOU WC: Manipulation tabs exist:', $('.studiou-wcpcm-tabs').length > 0); console.log('STUDIOU WC: Product import form exists:', $('#studiou-wcpcm-product-import-form').length > 0); console.log('STUDIOU WC: Product export form exists:', $('#studiou-wcpcm-product-export-form').length > 0); }); /** * Initialize Update Product Variant Prices form (UPVP) — v1.5.3 */ function initUpdateVariantPricesForm() { var $form = $('#studiou-wcpcm-upvp-form'); var $catList = $('#upvp_categories_list'); var $attrSelect = $('#upvp_attribute'); var $valueSelect = $('#upvp_attribute_value'); var $newPrice = $('#upvp_new_price'); var $countLabel = $('#studiou-wcpcm-upvp-affected-count'); var $dryRunBtn = $('#studiou-wcpcm-upvp-dry-run-button'); var $applyBtn = $('#studiou-wcpcm-upvp-apply-button'); var $spinner = $form.find('.spinner'); var $resultsBox = $('.studiou-wcpcm-upvp-results'); var $resultsMsg = $('#studiou-wcpcm-upvp-message'); var i18n = (studiouWcpcm && studiouWcpcm.i18n) ? studiouWcpcm.i18n : {}; var countDebounceHandle = null; var countInFlight = false; function getSelectedCategoryIds() { var ids = []; $catList.find('input[type="checkbox"]:checked').each(function() { ids.push($(this).val()); }); return ids; } function setApplyEnabled(count) { var enabled = (count > 0) && !countInFlight; $dryRunBtn.prop('disabled', !enabled); $applyBtn.prop('disabled', !enabled); } function loadAttributes() { $attrSelect.prop('disabled', true).html( '' ); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_upvp_get_attributes', nonce: studiouWcpcm.nonce }, success: function(response) { $attrSelect.empty(); $attrSelect.append(''); if (response.success && response.data && response.data.attributes) { $.each(response.data.attributes, function(_, a) { $attrSelect.append( $(''); if (!key) { $valueSelect.empty(); $valueSelect.append(''); $valueSelect.prop('disabled', true); refreshCount(); return; } $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_upvp_get_attribute_values', nonce: studiouWcpcm.nonce, attribute_key: key, category_ids: getSelectedCategoryIds() }, success: function(response) { $valueSelect.empty(); $valueSelect.append(''); if (response.success && response.data && response.data.values) { $.each(response.data.values, function(_, v) { $valueSelect.append($(''); $valueSelect.prop('disabled', false); } }); } function refreshCount() { if (countDebounceHandle) { clearTimeout(countDebounceHandle); } countDebounceHandle = setTimeout(doRefreshCount, 300); } function doRefreshCount() { var cats = getSelectedCategoryIds(); var key = $attrSelect.val(); var val = $valueSelect.val(); if (!cats.length || !key || !val) { $countLabel.text('0'); setApplyEnabled(0); return; } countInFlight = true; setApplyEnabled(0); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_upvp_count', nonce: studiouWcpcm.nonce, category_ids: cats, attribute_key: key, attribute_value: val }, success: function(response) { countInFlight = false; if (response.success && response.data) { var c = parseInt(response.data.count, 10) || 0; $countLabel.text(c); setApplyEnabled(c); } else { $countLabel.text('0'); setApplyEnabled(0); } }, error: function() { countInFlight = false; $countLabel.text('0'); setApplyEnabled(0); } }); } function renderSampleTable(sample, newPriceLabel) { if (!sample || !sample.length) return ''; var html = ''; html += ''; html += ''; if (newPriceLabel !== undefined) html += ''; html += ''; $.each(sample, function(_, r) { html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; if (newPriceLabel !== undefined) html += ''; html += ''; }); html += '
IDSKUNameParentCurrentNew
' + r.id + '' + (r.sku || '') + '' + (r.name || '') + '' + (r.parent_name || '') + '' + (r.current_price || '') + '' + newPriceLabel + '
'; return html; } // Wire up Check All / Uncheck All $('#studiou-wcpcm-upvp-check-all').on('click', function(e) { e.preventDefault(); $catList.find('input[type="checkbox"]').prop('checked', true); refreshCount(); }); $('#studiou-wcpcm-upvp-uncheck-all').on('click', function(e) { e.preventDefault(); $catList.find('input[type="checkbox"]').prop('checked', false); refreshCount(); }); $catList.on('change', 'input[type="checkbox"]', refreshCount); $attrSelect.on('change', loadValues); $valueSelect.on('change', refreshCount); // Dry Run $dryRunBtn.on('click', function(e) { e.preventDefault(); var cats = getSelectedCategoryIds(); var key = $attrSelect.val(); var val = $valueSelect.val(); if (!cats.length) { showNotice('error', i18n.selectAtLeastOneCategory || 'Please select at least one category'); return; } if (!key) { showNotice('error', i18n.upvpSelectAttribute || 'Please select an attribute'); return; } if (!val) { showNotice('error', i18n.upvpSelectValue || 'Please select an attribute value'); return; } $('.studiou-wcpcm-notice-area').empty(); $resultsBox.hide(); $spinner.css('visibility', 'visible'); $dryRunBtn.prop('disabled', true); $applyBtn.prop('disabled', true); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_upvp_count', nonce: studiouWcpcm.nonce, category_ids: cats, attribute_key: key, attribute_value: val }, success: function(response) { if (response.success && response.data) { var c = parseInt(response.data.count, 10) || 0; var sample = response.data.sample || []; var price = $newPrice.val(); var msg = ''; if (c === 0) { msg = '

' + (i18n.upvpNoMatches || 'No variations match the current filter.') + '

'; } else { var tpl = i18n.upvpDryRunComplete || 'Dry run complete: %d variations would be updated to %s.'; msg = '

' + tpl.replace('%d', c).replace('%s', price !== '' ? price : '—') + '

'; msg += renderSampleTable(sample, price !== '' ? price : undefined); } $resultsMsg.html(msg); $resultsBox.show(); $countLabel.text(c); setApplyEnabled(c); } else { showNotice('error', (response.data && response.data.message) || 'Dry run failed'); } }, error: function(xhr, status, error) { showNotice('error', 'Dry run error: ' + error); }, complete: function() { $spinner.css('visibility', 'hidden'); } }); }); // Apply $applyBtn.on('click', function(e) { e.preventDefault(); var cats = getSelectedCategoryIds(); var key = $attrSelect.val(); var val = $valueSelect.val(); var price = $newPrice.val(); var count = parseInt($countLabel.text(), 10) || 0; if (!cats.length) { showNotice('error', i18n.selectAtLeastOneCategory || 'Please select at least one category'); return; } if (!key) { showNotice('error', i18n.upvpSelectAttribute || 'Please select an attribute'); return; } if (!val) { showNotice('error', i18n.upvpSelectValue || 'Please select an attribute value'); return; } if (price === '' || isNaN(parseFloat(price)) || parseFloat(price) < 0) { showNotice('error', i18n.upvpInvalidPrice || 'Please enter a valid new price'); return; } var confirmTpl = i18n.upvpConfirmApply || 'Are you sure you want to update %d variations?'; if (!confirm(confirmTpl.replace('%d', count))) { return; } $('.studiou-wcpcm-notice-area').empty(); $resultsBox.hide(); $spinner.css('visibility', 'visible'); $dryRunBtn.prop('disabled', true); $applyBtn.prop('disabled', true); $.ajax({ url: studiouWcpcm.ajaxUrl, type: 'POST', data: { action: 'studiou_wcpcm_upvp_apply', nonce: studiouWcpcm.nonce, category_ids: cats, attribute_key: key, attribute_value: val, new_price: price }, success: function(response) { if (response.success && response.data) { showNotice('success', i18n.upvpApplySuccess || 'Variant prices updated successfully'); var html = '

' + (response.data.message || '') + '

'; if (response.data.failed_ids && response.data.failed_ids.length) { html += '

Failed IDs: ' + response.data.failed_ids.join(', ') + '

'; } $resultsMsg.html(html); $resultsBox.show(); refreshCount(); } else { showNotice('error', (response.data && response.data.message) || (i18n.upvpApplyError || 'Variant prices update error')); } }, error: function(xhr, status, error) { showNotice('error', (i18n.upvpApplyError || 'Variant prices update error') + ': ' + error); }, complete: function() { $spinner.css('visibility', 'hidden'); $dryRunBtn.prop('disabled', false); $applyBtn.prop('disabled', false); } }); }); // Initial load loadAttributes(); setApplyEnabled(0); } })(jQuery);