/** * 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' + '' + '
'; $('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 += '
  1. ' + step + '
  2. '; }); authHtml += '
'; } authHtml += '

🚀 Authorize with Zoho CRM

'; // Show credential status if (response.data.credentials_status) { authHtml += '

Current Status:

'; 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 += ''; // 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 += ''; // Show details per type html += '
Details by type
'; 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'); }); }); });