# mxchat-basic/2.6.0/js/knowledge-processing.js

MxChat – AI Chatbot &amp; Content Generation for WordPress, version 2.6.0. 1,154 lines.

- Page: https://pluginprobe.com/plugins/mxchat-basic/2.6.0/code/js/knowledge-processing.js
- Raw: https://pluginprobe.com/plugins/mxchat-basic/2.6.0/raw/js/knowledge-processing.js
- Modified: 2025-12-13T02:27:48+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/mxchat-basic/2.6.0/code/js/knowledge-processing.js#L10-L20`.

```javascript
jQuery(document).ready(function($) {
    // ========================================
    // UNIQUE URL GENERATOR FOR DIRECT CONTENT
    // ========================================

    // Show/hide generate button based on checkbox
    $('#mxchat-unique-url-toggle').on('change', function() {
        const $button = $('#mxchat-generate-unique-url');
        const $urlInput = $('#article_url');

        if ($(this).is(':checked')) {
            $button.show();
            // If URL field is empty and checkbox is enabled, generate immediately
            if (!$urlInput.val()) {
                generateUniqueUrl();
            }
        } else {
            $button.hide();
        }
    });

    // Generate unique URL when button is clicked
    $('#mxchat-generate-unique-url').on('click', function() {
        generateUniqueUrl();
    });

    // Function to generate unique URL
    function generateUniqueUrl() {
        const $urlInput = $('#article_url');
        let baseUrl = $urlInput.val().trim();

        // If no URL provided, use a default
        if (!baseUrl) {
            baseUrl = window.location.origin;
        }

        // Split URL into base and hash fragment
        let hashFragment = '';
        if (baseUrl.includes('#')) {
            const parts = baseUrl.split('#');
            baseUrl = parts[0];
            hashFragment = '#' + parts[1];
        }

        // Remove any existing ref parameter (matches ref=anything up to & or end of string)
        baseUrl = baseUrl.replace(/[?&]ref=[^&]+(&|$)/, function(match, ending) {
            // If it ends with &, keep it; otherwise remove the whole thing
            return ending === '&' ? '&' : '';
        });

        // Clean up any trailing ? or & from URL
        baseUrl = baseUrl.replace(/[?&]$/, '');

        // Generate unique reference (timestamp only for cleaner URLs)
        const timestamp = Date.now();
        const uniqueRef = timestamp;

        // Add the unique reference as a query parameter (before hash)
        const separator = baseUrl.includes('?') ? '&' : '?';
        const uniqueUrl = baseUrl + separator + 'ref=' + uniqueRef + hashFragment;

        // Update the input field
        $urlInput.val(uniqueUrl);

        // Visual feedback
        $urlInput.css('background-color', '#e7f7e7');
        setTimeout(function() {
            $urlInput.css('background-color', '');
        }, 1000);
    }

    // ========================================
    // QUEUE PROCESSING SYSTEM
    // ========================================

    let isProcessingQueue = false;
    let currentQueueId = null;
    let currentQueueType = null;

    // Process 5 items at a time
    const BATCH_SIZE = 5;

    // 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 = $('<div class="mxchat-processing-message" style="text-align: center; padding: 15px; background: #f0f7ff; border-radius: 8px; margin-top: 15px;">⏳ Queue created! Processing will start in a moment...</div>');
                $('.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 batch processing loop
        processNextBatch();
    }

    /**
     * Process the next batch of items (5 at a time)
     */
    function processNextBatch() {
        if (!isProcessingQueue) {
            //console.log('MxChat: Processing stopped');
            return;
        }

        // Fetch the next batch of items
        const fetchPromises = [];

        for (let i = 0; i < BATCH_SIZE; i++) {
            const promise = $.ajax({
                url: ajaxurl,
                type: 'POST',
                data: {
                    action: 'mxchat_get_next_queue_item',
                    nonce: mxchatAdmin.queue_nonce,
                    queue_id: currentQueueId
                }
            });
            fetchPromises.push(promise);
        }

        // Wait for all fetch requests to complete
        Promise.all(fetchPromises).then(function(responses) {
            // Filter out completed/error responses and extract items
            const items = [];
            let queueComplete = false;

            for (let response of responses) {
                if (response.success && response.data && !response.data.complete) {
                    items.push(response.data.item);
                } else if (response.data && response.data.complete) {
                    queueComplete = true;
                }
            }

            // If no items to process, queue is done
            if (items.length === 0) {
                if (queueComplete) {
                    handleQueueComplete();
                } else {
                    verifyQueueCompletion();
                }
                return;
            }

            // Process all items in this batch simultaneously
            const processPromises = items.map(item => processQueueItem(item));

            // Wait for all items to finish processing
            Promise.all(processPromises).then(function() {
                // Update progress after batch completes
                updateQueueProgress();

                // If we got fewer items than batch size, queue might be done
                if (items.length < BATCH_SIZE || queueComplete) {
                    verifyQueueCompletion();
                } else {
                    // Process next batch immediately
                    processNextBatch();
                }
            }).catch(function(error) {
                console.error('MxChat: Error processing batch:', error);
                // Continue anyway
                updateQueueProgress();
                setTimeout(function() {
                    processNextBatch();
                }, 1000);
            });

        }).catch(function(error) {
            console.error('MxChat: Error fetching batch:', 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...');
                        processNextBatch();
                    }
                } 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
     * Returns a Promise that resolves when processing is complete
     */
    function processQueueItem(item) {
        return $.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
            }
        }).then(function(response) {
            if (response.success) {
                // Item processed successfully
                //console.log('MxChat: Item processed successfully:', item.id);
                return true;
            } 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);
                return false;
            }
        }).catch(function(xhr, status, error) {
            // Network error - log it but KEEP GOING
            console.error('MxChat: AJAX/Network error processing item:', item.id, error);
            return false;
        });
    }
    
    /**
     * 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(
                '<span class="mxchat-status-badge mxchat-status-warning" style="margin-left: 10px; padding: 4px 12px; border-radius: 4px; font-size: 12px; font-weight: normal;">⚠️ Completed with ' + 
                status.failed + ' failures - Refresh to view entries</span>'
            );
        } else {
            $card.find('.mxchat-status-header h4').after(
                '<span class="mxchat-status-badge mxchat-status-success" style="margin-left: 10px; padding: 4px 12px; border-radius: 4px; font-size: 12px; font-weight: normal;">✓ Complete - Refresh to view entries</span>'
            );
        }
                
        // 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 = '<div style="background: #f0f7ff; padding: 15px; border-radius: 4px; margin-bottom: 15px;">';
        detailsHtml += '<h4 style="margin: 0 0 10px 0;">📊 Final Results</h4>';
        detailsHtml += '<p style="margin: 5px 0;"><strong>Total Pages:</strong> ' + status.total + '</p>';
        detailsHtml += '<p style="margin: 5px 0; color: #2ea44f;"><strong>✓ Successfully Processed:</strong> ' + status.completed + '</p>';
        
        if (status.failed > 0) {
            detailsHtml += '<p style="margin: 5px 0; color: #cf222e;"><strong>✗ Failed:</strong> ' + status.failed + '</p>';
        }
        
        detailsHtml += '</div>';
        
        // Add error details if there are failures (NO RETRY BUTTON)
        if (status.failed > 0 && status.failed_items && status.failed_items.length > 0) {
            detailsHtml += '<div class="mxchat-error-summary" style="background: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin-top: 15px;">';
            detailsHtml += '<h4 style="margin: 0 0 10px 0; color: #856404;">⚠️ Failed Pages</h4>';
            
            detailsHtml += '<details style="cursor: pointer;">';
            detailsHtml += '<summary style="font-weight: bold; color: #856404; padding: 5px 0;">Click to view ' + status.failed_items.length + ' failed pages</summary>';
            
            detailsHtml += '<div style="margin-top: 10px; max-height: 400px; overflow-y: auto;">';
            detailsHtml += '<table style="width: 100%; border-collapse: collapse;">';
            detailsHtml += '<thead><tr style="background: #f5f5f5;"><th style="padding: 8px; text-align: left;">Page</th><th style="padding: 8px; text-align: left;">Error</th><th style="padding: 8px; text-align: left;">Attempts</th></tr></thead>';
            detailsHtml += '<tbody>';
            
            status.failed_items.forEach(function(item) {
                const data = JSON.parse(item.item_data);
                const pageNum = data.page_number || 'Unknown';
                
                detailsHtml += '<tr style="border-bottom: 1px solid #ddd;">';
                detailsHtml += '<td style="padding: 8px;">Page ' + pageNum + '</td>';
                detailsHtml += '<td style="padding: 8px; word-break: break-word;">' + (item.error_message || 'Unknown error') + '</td>';
                detailsHtml += '<td style="padding: 8px;">' + item.attempts + '</td>';
                detailsHtml += '</tr>';
            });
            
            detailsHtml += '</tbody></table>';
            detailsHtml += '</div>';
            detailsHtml += '</details>';
            
            detailsHtml += '</div>';
        }
        
        $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(
                '<span class="mxchat-status-badge mxchat-status-warning" style="margin-left: 10px; padding: 4px 12px; border-radius: 4px; font-size: 12px; font-weight: normal;">⚠️ Completed with ' + 
                status.failed + ' failures - Refresh to view entries</span>'
            );
        } else {
            $card.find('.mxchat-status-header h4').after(
                '<span class="mxchat-status-badge mxchat-status-success" style="margin-left: 10px; padding: 4px 12px; border-radius: 4px; font-size: 12px; font-weight: normal;">✓ Complete - Refresh to view entries</span>'
            );
        }
        
        // 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 = '<div style="background: #f0f7ff; padding: 15px; border-radius: 4px; margin-bottom: 15px;">';
        detailsHtml += '<h4 style="margin: 0 0 10px 0;">📊 Final Results</h4>';
        detailsHtml += '<p style="margin: 5px 0;"><strong>Total URLs:</strong> ' + status.total + '</p>';
        detailsHtml += '<p style="margin: 5px 0; color: #2ea44f;"><strong>✓ Successfully Processed:</strong> ' + status.completed + '</p>';
        
        if (status.failed > 0) {
            detailsHtml += '<p style="margin: 5px 0; color: #cf222e;"><strong>✗ Failed:</strong> ' + status.failed + '</p>';
        }
        
        detailsHtml += '</div>';
        
        // Add error details if there are failures (NO RETRY BUTTON)
        if (status.failed > 0 && status.failed_items && status.failed_items.length > 0) {
            detailsHtml += '<div class="mxchat-error-summary" style="background: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin-top: 15px;">';
            detailsHtml += '<h4 style="margin: 0 0 10px 0; color: #856404;">⚠️ Failed URLs</h4>';
            
            detailsHtml += '<details style="cursor: pointer;">';
            detailsHtml += '<summary style="font-weight: bold; color: #856404; padding: 5px 0;">Click to view ' + status.failed_items.length + ' failed URLs</summary>';
            
            detailsHtml += '<div style="margin-top: 10px; max-height: 400px; overflow-y: auto;">';
            detailsHtml += '<table style="width: 100%; border-collapse: collapse;">';
            detailsHtml += '<thead><tr style="background: #f5f5f5;"><th style="padding: 8px; text-align: left;">URL</th><th style="padding: 8px; text-align: left;">Error</th><th style="padding: 8px; text-align: left;">Attempts</th></tr></thead>';
            detailsHtml += '<tbody>';
            
            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 += '<tr style="border-bottom: 1px solid #ddd;">';
                detailsHtml += '<td style="padding: 8px;"><a href="' + url + '" target="_blank" style="color: #0073aa; text-decoration: none;">' + displayUrl + '</a></td>';
                detailsHtml += '<td style="padding: 8px; word-break: break-word;">' + (item.error_message || 'Unknown error') + '</td>';
                detailsHtml += '<td style="padding: 8px;">' + item.attempts + '</td>';
                detailsHtml += '</tr>';
            });
            
            detailsHtml += '</tbody></table>';
            detailsHtml += '</div>';
            detailsHtml += '</details>';
            
            detailsHtml += '</div>';
        }
        
        $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 = '<div class="mxchat-status-card">';
            html += '<div class="mxchat-status-header">';
            html += '<h4>' + cardTitle + '</h4>';
            html += '<div class="mxchat-status-warning" style="background: #fff3cd; color: #856404; padding: 8px 12px; border-radius: 4px; font-size: 13px; margin: 10px 0;">';
            html += '⚠️ <strong>Keep this tab open</strong> - Processing runs in your browser';
            html += '</div>';
            html += '<form method="post" class="mxchat-stop-form" action="' + 
                   mxchatAdmin.admin_url + 'admin-post.php?action=mxchat_stop_processing">';
            html += '<input type="hidden" name="mxchat_stop_processing_nonce" value="' + 
                   mxchatAdmin.stop_nonce + '">';
            html += '<button type="submit" name="stop_processing" class="mxchat-button-secondary">';
            html += 'Stop Processing</button></form>';
            html += '</div>';
            html += '<div class="mxchat-progress-bar">';
            html += '<div class="mxchat-progress-fill" style="width: 0%"></div>';
            html += '</div>';
            html += '<div class="mxchat-status-details">';
            html += '<p>Initializing...</p>';
            html += '</div>';
            html += '</div>';
            
            // 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 = '<p>Progress: ' + (status.completed + status.failed) + ' of ' + 
                         status.total + ' pages (' + status.percentage + '%)</p>';
        
        if (status.completed > 0) {
            detailsHtml += '<p><strong>✓ Processed successfully:</strong> ' + status.completed + '</p>';
        }
        
        if (status.failed > 0) {
            detailsHtml += '<p class="error-count"><strong>✗ Failed pages:</strong> ' + status.failed + '</p>';
        }
        
        detailsHtml += '<p><strong>Status:</strong> Processing</p>';
        
        $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 = '<p>Progress: ' + (status.completed + status.failed) + ' of ' + 
                         status.total + ' URLs (' + status.percentage + '%)</p>';
        
        if (status.completed > 0) {
            detailsHtml += '<p><strong>✓ Processed successfully:</strong> ' + status.completed + '</p>';
        }
        
        if (status.failed > 0) {
            detailsHtml += '<p class="error-count"><strong>✗ Failed URLs:</strong> ' + status.failed + '</p>';
        }
        
        detailsHtml += '<p><strong>Status:</strong> Processing</p>';
        
        $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 = '<div class="mxchat-status-card" data-queue-id="' + (status.queue_id || '') + '">';
        html += '<div class="mxchat-status-header">';
        html += '<h4>PDF Processing Status</h4>';
        html += '</div>';
        html += '<div class="mxchat-progress-bar">';
        html += '<div class="mxchat-progress-fill" style="width: ' + status.percentage + '%"></div>';
        html += '</div>';
        html += '<div class="mxchat-status-details">';
        html += '<p>Progress: ' + status.processed_pages + ' of ' + status.total_pages + ' pages</p>';
        html += '</div>';
        html += '</div>';
        
        $('.mxchat-import-section').after($(html));
    }
    
    /**
     * Create sitemap status card
     */
    function createSitemapStatusCard(status) {
        let html = '<div class="mxchat-status-card" data-queue-id="' + (status.queue_id || '') + '">';
        html += '<div class="mxchat-status-header">';
        html += '<h4>Sitemap Processing Status</h4>';
        html += '</div>';
        html += '<div class="mxchat-progress-bar">';
        html += '<div class="mxchat-progress-fill" style="width: ' + status.percentage + '%"></div>';
        html += '</div>';
        html += '<div class="mxchat-status-details">';
        html += '<p>Progress: ' + status.processed_urls + ' of ' + status.total_urls + ' URLs</p>';
        html += '</div>';
        html += '</div>';
        
        $('.mxchat-import-section').after($(html));
    }
    
    /**
     * Add dismiss button to completed cards
     */
    function addDismissButton($card) {
        if ($card.find('.mxchat-dismiss-button').length === 0) {
            const dismissButton = $('<button type="button" class="mxchat-dismiss-button" style="padding: 6px 12px; background: #666; color: white; border: none; border-radius: 3px; cursor: pointer; margin-left: 10px;">Dismiss</button>');
            $card.find('.mxchat-status-header').append(dismissButton);
        }
    }
    
    /**
     * Show notification helper
     */
    function showNotification(type, message) {
        const $notification = $('<div class="mxchat-kb-notification ' + type + '">' + message + '</div>');
        $('.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('<span class="dashicons dashicons-update-alt"></span> 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('<span class="dashicons dashicons-plus-alt"></span> Add Mapping');
            },
            error: function() {
                alert('Network error occurred');
                $btn.prop('disabled', false).html('<span class="dashicons dashicons-plus-alt"></span> 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('<span class="dashicons dashicons-update-alt"></span> 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('<span class="dashicons dashicons-trash"></span> Delete');
                    $row.removeClass('mxchat-row-deleting');
                }
            },
            error: function() {
                alert('Network error occurred');
                $btn.html('<span class="dashicons dashicons-trash"></span> 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 = '<h5>Bulk Update Complete</h5>';
                    resultHtml += '<p><strong>Total Updated:</strong> ' + response.data.updated_count + '</p>';
                    resultHtml += '<p><strong>Tags Processed:</strong> ' + response.data.tags_processed + '</p>';
                    
                    if (response.data.details && response.data.details.length > 0) {
                        resultHtml += '<ul>';
                        response.data.details.forEach(function(detail) {
                            resultHtml += '<li>' + detail + '</li>';
                        });
                        resultHtml += '</ul>';
                    }
                    
                    $result.html(resultHtml).show();
                    showNotification('success', 'Bulk update completed successfully!');
                } else {
                    $result.removeClass('success').addClass('error');
                    $result.html('<h5>Update Failed</h5><p>' + response.data + '</p>').show();
                }
            },
            error: function() {
                $progress.hide();
                $btn.prop('disabled', false);
                $result.removeClass('success').addClass('error');
                $result.html('<h5>Network Error</h5><p>Please try again.</p>').show();
            }
        });
    });
    
    function loadTagRoleMappings() {
        const $container = $('#mxchat-mappings-container');
        $container.html('<div class="mxchat-loading-mappings"><span class="mxchat-role-spinner is-active"></span> Loading mappings...</div>');
        
        $.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 = '<table class="mxchat-mappings-table">';
                    html += '<thead><tr><th>Tag</th><th>Role Restriction</th><th>Posts with Tag</th><th>Actions</th></tr></thead><tbody>';
                    
                    response.data.mappings.forEach(function(mapping) {
                        html += '<tr>';
                        html += '<td><span class="mxchat-tag-badge"><span class="dashicons dashicons-tag"></span>' + mapping.tag_slug + '</span></td>';
                        html += '<td><span class="mxchat-role-badge ' + mapping.role_restriction + '">' + mapping.role_label + '</span></td>';
                        html += '<td><span class="mxchat-post-count"><span class="dashicons dashicons-admin-post"></span>' + mapping.post_count + '</span></td>';
                        html += '<td><div class="mxchat-mapping-actions"><button class="mxchat-delete-mapping" data-tag-slug="' + mapping.tag_slug + '"><span class="dashicons dashicons-trash"></span> Delete</button></div></td>';
                        html += '</tr>';
                    });
                    
                    html += '</tbody></table>';
                    $container.html(html);
                } else {
                    $container.html('');
                    $('#mxchat-no-mappings').show();
                }
            },
            error: function() {
                $container.html('<div class="mxchat-error">Failed to load mappings. Please refresh the page.</div>');
            }
        });
    }
    
    // ========================================
    // 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));
                            }
                        }
                    });
                    
                    $('<div class="notice notice-success is-dismissible"><p>Entry deleted successfully from Pinecone.</p></div>')
                        .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');
            }
        });
    });

    // ========================================
    // ACCORDION FUNCTIONALITY
    // ========================================

    // Handle expand/collapse toggle
    $(document).on('click', '.mxchat-expand-toggle', function(e) {
        e.preventDefault();
        e.stopPropagation();

        const $button = $(this);
        const $wrapper = $button.closest('.mxchat-accordion-wrapper');
        const $preview = $wrapper.find('.mxchat-content-preview');
        const $fullContent = $wrapper.find('.mxchat-content-full');

        // Toggle expanded state
        if ($fullContent.is(':visible')) {
            // Collapse
            $fullContent.slideUp(300);
            $button.removeClass('expanded');
        } else {
            // Expand
            $fullContent.slideDown(300);
            $button.addClass('expanded');
        }
    });

    // Click anywhere on preview to toggle (expand or collapse)
    $(document).on('click', '.mxchat-content-preview', function(e) {
        // Only trigger if not clicking the button directly
        if (!$(e.target).closest('.mxchat-expand-toggle').length) {
            const $preview = $(this);
            const $wrapper = $preview.closest('.mxchat-accordion-wrapper');
            const $button = $preview.find('.mxchat-expand-toggle');

            // Only trigger if there's a button (meaning content is long enough to expand)
            if ($button.length) {
                $button.trigger('click');
            }
        }
    });
});
```
