/** * Admin JavaScript for Informatiq Smart Pricing */ (function($) { 'use strict'; var InformatiqSP = { /** * Initialize */ init: function() { this.bindEvents(); }, /** * Bind event handlers */ bindEvents: function() { $('#informatiq-sp-manual-sync').on('click', this.handleManualSync); $('#informatiq-sp-test-connection').on('click', this.handleTestConnection); $('#informatiq-sp-revoke-auth').on('click', this.handleRevokeAuth); $('#informatiq-sp-compare-products').on('click', this.handleCompareProducts); }, /** * Handle manual sync button click */ handleManualSync: function(e) { e.preventDefault(); var $button = $(this); var $status = $('#informatiq-sp-sync-status'); // Confirm action if (!confirm(informatiqSP.strings.confirmSync || 'Are you sure you want to run a manual price sync? This may take several minutes.')) { return; } // Disable button and show loading state $button.prop('disabled', true).addClass('informatiq-sp-loading'); // Show status message $status .removeClass('notice-success notice-error') .addClass('notice-info') .html('

' + (informatiqSP.strings.syncInProgress || 'Sync in progress...') + '

') .show(); // Make AJAX request $.ajax({ url: informatiqSP.ajaxUrl, type: 'POST', data: { action: 'informatiq_sp_manual_sync', nonce: informatiqSP.nonce }, success: function(response) { if (response.success) { $status .removeClass('notice-info notice-error') .addClass('notice-success') .html('

' + response.data.message + '

'); // Reload page after 2 seconds to show updated logs setTimeout(function() { location.reload(); }, 2000); } else { $status .removeClass('notice-info notice-success') .addClass('notice-error') .html('

Error: ' + (response.data.message || 'Unknown error') + '

'); } }, error: function(jqXHR, textStatus, errorThrown) { $status .removeClass('notice-info notice-success') .addClass('notice-error') .html('

Error: ' + errorThrown + '

'); }, complete: function() { $button.prop('disabled', false).removeClass('informatiq-sp-loading'); } }); }, /** * Handle test connection button click */ handleTestConnection: function(e) { e.preventDefault(); var $button = $(this); var $status = $('#informatiq-sp-sync-status'); // Disable button and show loading state $button.prop('disabled', true).addClass('informatiq-sp-loading'); // Show status message $status .removeClass('notice-success notice-error') .addClass('notice-info') .html('

' + (informatiqSP.strings.testInProgress || 'Testing connection...') + '

') .show(); // Make AJAX request $.ajax({ url: informatiqSP.ajaxUrl, type: 'POST', data: { action: 'informatiq_sp_test_connection', nonce: informatiqSP.nonce }, success: function(response) { if (response.success) { $status .removeClass('notice-info notice-error') .addClass('notice-success') .html('

' + response.data.message + '

'); } else { $status .removeClass('notice-info notice-success') .addClass('notice-error') .html('

Error: ' + (response.data.message || 'Unknown error') + '

'); } }, error: function(jqXHR, textStatus, errorThrown) { $status .removeClass('notice-info notice-success') .addClass('notice-error') .html('

Error: ' + errorThrown + '

'); }, complete: function() { $button.prop('disabled', false).removeClass('informatiq-sp-loading'); // Hide status message after 5 seconds setTimeout(function() { $status.fadeOut(); }, 5000); } }); }, /** * Handle revoke authorization button click */ handleRevokeAuth: function(e) { e.preventDefault(); var $button = $(this); // Confirm action if (!confirm(informatiqSP.strings.revokeConfirm || 'Are you sure you want to revoke Google authorization?')) { return; } // Disable button and show loading state $button.prop('disabled', true).text(informatiqSP.strings.revokeInProgress || 'Revoking...'); // Make AJAX request $.ajax({ url: informatiqSP.ajaxUrl, type: 'POST', data: { action: 'informatiq_sp_revoke_auth', nonce: informatiqSP.nonce }, success: function(response) { if (response.success) { // Reload page to show updated status location.reload(); } else { alert('Error: ' + (response.data.message || 'Unknown error')); $button.prop('disabled', false).text('Revoke Authorization'); } }, error: function(jqXHR, textStatus, errorThrown) { alert('Error: ' + errorThrown); $button.prop('disabled', false).text('Revoke Authorization'); } }); }, // Store comparison data for pagination and bulk updates. comparisonData: null, currentPage: 1, perPage: 50, /** * Handle compare products button click */ handleCompareProducts: function(e) { e.preventDefault(); var self = InformatiqSP; var $button = $(this); var $spinner = $button.next('.spinner'); var $results = $('#informatiq-sp-comparison-results'); var $status = $('#informatiq-sp-sync-status'); // Show loading status $status .removeClass('notice-success notice-error') .addClass('notice-info') .html('

Loading price insights from Google... This may take a moment.

') .show(); $button.prop('disabled', true); $spinner.addClass('is-active'); $.ajax({ url: informatiqSP.ajaxUrl, type: 'POST', timeout: 180000, // 3 minute timeout data: { action: 'informatiq_sp_compare_products', nonce: informatiqSP.nonce }, success: function(response) { console.log('Price insights response:', response); $status.hide(); if (response.success) { self.comparisonData = response.data; self.currentPage = 1; self.renderComparison(); $results.show(); } else { $status .removeClass('notice-info notice-success') .addClass('notice-error') .html('

Error: ' + (response.data.message || 'Unknown error') + '

') .show(); } }, error: function(jqXHR, textStatus, errorThrown) { console.error('AJAX error:', textStatus, errorThrown); $status .removeClass('notice-info notice-success') .addClass('notice-error') .html('

Error: ' + errorThrown + '

') .show(); }, complete: function() { $button.prop('disabled', false); $spinner.removeClass('is-active'); } }); }, /** * Render comparison table with pagination */ renderComparison: function() { var self = this; var data = this.comparisonData; var $tbody = $('#informatiq-sp-comparison-tbody'); var $summary = $('#informatiq-sp-comparison-summary'); var $pagination = $('#informatiq-sp-pagination'); if (!data || !data.products) return; var products = data.products; var totalPages = Math.ceil(products.length / this.perPage); var start = (this.currentPage - 1) * this.perPage; var end = start + this.perPage; var pageProducts = products.slice(start, end); // Stats var stats = { total: products.length, withInsight: 0, canIncrease: 0, shouldDecrease: 0, totalPotentialGain: 0 }; products.forEach(function(p) { if (p.has_insight) { stats.withInsight++; if (p.potential_gain > 0) { stats.canIncrease++; stats.totalPotentialGain += p.potential_gain; } else if (p.potential_gain < 0) { stats.shouldDecrease++; } } }); // Render summary var summaryHtml = 'Summary: ' + stats.withInsight + ' of ' + stats.total + ' products have Google price insights. '; summaryHtml += '' + stats.canIncrease + ' can increase price, '; summaryHtml += '' + stats.shouldDecrease + ' should decrease price. '; if (stats.totalPotentialGain > 0) { summaryHtml += '
Total potential gain: +' + data.currency + stats.totalPotentialGain.toFixed(2) + ' per sale cycle'; } $summary.html(summaryHtml); // Render table rows var html = ''; if (pageProducts.length === 0) { html = 'No products found.'; } else { pageProducts.forEach(function(product) { var localPrice = product.local_price ? data.currency + parseFloat(product.local_price).toFixed(2) : '-'; var suggestedPrice = product.suggested_price ? data.currency + parseFloat(product.suggested_price).toFixed(2) : '-'; var priceLabel = product.price_type === 'sale' ? ' (sale)' : ''; // Percentage difference styling var percentDiffHtml = '-'; var percentDiffStyle = ''; if (product.percent_diff !== null) { var pct = parseFloat(product.percent_diff); if (pct > 0) { percentDiffHtml = '+' + pct.toFixed(1) + '%'; percentDiffStyle = 'color: #00a32a; font-weight: bold;'; } else if (pct < 0) { percentDiffHtml = pct.toFixed(1) + '%'; percentDiffStyle = 'color: #d63638;'; } else { percentDiffHtml = '0%'; } } // Predicted changes var imprChange = product.predicted_impressions_change !== null ? (product.predicted_impressions_change > 0 ? '+' : '') + product.predicted_impressions_change.toFixed(1) + '%' : '-'; var clickChange = product.predicted_clicks_change !== null ? (product.predicted_clicks_change > 0 ? '+' : '') + product.predicted_clicks_change.toFixed(1) + '%' : '-'; var convChange = product.predicted_conversions_change !== null ? (product.predicted_conversions_change > 0 ? '+' : '') + product.predicted_conversions_change.toFixed(1) + '%' : '-'; var imprStyle = product.predicted_impressions_change > 0 ? 'color:#00a32a;' : (product.predicted_impressions_change < 0 ? 'color:#d63638;' : ''); var clickStyle = product.predicted_clicks_change > 0 ? 'color:#00a32a;' : (product.predicted_clicks_change < 0 ? 'color:#d63638;' : ''); var convStyle = product.predicted_conversions_change > 0 ? 'color:#00a32a;' : (product.predicted_conversions_change < 0 ? 'color:#d63638;' : ''); // Checkbox and action button - show for all products with insight data var checkbox = product.has_insight && product.suggested_price ? '' : ''; var actionBtn = product.has_insight && product.suggested_price ? '' : (product.has_insight ? 'No suggestion' : '-'); var costPrice = product.cost !== null ? data.currency + parseFloat(product.cost).toFixed(2) : '-'; html += ''; html += '' + checkbox + ''; html += '' + (product.brand || '-') + ''; html += '' + product.sku + ''; html += '' + self.truncate(product.name, 35) + ''; html += '' + costPrice + ''; html += '' + localPrice + priceLabel + ''; html += '' + suggestedPrice + ''; html += '' + percentDiffHtml + ''; html += '' + imprChange + ''; html += '' + clickChange + ''; html += '' + convChange + ''; html += '' + actionBtn + ''; html += ''; }); } $tbody.html(html); // Render pagination if (totalPages > 1) { var paginationHtml = 'Page ' + this.currentPage + ' of ' + totalPages + ' '; if (this.currentPage > 1) { paginationHtml += ' '; } if (this.currentPage < totalPages) { paginationHtml += ''; } $pagination.html(paginationHtml).show(); } else { $pagination.hide(); } // Bind events for this page self.bindComparisonEvents(); }, /** * Truncate text */ truncate: function(str, len) { if (!str) return ''; return str.length > len ? str.substring(0, len) + '...' : str; }, /** * Bind comparison table events */ bindComparisonEvents: function() { var self = this; // Single update buttons $('.informatiq-sp-update-single').off('click').on('click', function() { var $btn = $(this); var productId = $btn.data('product-id'); var newPrice = $btn.data('new-price'); $btn.prop('disabled', true).text('Updating...'); $.ajax({ url: informatiqSP.ajaxUrl, type: 'POST', data: { action: 'informatiq_sp_update_price', nonce: informatiqSP.nonce, product_id: productId, new_price: newPrice }, success: function(response) { if (response.success) { $btn.text('Done!').addClass('button-primary'); $btn.closest('tr').css('background-color', '#d4edda'); } else { alert('Error: ' + response.data.message); $btn.prop('disabled', false).text('Update'); } }, error: function() { alert('Request failed'); $btn.prop('disabled', false).text('Update'); } }); }); // Pagination $('.informatiq-sp-page').off('click').on('click', function() { self.currentPage = $(this).data('page'); self.renderComparison(); $('html, body').animate({ scrollTop: $('#informatiq-sp-comparison-results').offset().top - 50 }, 200); }); // Select all checkboxes $('#informatiq-sp-select-all, #informatiq-sp-select-all-header').off('change').on('change', function() { var checked = $(this).prop('checked'); $('.informatiq-sp-select-product').prop('checked', checked); $('#informatiq-sp-select-all, #informatiq-sp-select-all-header').prop('checked', checked); self.updateBulkButton(); }); // Individual checkbox $('.informatiq-sp-select-product').off('change').on('change', function() { self.updateBulkButton(); }); // Bulk update button $('#informatiq-sp-bulk-update').off('click').on('click', function() { self.handleBulkUpdate(); }); }, /** * Update bulk button state */ updateBulkButton: function() { var count = $('.informatiq-sp-select-product:checked').length; $('#informatiq-sp-bulk-update').prop('disabled', count === 0).text('Bulk Update Selected (' + count + ')'); }, /** * Handle bulk update */ handleBulkUpdate: function() { var updates = []; $('.informatiq-sp-select-product:checked').each(function() { updates.push({ product_id: $(this).data('product-id'), new_price: $(this).data('new-price') }); }); if (updates.length === 0) return; if (!confirm('Update prices for ' + updates.length + ' products?')) return; var $btn = $('#informatiq-sp-bulk-update'); $btn.prop('disabled', true).text('Updating...'); $.ajax({ url: informatiqSP.ajaxUrl, type: 'POST', data: { action: 'informatiq_sp_bulk_update_prices', nonce: informatiqSP.nonce, updates: updates }, success: function(response) { if (response.success) { alert(response.data.message); location.reload(); } else { alert('Error: ' + response.data.message); $btn.prop('disabled', false).text('Bulk Update Selected'); } }, error: function() { alert('Request failed'); $btn.prop('disabled', false).text('Bulk Update Selected'); } }); }, }; // Initialize when document is ready $(document).ready(function() { InformatiqSP.init(); }); })(jQuery);