jQuery(document).ready(function($) { // ======================================== // QUEUE PROCESSING SYSTEM // ======================================== let isProcessingQueue = false; let currentQueueId = null; let currentQueueType = null; // Check if we should start queue processing on page load checkForActiveQueues(); // Form submission handler - triggers queue processing $('#mxchat-url-form').on('submit', function(e) { //console.log('MxChat: Form submitted, queue will be created'); // Don't prevent default - let form submit normally // But schedule a check after redirect localStorage.setItem('mxchat_check_queue_after_submit', Date.now().toString()); }); // Check if we just submitted a form and need to start processing const justSubmitted = localStorage.getItem('mxchat_check_queue_after_submit'); if (justSubmitted) { const submitTime = parseInt(justSubmitted); const now = Date.now(); // If submitted within last 10 seconds, wait for queue to be created if (now - submitTime < 10000) { //console.log('MxChat: Form was just submitted, waiting for queue creation...'); localStorage.removeItem('mxchat_check_queue_after_submit'); // Show processing message if ($('.mxchat-processing-message').length === 0) { const message = $('
⏳ Queue created! Processing will start in a moment...
'); $('.mxchat-import-section').after(message); } // Check for queue multiple times with increasing delays setTimeout(function() { checkForActiveQueues(); }, 1000); setTimeout(function() { checkForActiveQueues(); }, 2000); setTimeout(function() { checkForActiveQueues(); }, 3000); setTimeout(function() { checkForActiveQueues(); }, 5000); } } function checkForActiveQueues() { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_get_status_updates', nonce: mxchatAdmin.status_nonce }, success: function(response) { //console.log('MxChat: Checking for active queues...', response); if (response.sitemap_queue_id && response.sitemap_status) { if (response.sitemap_status.status === 'processing') { //console.log('MxChat: Found active sitemap queue:', response.sitemap_queue_id); startQueueProcessing(response.sitemap_queue_id, 'sitemap'); } else if (response.sitemap_status.status === 'complete') { //console.log('MxChat: Sitemap queue already complete'); // ADD THIS LINE: $('.mxchat-processing-message').remove(); // Show completed status card (no auto-refresh) updateSitemapStatus(response.sitemap_status); } } if (response.pdf_queue_id && response.pdf_status) { if (response.pdf_status.status === 'processing') { //console.log('MxChat: Found active PDF queue:', response.pdf_queue_id); startQueueProcessing(response.pdf_queue_id, 'pdf'); } else if (response.pdf_status.status === 'complete') { //console.log('MxChat: PDF queue already complete'); // ADD THIS LINE: $('.mxchat-processing-message').remove(); // Show completed status card (no auto-refresh) updatePdfStatus(response.pdf_status); } } // ADD THIS: If no queues found at all, remove the message if (!response.sitemap_queue_id && !response.pdf_queue_id) { $('.mxchat-processing-message').remove(); } }, error: function(xhr, status, error) { console.error('MxChat: Error checking for active queues:', error); // ADD THIS: Remove message on error too $('.mxchat-processing-message').remove(); } }); } /** * Start processing a queue */ function startQueueProcessing(queueId, queueType) { if (isProcessingQueue) { //console.log('MxChat: Already processing a queue, skipping'); return; } isProcessingQueue = true; currentQueueId = queueId; currentQueueType = queueType; //console.log('MxChat: Starting queue processing:', queueId, queueType); // Remove any "waiting" messages $('.mxchat-processing-message').remove(); // Create or update status card createOrUpdateStatusCard(queueType); // Start the processing loop processNextQueueItem(); } /** * Process the next item in the queue */ function processNextQueueItem() { if (!isProcessingQueue) { //console.log('MxChat: Processing stopped'); return; } // Get next item from queue $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_get_next_queue_item', nonce: mxchatAdmin.queue_nonce, queue_id: currentQueueId }, success: function(response) { if (!response.success) { console.error('MxChat: Error getting next queue item:', response.data); // Check if queue is actually complete despite error verifyQueueCompletion(); return; } if (response.data.complete) { // Queue is complete! //console.log('MxChat: Queue processing complete!'); handleQueueComplete(); return; } // Process this item const item = response.data.item; //console.log('MxChat: Processing item:', item.type, item.id); processQueueItem(item); }, error: function(xhr, status, error) { console.error('MxChat: AJAX error getting next item:', error); // Network error - verify queue status before retrying setTimeout(function() { verifyQueueCompletion(); }, 2000); } }); } /** * Verify if queue is actually complete * Prevents infinite loops when last item fails */ function verifyQueueCompletion() { //console.log('MxChat: Verifying queue completion status...'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_get_queue_status', nonce: mxchatAdmin.queue_nonce, queue_id: currentQueueId }, success: function(response) { if (response.success) { const status = response.data; // If no pending or processing items, queue is done if (status.pending === 0 && status.processing === 0) { //console.log('MxChat: Queue verified as complete'); handleQueueComplete(); } else { // Still has items, try to continue //console.log('MxChat: Queue still has pending items, continuing...'); processNextQueueItem(); } } else { // Can't verify, assume complete to prevent infinite loop //console.log('MxChat: Could not verify queue status, assuming complete'); handleQueueComplete(); } }, error: function() { // Can't verify, assume complete to prevent infinite loop //console.log('MxChat: Network error verifying queue, assuming complete'); handleQueueComplete(); } }); } /** * Process a single queue item * Never stops the queue - always continues regardless of success/failure */ function processQueueItem(item) { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_process_queue_item', nonce: mxchatAdmin.queue_nonce, item_id: item.id, item_type: item.type, item_data: item.data, bot_id: item.bot_id }, success: function(response) { if (response.success) { // Item processed successfully //console.log('MxChat: Item processed successfully:', item.id); // Update progress updateQueueProgress(); // Small delay to prevent server overload, then process next setTimeout(function() { processNextQueueItem(); }, 500); // 500ms delay between items } else { // Item failed but we KEEP GOING console.warn('MxChat: Item processing failed (will continue):', item.type, item.id); console.warn('MxChat: Error details:', response.data); // Update progress to reflect the attempt updateQueueProgress(); // Continue to next item regardless setTimeout(function() { processNextQueueItem(); }, 500); } }, error: function(xhr, status, error) { // Network error - log it but KEEP GOING console.error('MxChat: AJAX/Network error processing item:', item.id, error); // Update progress updateQueueProgress(); // Wait a bit longer for network errors, then continue setTimeout(function() { processNextQueueItem(); }, 1000); } }); } /** * Update queue progress (fetches latest stats) */ function updateQueueProgress() { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_get_queue_status', nonce: mxchatAdmin.queue_nonce, queue_id: currentQueueId }, success: function(response) { if (response.success) { const status = response.data; // Update the appropriate status card if (currentQueueType === 'pdf') { updatePdfStatusFromQueue(status); } else { updateSitemapStatusFromQueue(status); } } } }); } /** * Handle queue completion * NO AUTO-REFRESH - Show completed card with errors until dismissed */ function handleQueueComplete() { isProcessingQueue = false; //console.log('MxChat: Queue processing completed - showing final results'); // Get final status with error details $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_get_queue_status', nonce: mxchatAdmin.queue_nonce, queue_id: currentQueueId }, success: function(response) { if (response.success) { const status = response.data; // Show completed status card (NO REFRESH) if (currentQueueType === 'pdf') { showCompletedPdfCard(status); } else { showCompletedSitemapCard(status); } // Mark the queue as complete on server markQueueAsComplete(currentQueueId); // Show notification based on results if (status.failed > 0) { showNotification('warning', `Processing completed: ${status.completed} succeeded, ${status.failed} failed. ` + `Review errors below and dismiss when ready.` ); } else { showNotification('success', `Processing completed successfully! All ${status.completed} items processed. ` + `Dismiss the status card when ready.` ); } // NO AUTO-REFRESH - User must manually dismiss } else { // Couldn't get final status, just show generic completion showNotification('success', 'Processing completed! Refresh page to see final results.'); } }, error: function() { // Error getting final status showNotification('success', 'Processing completed! Refresh page to see final results.'); } }); } /** * Show completed PDF card with full error details (NO RETRY BUTTON) */ function showCompletedPdfCard(status) { let $card = $('.mxchat-status-card:contains("PDF Processing")'); if ($card.length === 0) { return; } // Remove processing UI elements $card.find('.mxchat-stop-form').remove(); $card.find('.mxchat-status-warning').remove(); // Update header with completion badge $card.find('.mxchat-status-badge').remove(); if (status.failed > 0) { $card.find('.mxchat-status-header h4').after( '⚠️ Completed with ' + status.failed + ' failures - Refresh to view entries' ); } else { $card.find('.mxchat-status-header h4').after( '✓ Complete - Refresh to view entries' ); } // Add dismiss button addDismissButton($card); // Update progress bar to 100% $card.find('.mxchat-progress-fill').css('width', '100%'); // Update details with final stats let detailsHtml = '
'; detailsHtml += '

📊 Final Results

'; detailsHtml += '

Total Pages: ' + status.total + '

'; detailsHtml += '

✓ Successfully Processed: ' + status.completed + '

'; if (status.failed > 0) { detailsHtml += '

✗ Failed: ' + status.failed + '

'; } detailsHtml += '
'; // Add error details if there are failures (NO RETRY BUTTON) if (status.failed > 0 && status.failed_items && status.failed_items.length > 0) { detailsHtml += '
'; detailsHtml += '

⚠️ Failed Pages

'; detailsHtml += '
'; detailsHtml += 'Click to view ' + status.failed_items.length + ' failed pages'; detailsHtml += '
'; detailsHtml += ''; detailsHtml += ''; detailsHtml += ''; status.failed_items.forEach(function(item) { const data = JSON.parse(item.item_data); const pageNum = data.page_number || 'Unknown'; detailsHtml += ''; detailsHtml += ''; detailsHtml += ''; detailsHtml += ''; detailsHtml += ''; }); detailsHtml += '
PageErrorAttempts
Page ' + pageNum + '' + (item.error_message || 'Unknown error') + '' + item.attempts + '
'; detailsHtml += '
'; detailsHtml += '
'; detailsHtml += '
'; } $card.find('.mxchat-status-details').html(detailsHtml); } /** * Show completed sitemap card with full error details (NO RETRY BUTTON) */ function showCompletedSitemapCard(status) { let $card = $('.mxchat-status-card:contains("Sitemap Processing")'); if ($card.length === 0) { return; } // Remove processing UI elements $card.find('.mxchat-stop-form').remove(); $card.find('.mxchat-status-warning').remove(); // Update header with completion badge $card.find('.mxchat-status-badge').remove(); if (status.failed > 0) { $card.find('.mxchat-status-header h4').after( '⚠️ Completed with ' + status.failed + ' failures - Refresh to view entries' ); } else { $card.find('.mxchat-status-header h4').after( '✓ Complete - Refresh to view entries' ); } // Add dismiss button addDismissButton($card); // Update progress bar to 100% $card.find('.mxchat-progress-fill').css('width', '100%'); // Update details with final stats let detailsHtml = '
'; detailsHtml += '

📊 Final Results

'; detailsHtml += '

Total URLs: ' + status.total + '

'; detailsHtml += '

✓ Successfully Processed: ' + status.completed + '

'; if (status.failed > 0) { detailsHtml += '

✗ Failed: ' + status.failed + '

'; } detailsHtml += '
'; // Add error details if there are failures (NO RETRY BUTTON) if (status.failed > 0 && status.failed_items && status.failed_items.length > 0) { detailsHtml += '
'; detailsHtml += '

⚠️ Failed URLs

'; detailsHtml += '
'; detailsHtml += 'Click to view ' + status.failed_items.length + ' failed URLs'; detailsHtml += '
'; detailsHtml += ''; detailsHtml += ''; detailsHtml += ''; status.failed_items.forEach(function(item) { const data = JSON.parse(item.item_data); const url = data.url || 'Unknown URL'; const displayUrl = url.length > 60 ? url.substring(0, 57) + '...' : url; detailsHtml += ''; detailsHtml += ''; detailsHtml += ''; detailsHtml += ''; detailsHtml += ''; }); detailsHtml += '
URLErrorAttempts
' + displayUrl + '' + (item.error_message || 'Unknown error') + '' + item.attempts + '
'; detailsHtml += '
'; detailsHtml += '
'; detailsHtml += '
'; } $card.find('.mxchat-status-details').html(detailsHtml); } /** * Mark queue as complete on server side * This prevents it from auto-starting on page refresh */ function markQueueAsComplete(queueId) { // This is a fire-and-forget call to update queue status $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_mark_queue_complete', nonce: mxchatAdmin.queue_nonce, queue_id: queueId }, success: function(response) { //console.log('MxChat: Queue marked as complete on server'); }, error: function() { //console.log('MxChat: Could not mark queue as complete, but continuing'); } }); } /** * Stop processing button handler */ $(document).on('submit', '.mxchat-stop-form', function() { //console.log('MxChat: Stop processing requested'); isProcessingQueue = false; currentQueueId = null; currentQueueType = null; }); /** * Create or update status card */ function createOrUpdateStatusCard(queueType) { const cardTitle = queueType === 'pdf' ? 'PDF Processing Status' : 'Sitemap Processing Status'; let $card = $('.mxchat-status-card:contains("' + cardTitle + '")'); if ($card.length === 0) { // Create new card let html = '
'; html += '
'; html += '

' + cardTitle + '

'; html += '
'; html += '⚠️ Keep this tab open - Processing runs in your browser'; html += '
'; html += '
'; html += ''; html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; html += '

Initializing...

'; html += '
'; html += '
'; // Insert card let $importSection = $('.mxchat-import-section'); if ($importSection.length > 0) { $importSection.after($(html)); } } } /** * Update PDF status from queue data (DURING PROCESSING) */ function updatePdfStatusFromQueue(status) { let $card = $('.mxchat-status-card:contains("PDF Processing")'); if ($card.length === 0) { return; } // Update progress bar $card.find('.mxchat-progress-fill').css('width', status.percentage + '%'); // Update details let detailsHtml = '

Progress: ' + (status.completed + status.failed) + ' of ' + status.total + ' pages (' + status.percentage + '%)

'; if (status.completed > 0) { detailsHtml += '

✓ Processed successfully: ' + status.completed + '

'; } if (status.failed > 0) { detailsHtml += '

✗ Failed pages: ' + status.failed + '

'; } detailsHtml += '

Status: Processing

'; $card.find('.mxchat-status-details').html(detailsHtml); } /** * Update sitemap status from queue data (DURING PROCESSING) */ function updateSitemapStatusFromQueue(status) { let $card = $('.mxchat-status-card:contains("Sitemap Processing")'); if ($card.length === 0) { return; } // Update progress bar $card.find('.mxchat-progress-fill').css('width', status.percentage + '%'); // Update details let detailsHtml = '

Progress: ' + (status.completed + status.failed) + ' of ' + status.total + ' URLs (' + status.percentage + '%)

'; if (status.completed > 0) { detailsHtml += '

✓ Processed successfully: ' + status.completed + '

'; } if (status.failed > 0) { detailsHtml += '

✗ Failed URLs: ' + status.failed + '

'; } detailsHtml += '

Status: Processing

'; $card.find('.mxchat-status-details').html(detailsHtml); } // ======================================== // STATUS UPDATES FOR COMPLETED QUEUES (FROM SERVER) // ======================================== /** * Dismiss completed status button handler */ $(document).on('click', '.mxchat-dismiss-button', function() { const $button = $(this); const $card = $button.closest('.mxchat-status-card'); let cardType = $card.data('card-type'); if (!cardType) { cardType = $card.find('h4').text().includes('PDF') ? 'pdf' : 'sitemap'; } $card.fadeOut(300, function() { $(this).remove(); }); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_clear_queue', nonce: mxchatAdmin.queue_nonce, queue_id: $card.data('queue-id') || '' }, success: function(response) { //console.log('MxChat: Queue cleared'); } }); }); /** * Update PDF status card (for already completed queues on page load) */ function updatePdfStatus(status) { let $pdfCard = $('.mxchat-status-card:contains("PDF Processing")'); if ($pdfCard.length === 0 && status) { createPdfStatusCard(status); $pdfCard = $('.mxchat-status-card:contains("PDF Processing")'); } if ($pdfCard.length > 0 && status.status === 'complete') { // Show as completed (same as showCompletedPdfCard but from server data) showCompletedPdfCard(status); } } /** * Update sitemap status card (for already completed queues on page load) */ function updateSitemapStatus(status) { let $sitemapCard = $('.mxchat-status-card:contains("Sitemap Processing")'); if ($sitemapCard.length === 0 && status) { createSitemapStatusCard(status); $sitemapCard = $('.mxchat-status-card:contains("Sitemap Processing")'); } if ($sitemapCard.length > 0 && status.status === 'complete') { // Show as completed (same as showCompletedSitemapCard but from server data) showCompletedSitemapCard(status); } } /** * Create PDF status card */ function createPdfStatusCard(status) { let html = '
'; html += '
'; html += '

PDF Processing Status

'; html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; html += '

Progress: ' + status.processed_pages + ' of ' + status.total_pages + ' pages

'; html += '
'; html += '
'; $('.mxchat-import-section').after($(html)); } /** * Create sitemap status card */ function createSitemapStatusCard(status) { let html = '
'; html += '
'; html += '

Sitemap Processing Status

'; html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; html += '

Progress: ' + status.processed_urls + ' of ' + status.total_urls + ' URLs

'; html += '
'; html += '
'; $('.mxchat-import-section').after($(html)); } /** * Add dismiss button to completed cards */ function addDismissButton($card) { if ($card.find('.mxchat-dismiss-button').length === 0) { const dismissButton = $(''); $card.find('.mxchat-status-header').append(dismissButton); } } /** * Show notification helper */ function showNotification(type, message) { const $notification = $('
' + message + '
'); $('.mxchat-content, body').first().prepend($notification); setTimeout(function() { $notification.fadeOut(300, function() { $(this).remove(); }); }, 5000); } // ======================================== // ROLE-BASED CONTENT RESTRICTIONS (Keep existing code) // ======================================== if ($('#mxchat-mappings-container').length > 0) { loadTagRoleMappings(); } $('#mxchat-add-tag-role').on('click', function() { const tagSlug = $('#mxchat-tag-input').val().trim(); const roleRestriction = $('#mxchat-role-select').val(); if (!tagSlug) { alert('Please enter a tag name'); return; } const $btn = $(this); $btn.prop('disabled', true).html(' Adding...'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_add_tag_role_mapping', nonce: mxchatAdmin.settings_nonce, tag_slug: tagSlug, role_restriction: roleRestriction }, success: function(response) { if (response.success) { $('#mxchat-tag-input').val(''); $('#mxchat-role-select').val('public'); loadTagRoleMappings(); showNotification('success', 'Tag-role mapping added successfully!'); } else { alert('Error: ' + response.data); } $btn.prop('disabled', false).html(' Add Mapping'); }, error: function() { alert('Network error occurred'); $btn.prop('disabled', false).html(' Add Mapping'); } }); }); $(document).on('click', '.mxchat-delete-mapping', function() { if (!confirm('Are you sure you want to delete this mapping?')) { return; } const $btn = $(this); const $row = $btn.closest('tr'); const tagSlug = $btn.data('tag-slug'); $btn.html(' Deleting...'); $row.addClass('mxchat-row-deleting'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_delete_tag_role_mapping', nonce: mxchatAdmin.settings_nonce, tag_slug: tagSlug }, success: function(response) { if (response.success) { $row.fadeOut(300, function() { $(this).remove(); if ($('.mxchat-mappings-table tbody tr').length === 0) { $('.mxchat-mappings-table').hide(); $('#mxchat-no-mappings').show(); } }); showNotification('success', 'Mapping deleted successfully!'); } else { alert('Error: ' + response.data); $btn.html(' Delete'); $row.removeClass('mxchat-row-deleting'); } }, error: function() { alert('Network error occurred'); $btn.html(' Delete'); $row.removeClass('mxchat-row-deleting'); } }); }); $('#mxchat-bulk-update-roles').on('click', function() { if (!confirm('This will update role restrictions for all existing content with mapped tags. Continue?')) { return; } const $btn = $(this); const $progress = $('#mxchat-bulk-update-progress'); const $result = $('#mxchat-bulk-update-result'); $progress.show(); $result.hide(); $btn.prop('disabled', true); $progress.find('.mxchat-progress-text').text('Starting bulk update...'); $progress.find('.mxchat-progress-fill').css('width', '0%'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_bulk_update_tag_roles', nonce: mxchatAdmin.settings_nonce }, success: function(response) { $progress.hide(); $btn.prop('disabled', false); if (response.success) { $result.removeClass('error').addClass('success'); let resultHtml = '
Bulk Update Complete
'; resultHtml += '

Total Updated: ' + response.data.updated_count + '

'; resultHtml += '

Tags Processed: ' + response.data.tags_processed + '

'; if (response.data.details && response.data.details.length > 0) { resultHtml += ''; } $result.html(resultHtml).show(); showNotification('success', 'Bulk update completed successfully!'); } else { $result.removeClass('success').addClass('error'); $result.html('
Update Failed

' + response.data + '

').show(); } }, error: function() { $progress.hide(); $btn.prop('disabled', false); $result.removeClass('success').addClass('error'); $result.html('
Network Error

Please try again.

').show(); } }); }); function loadTagRoleMappings() { const $container = $('#mxchat-mappings-container'); $container.html('
Loading mappings...
'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_get_tag_role_mappings', nonce: mxchatAdmin.settings_nonce }, success: function(response) { if (response.success && response.data.mappings.length > 0) { $('#mxchat-no-mappings').hide(); let html = ''; html += ''; response.data.mappings.forEach(function(mapping) { html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; }); html += '
TagRole RestrictionPosts with TagActions
' + mapping.tag_slug + '' + mapping.role_label + '' + mapping.post_count + '
'; $container.html(html); } else { $container.html(''); $('#mxchat-no-mappings').show(); } }, error: function() { $container.html('
Failed to load mappings. Please refresh the page.
'); } }); } // ======================================== // PINECONE DELETE HANDLER (Keep existing code) // ======================================== $(document).on('click', '.delete-button-ajax', function(e) { e.preventDefault(); if (!confirm('Are you sure you want to delete this entry?')) { return; } var $button = $(this); var $row = $button.closest('tr'); var vectorId = $button.data('vector-id'); var botId = $button.data('bot-id') || 'default'; var nonce = $button.data('nonce'); $button.prop('disabled', true); $button.find('.dashicons').removeClass('dashicons-trash').addClass('dashicons-update-alt'); $row.addClass('mxchat-row-deleting'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_delete_pinecone_prompt', nonce: nonce, vector_id: vectorId, bot_id: botId }, success: function(response) { if (response.success) { $row.fadeOut(500, function() { $(this).remove(); var $countSpan = $('.mxchat-record-count'); if ($countSpan.length) { var currentText = $countSpan.text(); var matches = currentText.match(/\((\d+)/); if (matches) { var currentCount = parseInt(matches[1]); var newCount = Math.max(0, currentCount - 1); $countSpan.text($countSpan.text().replace(/\(\d+/, '(' + newCount)); } } }); $('

Entry deleted successfully from Pinecone.

') .insertAfter('.mxchat-hero') .delay(3000) .fadeOut(); } else { $button.prop('disabled', false); $button.find('.dashicons').removeClass('dashicons-update-alt').addClass('dashicons-trash'); $row.removeClass('mxchat-row-deleting'); alert('Error: ' + response.data); } }, error: function() { $button.prop('disabled', false); $button.find('.dashicons').removeClass('dashicons-update-alt').addClass('dashicons-trash'); $row.removeClass('mxchat-row-deleting'); alert('Network error occurred'); } }); }); });