Features: - Fetch competitor benchmark prices from Google Price Competitiveness Report - New get_all_benchmark_prices() method in Google API class - Display competitor price instead of own Google price - Calculate recommended price (slightly below competitor) - Show potential gain/loss per product if price is optimized - Color-coded status: - Green: Your price is cheaper (opportunity to increase) - Blue: Competitive (within 2% of competitor) - Red: Expensive (above competitor) - Summary statistics showing: - Products with benchmark data - Count by status (cheaper/competitive/expensive) - Total potential gain if all prices optimized Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
390 lines
12 KiB
JavaScript
390 lines
12 KiB
JavaScript
/**
|
|
* Admin JavaScript for Informatiq Smart Pricing
|
|
*/
|
|
|
|
console.log('Informatiq Smart Pricing: Script loaded');
|
|
|
|
(function($) {
|
|
'use strict';
|
|
|
|
console.log('Informatiq Smart Pricing: IIFE executed, jQuery available:', typeof $ !== 'undefined');
|
|
|
|
var InformatiqSP = {
|
|
/**
|
|
* Initialize
|
|
*/
|
|
init: function() {
|
|
console.log('Informatiq Smart Pricing: init() called');
|
|
console.log('informatiqSP object:', typeof informatiqSP !== 'undefined' ? informatiqSP : 'NOT DEFINED');
|
|
this.bindEvents();
|
|
},
|
|
|
|
/**
|
|
* Bind event handlers
|
|
*/
|
|
bindEvents: function() {
|
|
console.log('Informatiq Smart Pricing: bindEvents() called');
|
|
|
|
var $compareBtn = $('#informatiq-sp-compare-products');
|
|
console.log('Compare products button found:', $compareBtn.length > 0);
|
|
|
|
$('#informatiq-sp-manual-sync').on('click', this.handleManualSync);
|
|
$('#informatiq-sp-test-connection').on('click', this.handleTestConnection);
|
|
$('#informatiq-sp-revoke-auth').on('click', this.handleRevokeAuth);
|
|
$compareBtn.on('click', this.handleCompareProducts);
|
|
|
|
console.log('Informatiq Smart Pricing: All event handlers bound');
|
|
},
|
|
|
|
/**
|
|
* 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('<p>' + (informatiqSP.strings.syncInProgress || 'Sync in progress...') + '</p>')
|
|
.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('<p>' + response.data.message + '</p>');
|
|
|
|
// 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('<p>Error: ' + (response.data.message || 'Unknown error') + '</p>');
|
|
}
|
|
},
|
|
error: function(jqXHR, textStatus, errorThrown) {
|
|
$status
|
|
.removeClass('notice-info notice-success')
|
|
.addClass('notice-error')
|
|
.html('<p>Error: ' + errorThrown + '</p>');
|
|
},
|
|
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('<p>' + (informatiqSP.strings.testInProgress || 'Testing connection...') + '</p>')
|
|
.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('<p>' + response.data.message + '</p>');
|
|
} else {
|
|
$status
|
|
.removeClass('notice-info notice-success')
|
|
.addClass('notice-error')
|
|
.html('<p>Error: ' + (response.data.message || 'Unknown error') + '</p>');
|
|
}
|
|
},
|
|
error: function(jqXHR, textStatus, errorThrown) {
|
|
$status
|
|
.removeClass('notice-info notice-success')
|
|
.addClass('notice-error')
|
|
.html('<p>Error: ' + errorThrown + '</p>');
|
|
},
|
|
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');
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Handle compare products button click
|
|
*/
|
|
handleCompareProducts: function(e) {
|
|
e.preventDefault();
|
|
|
|
console.log('=== COMPARE PRODUCTS CLICKED ===');
|
|
|
|
var $button = $(this);
|
|
var $spinner = $button.next('.spinner');
|
|
var $results = $('#informatiq-sp-comparison-results');
|
|
var $tbody = $('#informatiq-sp-comparison-tbody');
|
|
var $status = $('#informatiq-sp-sync-status');
|
|
|
|
console.log('Button:', $button.length);
|
|
console.log('Spinner:', $spinner.length);
|
|
console.log('Results container:', $results.length);
|
|
console.log('informatiqSP:', informatiqSP);
|
|
|
|
// Show loading status
|
|
$status
|
|
.removeClass('notice-success notice-error')
|
|
.addClass('notice-info')
|
|
.html('<p><strong>Loading product comparison...</strong> This may take a moment.</p>')
|
|
.show();
|
|
|
|
// Disable button and show spinner
|
|
$button.prop('disabled', true);
|
|
$spinner.addClass('is-active');
|
|
|
|
console.log('Making AJAX request to:', informatiqSP.ajaxUrl);
|
|
console.log('With nonce:', informatiqSP.nonce);
|
|
|
|
// Make AJAX request
|
|
$.ajax({
|
|
url: informatiqSP.ajaxUrl,
|
|
type: 'POST',
|
|
timeout: 120000, // 2 minute timeout
|
|
data: {
|
|
action: 'informatiq_sp_compare_products',
|
|
nonce: informatiqSP.nonce
|
|
},
|
|
beforeSend: function(xhr) {
|
|
console.log('AJAX beforeSend - request starting');
|
|
},
|
|
success: function(response) {
|
|
console.log('AJAX success callback');
|
|
console.log('AJAX response:', response);
|
|
|
|
// Log debug info if available.
|
|
if (response.data && response.data.debug) {
|
|
console.log('=== DEBUG INFO ===');
|
|
console.log('Sample Google product:', response.data.debug.sample_google_product);
|
|
console.log('Sample indexed keys:', response.data.debug.sample_google_keys);
|
|
console.log('Total indexed entries:', response.data.debug.index_count);
|
|
console.log('==================');
|
|
}
|
|
|
|
$status.hide();
|
|
|
|
if (response.success) {
|
|
var data = response.data;
|
|
var html = '';
|
|
|
|
// Stats counters.
|
|
var stats = {
|
|
total: 0,
|
|
withBenchmark: 0,
|
|
cheaper: 0,
|
|
competitive: 0,
|
|
expensive: 0,
|
|
totalPotentialGain: 0
|
|
};
|
|
|
|
if (data.products.length === 0) {
|
|
html = '<tr><td colspan="6">No in-stock products with SKU found.</td></tr>';
|
|
} else {
|
|
$.each(data.products, function(i, product) {
|
|
stats.total++;
|
|
|
|
var localPrice = product.local_price ? data.currency + parseFloat(product.local_price).toFixed(2) : '-';
|
|
var benchmarkPrice = product.benchmark_price ? data.currency + parseFloat(product.benchmark_price).toFixed(2) : '-';
|
|
var recommendedPrice = product.recommended_price ? data.currency + parseFloat(product.recommended_price).toFixed(2) : '-';
|
|
var potentialGain = '-';
|
|
var priceLabel = product.price_type === 'sale' ? ' <small>(sale)</small>' : '';
|
|
|
|
// Status and colors.
|
|
var statusText = '-';
|
|
var statusStyle = '';
|
|
var localPriceStyle = '';
|
|
var gainStyle = '';
|
|
|
|
if (product.has_benchmark) {
|
|
stats.withBenchmark++;
|
|
|
|
if (product.price_status === 'cheaper') {
|
|
stats.cheaper++;
|
|
statusText = 'Cheaper';
|
|
statusStyle = 'color: #00a32a; font-weight: bold;';
|
|
localPriceStyle = 'color: #00a32a;';
|
|
} else if (product.price_status === 'competitive') {
|
|
stats.competitive++;
|
|
statusText = 'Competitive';
|
|
statusStyle = 'color: #2271b1; font-weight: bold;';
|
|
localPriceStyle = 'color: #2271b1;';
|
|
} else if (product.price_status === 'expensive') {
|
|
stats.expensive++;
|
|
statusText = 'Expensive';
|
|
statusStyle = 'color: #d63638; font-weight: bold;';
|
|
localPriceStyle = 'color: #d63638;';
|
|
}
|
|
|
|
if (product.potential_gain !== null) {
|
|
var gain = parseFloat(product.potential_gain);
|
|
stats.totalPotentialGain += gain;
|
|
if (gain > 0) {
|
|
potentialGain = '+' + data.currency + gain.toFixed(2);
|
|
gainStyle = 'color: #00a32a; font-weight: bold;';
|
|
} else if (gain < 0) {
|
|
potentialGain = '-' + data.currency + Math.abs(gain).toFixed(2);
|
|
gainStyle = 'color: #d63638;';
|
|
} else {
|
|
potentialGain = data.currency + '0.00';
|
|
}
|
|
}
|
|
} else if (product.found) {
|
|
statusText = 'No benchmark';
|
|
statusStyle = 'color: #dba617;';
|
|
} else {
|
|
statusText = 'Not in Google';
|
|
statusStyle = 'color: #888;';
|
|
}
|
|
|
|
html += '<tr>';
|
|
html += '<td><a href="post.php?post=' + product.id + '&action=edit" title="SKU: ' + product.sku + '">' + product.name + '</a></td>';
|
|
html += '<td style="' + localPriceStyle + '">' + localPrice + priceLabel + '</td>';
|
|
html += '<td>' + benchmarkPrice + '</td>';
|
|
html += '<td>' + recommendedPrice + '</td>';
|
|
html += '<td style="' + gainStyle + '">' + potentialGain + '</td>';
|
|
html += '<td style="' + statusStyle + '">' + statusText + '</td>';
|
|
html += '</tr>';
|
|
});
|
|
}
|
|
|
|
$tbody.html(html);
|
|
$results.show();
|
|
|
|
// Show summary with statistics.
|
|
var $summary = $('#informatiq-sp-comparison-summary');
|
|
var summaryHtml = '<strong>Summary:</strong> ';
|
|
summaryHtml += stats.withBenchmark + ' of ' + stats.total + ' products have competitor data. ';
|
|
summaryHtml += '<span style="color:#00a32a;">' + stats.cheaper + ' cheaper</span>, ';
|
|
summaryHtml += '<span style="color:#2271b1;">' + stats.competitive + ' competitive</span>, ';
|
|
summaryHtml += '<span style="color:#d63638;">' + stats.expensive + ' expensive</span>. ';
|
|
if (stats.totalPotentialGain > 0) {
|
|
summaryHtml += '<br><strong style="color:#00a32a;">Total potential gain if optimized: +' + data.currency + stats.totalPotentialGain.toFixed(2) + ' per sale</strong>';
|
|
} else if (stats.totalPotentialGain < 0) {
|
|
summaryHtml += '<br><strong>Current margin vs recommended: ' + data.currency + stats.totalPotentialGain.toFixed(2) + '</strong>';
|
|
}
|
|
$summary.html(summaryHtml).show();
|
|
|
|
} else {
|
|
$status
|
|
.removeClass('notice-info notice-success')
|
|
.addClass('notice-error')
|
|
.html('<p>Error: ' + (response.data.message || 'Unknown error') + '</p>')
|
|
.show();
|
|
}
|
|
},
|
|
error: function(jqXHR, textStatus, errorThrown) {
|
|
console.error('AJAX error:', textStatus, errorThrown, jqXHR.responseText);
|
|
$status
|
|
.removeClass('notice-info notice-success')
|
|
.addClass('notice-error')
|
|
.html('<p>Error: ' + errorThrown + ' - ' + textStatus + '</p>')
|
|
.show();
|
|
},
|
|
complete: function() {
|
|
console.log('AJAX request complete');
|
|
$button.prop('disabled', false);
|
|
$spinner.removeClass('is-active');
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// Initialize when document is ready
|
|
$(document).ready(function() {
|
|
InformatiqSP.init();
|
|
});
|
|
|
|
})(jQuery);
|