/**
* Zoho CRM Admin JavaScript
*
* @package HVACCommunityEvents
*/
jQuery(document).ready(function ($) {
// =====================================================
// Password visibility toggle
// =====================================================
$('#toggle-secret').on('click', function () {
var passwordField = $('#zoho_client_secret');
var toggleBtn = $(this);
if (passwordField.attr('type') === 'password') {
passwordField.attr('type', 'text');
toggleBtn.text('Hide');
} else {
passwordField.attr('type', 'password');
toggleBtn.text('Show');
}
});
// =====================================================
// Copy redirect URI to clipboard
// =====================================================
$('#copy-redirect-uri').on('click', function () {
var redirectUri = hvacZoho.redirectUri || '';
if (!redirectUri) {
alert('Redirect URI not available');
return;
}
navigator.clipboard.writeText(redirectUri).then(function () {
$('#copy-redirect-uri').text('Copied!').prop('disabled', true);
setTimeout(function () {
$('#copy-redirect-uri').text('Copy').prop('disabled', false);
}, 2000);
}).catch(function () {
// Fallback for older browsers
var tempInput = $(' ');
$('body').append(tempInput);
tempInput.val(redirectUri).select();
document.execCommand('copy');
tempInput.remove();
$('#copy-redirect-uri').text('Copied!').prop('disabled', true);
setTimeout(function () {
$('#copy-redirect-uri').text('Copy').prop('disabled', false);
}, 2000);
});
});
// =====================================================
// Flush rewrite rules
// =====================================================
$('#flush-rewrite-rules').on('click', function () {
var button = $(this);
button.prop('disabled', true).text('Flushing...');
$.post(hvacZoho.ajaxUrl, {
action: 'hvac_zoho_flush_rewrite_rules'
}, function (response) {
if (response.success) {
button.text('Flushed!').css('color', '#46b450');
setTimeout(function () {
location.reload();
}, 1000);
} else {
button.text('Error').css('color', '#dc3232');
setTimeout(function () {
button.prop('disabled', false).text('Flush Rules').css('color', '');
}, 2000);
}
});
});
// =====================================================
// Credentials form submission
// =====================================================
// =====================================================
// Credentials form submission
// =====================================================
$('#zoho-credentials-form').on('submit', function (e) {
e.preventDefault();
var formData = {
action: 'hvac_zoho_save_credentials',
zoho_client_id: $('#zoho_client_id').val(),
zoho_client_secret: $('#zoho_client_secret').val(),
nonce: $('input[name="hvac_zoho_nonce"]').val()
};
var $saveBtn = $('#save-credentials');
$saveBtn.prop('disabled', true).text('Saving...');
// Clear any previous messages
$('.notice').remove();
$.ajax({
url: hvacZoho.ajaxUrl,
method: 'POST',
data: formData,
success: function (response) {
if (response.success) {
window.location.href = window.location.href.split('?')[0] + '?page=hvac-zoho-sync&credentials_saved=1';
} else {
// Create error notice
var errorHtml = '
' +
'Error saving credentials: ' + response.data.message +
'
';
$('h1').after(errorHtml);
$saveBtn.prop('disabled', false).text('Save Credentials');
}
},
error: function (xhr, status, error) {
console.error('Zoho Save Error:', xhr);
var errorMessage = 'Unknown error occurred';
var errorDetails = '';
if (xhr.status === 400 || xhr.status === 403) {
errorMessage = 'Request blocked (' + xhr.status + ')';
errorDetails = 'This is likely due to a security plugin or Web Application Firewall (WAF) blocking the request. ' +
'The content (e.g. Client Secret) might be triggering a false positive security rule.';
} else if (xhr.status === 500) {
errorMessage = 'Server Error (500)';
errorDetails = 'Check the server error logs for more information.';
} else if (status === 'timeout') {
errorMessage = 'Request timed out';
} else if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) {
errorMessage = xhr.responseJSON.data.message;
} else {
errorMessage = error || status;
}
var errorHtml = '' +
'
❌ Save Failed: ' + errorMessage + '
';
if (errorDetails) {
errorHtml += '
' + errorDetails + '
';
}
// Add debug details
errorHtml += '
' +
'Technical Details ' +
'' +
'Status: ' + xhr.status + ' ' + xhr.statusText + ' ' +
'Response: ' + (xhr.responseText ? xhr.responseText.substring(0, 100) + '...' : '(empty)') + ' ' +
' ' +
' ';
$('h1').after(errorHtml);
// Re-enable button
$saveBtn.prop('disabled', false).text('Save Credentials');
// Scroll to error
$('html, body').animate({ scrollTop: 0 }, 'slow');
}
});
});
// =====================================================
// OAuth authorization handler
// =====================================================
$('#start-oauth').on('click', function () {
var clientId = $('#zoho_client_id').val();
var clientSecret = $('#zoho_client_secret').val();
if (!clientId || !clientSecret) {
alert('Please save your credentials first before starting OAuth authorization.');
return;
}
// Use server-generated OAuth URL with CSRF state parameter
var oauthUrl = hvacZoho.oauthUrl || '';
if (!oauthUrl) {
alert('OAuth URL not available. Please save your credentials first and refresh the page.');
return;
}
// Open OAuth URL in the same window to handle callback properly
window.location.href = oauthUrl;
});
// =====================================================
// Test connection
// =====================================================
$('#test-connection').on('click', function () {
var $button = $(this);
var $status = $('#connection-status');
$button.prop('disabled', true).text('Testing...');
$status.html('');
$.ajax({
url: hvacZoho.ajaxUrl,
method: 'POST',
data: {
action: 'hvac_zoho_test_connection',
nonce: hvacZoho.nonce
},
success: function (response) {
if (response.success) {
var successHtml = '';
successHtml += '
' + response.data.message + '
';
// Show credential details
if (response.data.client_id) {
successHtml += '
Client ID: ' + response.data.client_id + '
';
}
if (response.data.client_secret_exists) {
successHtml += '
Client Secret: ✓ Loaded
';
}
if (response.data.refresh_token_exists) {
successHtml += '
Refresh Token: ✓ Found
';
} else {
successHtml += '
Refresh Token: ❌ Missing (OAuth required)
';
}
// Show staging warning if present
if (response.data.staging_warning) {
successHtml += '
';
successHtml += '
Warning: ' + response.data.staging_warning + '
';
successHtml += '
';
}
// Show debug info if available
if (response.data.debug) {
successHtml += '
';
successHtml += 'Debug Information ';
successHtml += '';
successHtml += JSON.stringify(response.data.debug, null, 2);
successHtml += ' ';
}
successHtml += '
';
$status.html(successHtml);
} else {
// Debug: Log the response to see what we're getting
console.log('Error response:', response);
console.log('Message:', response.data.message);
console.log('Has auth_url:', !!response.data.auth_url);
// Handle OAuth authorization case specially
if (response.data.message === 'OAuth Authorization Required' && response.data.auth_url) {
var authHtml = '';
authHtml += '
🔐 OAuth Authorization Required ';
authHtml += '
' + response.data.details + '
';
if (response.data.next_steps) {
authHtml += '
';
response.data.next_steps.forEach(function (step) {
authHtml += '' + step + ' ';
});
authHtml += ' ';
}
authHtml += '
🚀 Authorize with Zoho CRM
';
// Show credential status
if (response.data.credentials_status) {
authHtml += '
Current Status:
';
authHtml += '
';
authHtml += 'Client ID: ' + response.data.credentials_status.client_id + ' ';
authHtml += 'Client Secret: ' + (response.data.credentials_status.client_secret_exists ? '✓ Loaded' : '❌ Missing') + ' ';
authHtml += 'Refresh Token: ' + (response.data.credentials_status.refresh_token_exists ? '✓ Found' : '❌ Missing') + ' ';
authHtml += ' ';
}
authHtml += '
After authorization, come back and test the connection again.
';
authHtml += '
';
$status.html(authHtml);
return;
} else {
console.log('OAuth conditions not met:');
console.log('Message matches:', response.data.message === 'OAuth Authorization Required');
console.log('Auth URL exists:', !!response.data.auth_url);
}
// Create detailed error display for other errors
var errorHtml = '';
errorHtml += '
' + response.data.message + ': ' + response.data.error + '
';
// Add error code if available
if (response.data.code) {
errorHtml += '
Error Code: ' + response.data.code + '
';
}
// Add details if available
if (response.data.details) {
errorHtml += '
Details: ' + response.data.details + '
';
}
// Add debugging info
errorHtml += '
';
errorHtml += '
Debug Information:
';
errorHtml += '
Check the PHP error log for more details.
';
errorHtml += '
Log location: wp-content/plugins/hvac-community-events/logs/zoho-debug.log
';
// Add raw response data if available
if (response.data.raw) {
errorHtml += '
';
errorHtml += 'Raw Response Data (click to expand) ';
errorHtml += '' +
JSON.stringify(JSON.parse(response.data.raw), null, 2) +
' ';
errorHtml += ' ';
}
// Add file/line info if available (for exceptions)
if (response.data.file) {
errorHtml += '
File: ' + response.data.file + '
';
}
// Add trace if available
if (response.data.trace) {
errorHtml += '
';
errorHtml += 'Stack Trace (click to expand) ';
errorHtml += '' +
response.data.trace +
' ';
errorHtml += ' ';
}
errorHtml += '
'; // Close debug info
errorHtml += '
'; // Close notice
$status.html(errorHtml);
}
},
error: function (xhr, status, error) {
var errorHtml = '';
errorHtml += '
AJAX Error: Connection test failed
';
errorHtml += '
Status: ' + status + '
';
errorHtml += '
Error: ' + error + '
';
errorHtml += '
';
$status.html(errorHtml);
},
complete: function () {
$button.prop('disabled', false).text('Test Connection');
}
});
});
// =====================================================
// Sync data with batch progress
// =====================================================
/**
* Sync with progress - auto-continues through all batches
* @param {jQuery} $button - The sync button
* @param {string} type - Sync type (events, users, attendees, rsvps, purchases)
* @param {jQuery} $status - Status container element
* @param {number} offset - Current offset
* @param {object} accumulated - Accumulated results across batches
*/
function syncWithProgress($button, type, $status, offset, accumulated) {
accumulated = accumulated || {
synced: 0,
failed: 0,
errors: [],
total: 0,
staging_mode: false,
responses: [],
test_data: []
};
$.ajax({
url: hvacZoho.ajaxUrl,
method: 'POST',
data: {
action: 'hvac_zoho_sync_data',
type: type,
offset: offset,
nonce: hvacZoho.nonce
},
success: function (response) {
if (response.success) {
var result = response.data;
// Update accumulated totals
accumulated.total = result.total; // Total is consistent across batches
accumulated.synced += result.synced;
accumulated.failed += result.failed;
accumulated.staging_mode = result.staging_mode;
// Merge arrays
if (result.errors && result.errors.length > 0) {
accumulated.errors = accumulated.errors.concat(result.errors);
}
if (result.responses && result.responses.length > 0) {
accumulated.responses = accumulated.responses.concat(result.responses);
}
if (result.test_data && result.test_data.length > 0) {
accumulated.test_data = accumulated.test_data.concat(result.test_data);
}
// Calculate progress
var processed = accumulated.synced + accumulated.failed;
var percent = accumulated.total > 0 ? Math.round((processed / accumulated.total) * 100) : 0;
// Update progress bar
var progressHtml = '' +
'
' +
'
' +
'' + processed + ' of ' + accumulated.total + ' processed (' + percent + '%)' +
'
';
$status.html(progressHtml);
// Check if there are more batches
if (result.has_more && result.next_offset > offset) {
// Continue with next batch
syncWithProgress($button, type, $status, result.next_offset, accumulated);
} else {
// All done! Show final results
displaySyncResults($button, type, $status, accumulated, result);
}
} else {
$status.html('' + response.data.message + ': ' + response.data.error + '
');
$button.prop('disabled', false).text('Sync ' + type.charAt(0).toUpperCase() + type.slice(1));
}
},
error: function () {
$status.html('Sync failed - network or server error
');
$button.prop('disabled', false).text('Sync ' + type.charAt(0).toUpperCase() + type.slice(1));
}
});
}
/**
* Display final sync results
*/
function displaySyncResults($button, type, $status, accumulated, lastResult) {
var html = '';
if (accumulated.staging_mode) {
html += '
🔧 STAGING MODE - Simulation Complete ';
html += '
No data was sent to Zoho CRM. This is a dry-run showing what would sync.
';
} else {
html += '
✅ Sync completed successfully!
';
}
if (lastResult.version) {
html += '
Server Code Version: ' + lastResult.version + '
';
}
html += '
' +
'Total records: ' + accumulated.total + ' ' +
'Synced: ' + accumulated.synced + ' ' +
'Failed: ' + accumulated.failed + ' ' +
' ';
// Show test data for staging
if (accumulated.test_data && accumulated.test_data.length > 0) {
html += '
' +
'View test data (first 5 records) ' +
'' +
JSON.stringify(accumulated.test_data.slice(0, 5), null, 2) +
' ' +
' ';
}
// Debug info
if (lastResult.debug_info) {
html += '
' +
'🔍 Debug: Mode Detection Info ' +
'';
if (typeof lastResult.debug_info.is_staging !== 'undefined') {
html += '
Is Staging: ' + (lastResult.debug_info.is_staging ? '✅ YES' : '❌ NO') + '
';
}
if (lastResult.debug_info.site_url) {
html += '
Site URL: ' + lastResult.debug_info.site_url + '
';
}
html += '
' +
JSON.stringify(lastResult.debug_info, null, 2) +
' ' +
'
' +
' ';
}
// Show errors if any
if (accumulated.errors && accumulated.errors.length > 0) {
html += '
' +
'❌ Errors (' + accumulated.errors.length + ') ' +
'' +
JSON.stringify(accumulated.errors.slice(0, 20), null, 2) +
' ' +
' ';
}
// Show API responses preview
if (accumulated.responses && accumulated.responses.length > 0) {
html += '
' +
'📡 Raw API Responses (first 10) ' +
'' +
JSON.stringify(accumulated.responses.slice(0, 10), null, 2) +
' ' +
' ';
}
html += '
';
$status.html(html);
$button.prop('disabled', false).text('Sync ' + type.charAt(0).toUpperCase() + type.slice(1));
}
// Sync button click handler
$('.sync-button').on('click', function () {
var $button = $(this);
var type = $button.data('type');
var $status = $('#' + type + '-status');
$button.prop('disabled', true).text('Syncing...');
$status.html('Starting sync for ' + type + '...
');
// Start sync with batch progress
syncWithProgress($button, type, $status, 0, null);
});
// Save settings
$('#zoho-settings-form').on('submit', function (e) {
e.preventDefault();
var $form = $(this);
var $button = $form.find('button[type="submit"]');
$button.prop('disabled', true).text('Saving...');
$.ajax({
url: hvacZoho.ajaxUrl,
method: 'POST',
data: {
action: 'hvac_zoho_save_settings',
nonce: hvacZoho.nonce,
auto_sync: $form.find('input[name="auto_sync"]').is(':checked') ? '1' : '0',
sync_frequency: $form.find('select[name="sync_frequency"]').val()
},
success: function (response) {
if (response.success) {
// Reload page to show updated status
window.location.reload();
} else {
// Use toast notification instead of alert
if (window.HVACToast) {
HVACToast.error('Error saving settings: ' + response.data.message);
} else {
alert('Error saving settings: ' + response.data.message);
}
$button.prop('disabled', false).text('Save Settings');
}
},
error: function () {
// Use toast notification instead of alert
if (window.HVACToast) {
HVACToast.error('Error saving settings');
} else {
alert('Error saving settings');
}
$button.prop('disabled', false).text('Save Settings');
}
});
});
// =====================================================
// Run Scheduled Sync Now Handler
// =====================================================
$('#run-scheduled-sync-now').on('click', function () {
var $button = $(this);
var $status = $('#scheduled-sync-status');
$button.prop('disabled', true).text('Running...');
$status.html('Starting scheduled sync...
');
$.ajax({
url: hvacZoho.ajaxUrl,
method: 'POST',
data: {
action: 'hvac_zoho_run_scheduled_sync',
nonce: hvacZoho.nonce
},
success: function (response) {
if (response.success) {
var result = response.data.result;
var html = '';
if (result.events && result.events.staging_mode) {
html += '
🔧 STAGING MODE - Simulation Complete ';
html += '
No data was sent to Zoho CRM.
';
} else {
html += '
✅ Scheduled sync completed!
';
}
html += '
';
html += 'Total synced: ' + (result.total_synced || 0) + ' ';
html += 'Total failed: ' + (result.total_failed || 0) + ' ';
html += 'Duration: ' + (result.duration_seconds || 0) + ' seconds ';
html += ' ';
// Show details per type
html += '
Details by type ';
['events', 'users', 'attendees', 'rsvps', 'purchases'].forEach(function (type) {
if (result[type]) {
html += '' + type + ': ' +
(result[type].synced || 0) + ' synced, ' +
(result[type].failed || 0) + ' failed ';
}
});
html += ' ';
html += '
';
$status.html(html);
} else {
$status.html('Sync failed: ' + response.data.message + '
');
}
},
error: function () {
$status.html('Error running scheduled sync
');
},
complete: function () {
$button.prop('disabled', false).text('🔄 Run Sync Now');
}
});
});
// =====================================================
// Reset Sync Hashes Handler
// =====================================================
$('#reset-sync-hashes').on('click', function () {
if (!confirm('This will clear all sync hashes and force every record to re-sync on the next run. Continue?')) {
return;
}
var $button = $(this);
var $status = $('#reset-hashes-status');
$button.prop('disabled', true).text('Resetting...');
$status.text('');
$.ajax({
url: hvacZoho.ajaxUrl,
method: 'POST',
data: {
action: 'hvac_zoho_reset_sync_hashes',
nonce: hvacZoho.nonce
},
success: function (response) {
if (response.success) {
$status.html(
'' +
response.data.posts_cleared + ' post hashes and ' +
response.data.users_cleared + ' user hashes cleared. '
);
} else {
$status.html('Error: ' + response.data.message + ' ');
}
},
error: function () {
$status.html('Network error - please try again ');
},
complete: function () {
$button.prop('disabled', false).text('Force Full Re-sync (Reset Hashes)');
}
});
});
// =====================================================
// Diagnostic Test Handler
// =====================================================
$('#diagnostic-test').on('click', function () {
var $button = $(this);
$button.prop('disabled', true).text('Testing...');
// Remove existing notices
$('.notice').remove();
// Test 1: Simple GET
var runSimpleTest = function () {
return $.ajax({
url: hvacZoho.ajaxUrl,
method: 'POST',
data: {
action: 'hvac_zoho_simple_test'
}
});
};
// Test 2: Payload Test (simulates credentials)
var runPayloadTest = function () {
var fakeId = '1000.' + new Array(20).join('a');
var fakeSecret = new Array(30).join('b');
return $.ajax({
url: hvacZoho.ajaxUrl,
method: 'POST',
data: {
action: 'hvac_zoho_simple_test',
test_payload: 'SIMULATED_CREDENTIALS',
zoho_client_id: fakeId,
zoho_client_secret: fakeSecret
}
});
};
// Execute tests sequence
runSimpleTest()
.then(function (response) {
if (response.success) {
console.log('Simple test passed');
return runPayloadTest();
} else {
return $.Deferred().reject({ status: 200, statusText: 'OK', responseJSON: response });
}
})
.then(function (response) {
if (response.success) {
// Success!
var successHtml = '' +
'
✅ Diagnostic Test Passed
' +
'
AJAX requests are working correctly. No WAF blocking detected for credential-like data.
' +
'
';
$('h1').after(successHtml);
} else {
return $.Deferred().reject({ status: 200, statusText: 'OK', responseJSON: response });
}
})
.fail(function (xhr) {
var errorHtml = '' +
'
❌ Diagnostic Test Failed
';
if (xhr.status === 400 || xhr.status === 403) {
errorHtml += '
WAF Blocking Detected!
';
errorHtml += '
The server returned ' + xhr.status + ' when sending data.
';
} else {
errorHtml += '
Status: ' + (xhr.status || 'Unknown') + '
';
if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) {
errorHtml += '
Message: ' + xhr.responseJSON.data.message + '
';
}
}
errorHtml += '
';
$('h1').after(errorHtml);
})
.always(function () {
$button.prop('disabled', false).text('🏥 Run Diagnostic Test');
});
});
});