jQuery(document).ready(function($) { // Modal elements const $modal = $('#mxchat-kb-content-selector-modal'); const $openButton = $('#mxchat-open-content-selector'); const $closeButtons = $('.mxchat-kb-modal-close'); const $contentList = $('.mxchat-kb-content-list'); const $loading = $('.mxchat-kb-loading'); const $pagination = $('.mxchat-kb-pagination'); const $processButton = $('#mxchat-kb-process-selected'); const $selectAll = $('#mxchat-kb-select-all'); const $selectionCount = $('.mxchat-kb-selection-count'); // Filter elements const $searchInput = $('#mxchat-kb-content-search');1 const $typeFilter = $('#mxchat-kb-content-type-filter'); const $statusFilter = $('#mxchat-kb-content-status-filter'); const $processedFilter = $('#mxchat-kb-processed-filter'); // Current state - using let for variables that change let currentPage = 1; let totalPages = 1; let selectedItems = new Set(); let allItems = []; // Open modal when WordPress import button is clicked $openButton.on('click', function() { $modal.show(); // Reset to first page when opening the modal currentPage = 1; loadContent(); }); // Close modal $closeButtons.on('click', function() { $modal.hide(); }); // Handle import option box clicks (for non-WordPress options) $('.mxchat-import-box').on('click', function() { const $box = $(this); const option = $box.data('option'); // Skip if this is the WordPress option (it has its own handler) if (option === 'wordpress') { return; } // Update active state $('.mxchat-import-box').removeClass('active'); $box.addClass('active'); // Hide all input areas $('#mxchat-url-input-area, #mxchat-content-input-area').hide(); // Handle different import options switch (option) { case 'pdf': case 'sitemap': case 'url': // Show URL input area with appropriate placeholder $('#mxchat-url-input-area').show(); $('#sitemap_url').attr('placeholder', $box.data('placeholder')); $('#import_type').val(option); // UPDATED: Add or update bot_id hidden field for URL forms updateBotIdInForm('#mxchat-url-form'); // Update the description text based on the import type let descriptionText = ''; if (option === 'pdf') { descriptionText = 'Import a PDF document by entering its URL above. PDFs are processed via cron job. If processing does not start, you can manually process batch 5 pages at a time.'; } else if (option === 'sitemap') { descriptionText = 'Enter a content-specific sub-sitemap URL, not the sitemap index. Sitemaps are processed via cron job. If processing does not start, you can manually process batch 5 pages at a time.'; } else if (option === 'url') { descriptionText = 'Import content from any webpage by entering its URL.'; } $('#url-description-text').text(descriptionText); break; case 'content': // Show content input area $('#mxchat-content-input-area').show(); // UPDATED: Add or update bot_id hidden field for content forms updateBotIdInForm('#mxchat-content-form'); break; } }); // Helper function to add/update bot_id hidden field in forms function updateBotIdInForm(formSelector) { const $form = $(formSelector); if ($form.length === 0) return; // Get current bot_id from the bot selector dropdown const currentBotId = $('#mxchat-bot-selector').val(); // Only add bot_id field if multi-bot is active and bot is not 'default' if (currentBotId && currentBotId !== 'default') { // Remove existing bot_id field if it exists $form.find('input[name="bot_id"]').remove(); // Add new bot_id field $form.append(''); console.log('Updated bot_id in form ' + formSelector + ' to: ' + currentBotId); } else { // Remove bot_id field if bot is default $form.find('input[name="bot_id"]').remove(); } } // Load content via AJAX function loadContent() { $loading.show(); $contentList.find('.mxchat-kb-content-item').remove(); const data = { action: 'mxchat_get_content_list', nonce: mxchatSelector.nonce, page: currentPage, per_page: 50, search: $searchInput.val(), post_type: $typeFilter.val(), post_status: $statusFilter.val(), processed_filter: $processedFilter.val() }; //console.log('Loading content for page', currentPage, 'with filters:', data); $.ajax({ url: mxchatSelector.ajaxurl, data: data, method: 'GET', dataType: 'json', success: function(response) { $loading.hide(); if (response.success && response.data.items && response.data.items.length > 0) { // Store the items directly let items = response.data.items; if (items.length > 0) { renderContentItems(items); renderPagination(parseInt(response.data.current_page), parseInt(response.data.total_pages)); // Update state allItems = items; totalPages = parseInt(response.data.total_pages); currentPage = parseInt(response.data.current_page); // Update select all checkbox based on current selection updateSelectAllState(); } else { displayNoResults($processedFilter.val()); } } else { displayNoResults($processedFilter.val()); } }, error: function(xhr, status, error) { $loading.hide(); console.error('AJAX Error:', status, error); $contentList.html('
Error loading content. Please try again.
'); // Clear pagination on error $pagination.empty(); } }); } // Helper function to display appropriate "no results" message function displayNoResults(processedStatus) { let message = 'No content found matching your criteria.'; if (processedStatus === 'processed') { message = 'No content found in knowledge base.'; } else if (processedStatus === 'unprocessed') { message = 'All content is already in knowledge base.'; } $contentList.html('
' + message + '
'); // Clear pagination when no results $pagination.empty(); } // Render content items function renderContentItems(items) { let html = ''; items.forEach(function(item) { const isSelected = selectedItems.has(item.id); const isProcessed = item.already_processed; // Updated badge text const badgeText = isProcessed ? 'In Knowledge Base' : 'Not In Knowledge Base'; const badgeClass = isProcessed ? 'mxchat-kb-processed-badge' : 'mxchat-kb-unprocessed-badge'; html += `
${item.title} ${badgeText} ${isProcessed ? 'Last updated: ' + item.processed_date + '' : ''}
${item.excerpt}
`; }); $contentList.html(html); // Add event listeners for checkboxes using delegation for better performance $contentList.off('change', 'input[type="checkbox"]').on('change', 'input[type="checkbox"]', function() { const $checkbox = $(this); const itemId = parseInt($checkbox.closest('.mxchat-kb-content-item').data('id')); if ($checkbox.is(':checked')) { selectedItems.add(itemId); } else { selectedItems.delete(itemId); } updateSelection(); }); } // Render pagination - FIXED VERSION function renderPagination(currentPage, totalPages) { // Clear existing pagination first $pagination.empty(); // Don't render pagination if only one page if (totalPages <= 1) { return; } let html = ''; $pagination.html(html); } // Handle pagination clicks directly on the document $(document).on('click', '.mxchat-kb-page-link', function(e) { e.preventDefault(); const newPage = parseInt($(this).data('page')); //console.log('Pagination clicked: changing from page', currentPage, 'to', newPage); // Only reload if the page actually changed if (currentPage !== newPage) { currentPage = newPage; loadContent(); } }); // Update selection counts and button state function updateSelection() { const selectedCount = selectedItems.size; $selectionCount.text(selectedCount + ' ' + (selectedCount === 1 ? 'selected' : 'selected')); $('.mxchat-kb-selected-count').text('(' + selectedCount + ')'); // Determine if any selected items are already processed const hasProcessedItems = Array.from(selectedItems).some(id => { const item = allItems.find(item => item.id === id); return item && item.already_processed; }); if (selectedCount > 0) { $processButton.prop('disabled', false); // Update button text based on selection if (hasProcessedItems && selectedCount === 1) { $processButton.text('Update Selected Content (1)').addClass('update-mode'); } else if (hasProcessedItems && selectedCount > 1) { $processButton.text('Process/Update Selected (' + selectedCount + ')').addClass('mixed-mode'); } else { $processButton.text('Process Selected Content (' + selectedCount + ')').removeClass('update-mode mixed-mode'); } } else { $processButton.prop('disabled', true); $processButton.text('Process Selected Content').removeClass('update-mode mixed-mode'); $('.mxchat-kb-selected-count').text('(0)'); } updateSelectAllState(); } // Update "Select All" checkbox state function updateSelectAllState() { const availableItems = allItems.length; const selectedAvailableItems = allItems.filter(item => selectedItems.has(item.id)).length; if (availableItems === 0) { $selectAll.prop('checked', false); $selectAll.prop('disabled', true); } else if (selectedAvailableItems === availableItems) { $selectAll.prop('checked', true); } else { $selectAll.prop('checked', false); } } // Handle Select All checkbox $selectAll.on('change', function() { const isChecked = $(this).is(':checked'); $contentList.find('.mxchat-kb-content-item input[type="checkbox"]').each(function() { const $checkbox = $(this); const $item = $checkbox.closest('.mxchat-kb-content-item'); const itemId = parseInt($item.data('id')); $checkbox.prop('checked', isChecked); if (isChecked) { selectedItems.add(itemId); } else { selectedItems.delete(itemId); } }); updateSelection(); }); // Handle search input let searchTimer; $searchInput.on('keyup', function() { clearTimeout(searchTimer); searchTimer = setTimeout(function() { currentPage = 1; // Reset to first page on new search loadContent(); }, 500); }); // Handle filter changes $typeFilter.add($statusFilter).add($processedFilter).on('change', function() { currentPage = 1; // Reset to first page on filter change selectedItems.clear(); // Clear selection when filter changes loadContent(); }); // Process selected content $processButton.on('click', function() { if (selectedItems.size === 0) { return; } const $button = $(this); $button.prop('disabled', true); // Update button text based on mode if ($button.hasClass('update-mode')) { $button.text('Updating...'); } else if ($button.hasClass('mixed-mode')) { $button.text('Processing/Updating...'); } else { $button.text('Processing...'); } // Convert selected items to array const selectedPostIds = Array.from(selectedItems); const totalToProcess = selectedPostIds.length; let processed = 0; let updated = 0; let failed = 0; const results = { success: [], updated: [], failed: [] }; // UPDATED: Get current bot_id for WordPress content processing const currentBotId = $('#mxchat-bot-selector').val(); // Create a modal to show progress const $progressModal = $('
' + '
' + '

Processing Content

' + '

Processing 1 of ' + totalToProcess + '...

' + '
' + '

' + '
' + '
'); $('body').append($progressModal); // Process posts one by one function processNext(index) { if (index >= selectedPostIds.length) { // All done finishProcessing(); return; } const postId = selectedPostIds[index]; const percent = Math.round((index / totalToProcess) * 100); const item = allItems.find(item => item.id === postId); const isUpdate = item && item.already_processed; // Update progress UI $progressModal.find('.mxchat-kb-processing-status') .text((isUpdate ? 'Updating' : 'Processing') + ' ' + (index + 1) + ' of ' + totalToProcess + '...'); $progressModal.find('.mxchat-kb-progress-fill').css('width', percent + '%'); // UPDATED: Prepare AJAX data with bot_id const ajaxData = { action: 'mxchat_process_selected_content', nonce: mxchatSelector.nonce, post_ids: [postId], is_update: isUpdate }; // Add bot_id if multi-bot is active and not default if (currentBotId && currentBotId !== 'default') { ajaxData.bot_id = currentBotId; } // Make AJAX request for this post $.ajax({ url: mxchatSelector.ajaxurl, method: 'POST', data: ajaxData, dataType: 'json', success: function(response) { if (response.success) { if (isUpdate) { updated++; results.updated.push({ id: postId, title: response.data.title || ('ID: ' + postId) }); } else { processed++; results.success.push({ id: postId, title: response.data.title || ('ID: ' + postId) }); } $progressModal.find('.mxchat-kb-current-item') .text('Successfully ' + (isUpdate ? 'updated' : 'processed') + ': ' + response.data.title); } else { failed++; results.failed.push({ id: postId, error: response.data || 'Unknown error' }); $progressModal.find('.mxchat-kb-current-item') .text('Failed to ' + (isUpdate ? 'update' : 'process') + ' ID: ' + postId); } // Process next post setTimeout(function() { processNext(index + 1); }, 500); // Small delay between requests }, error: function(xhr, status, error) { failed++; results.failed.push({ id: postId, error: error || 'Server error' }); $progressModal.find('.mxchat-kb-current-item') .text('Error ' + (isUpdate ? 'updating' : 'processing') + ' ID: ' + postId); // Process next post setTimeout(function() { processNext(index + 1); }, 500); } }); } // Function to finish processing and show results function finishProcessing() { // Remove progress modal $progressModal.remove(); // Determine notification type based on results let notificationClass = 'success'; if (failed > 0) { notificationClass = processed > 0 || updated > 0 ? 'warning' : 'error'; } // Create summary message let resultHTML = '
' + '

'; if (processed > 0 && updated > 0) { resultHTML += 'Processed ' + processed + ' new items and updated ' + updated + ' existing items'; } else if (processed > 0) { resultHTML += 'Processed ' + processed + ' items successfully'; } else if (updated > 0) { resultHTML += 'Updated ' + updated + ' items successfully'; } else { resultHTML += 'No items were processed successfully'; } if (failed > 0) { resultHTML += ' with ' + failed + ' failures'; } resultHTML += '

'; // Add details if there were failures if (failed > 0) { resultHTML += '
'; resultHTML += '
Failed Items:
'; } resultHTML += '
'; // Show results in modal $modal.find('.mxchat-kb-modal-content').prepend($(resultHTML)); // Clear selection selectedItems.clear(); updateSelection(); // Enable button $button.prop('disabled', false) .text('Process Selected Content') .removeClass('update-mode mixed-mode'); $('.mxchat-kb-selected-count').text('(0)'); // Only reload if there were successful operations if (processed > 0 || updated > 0) { // Reload after delay setTimeout(function() { // Reload the knowledge base table - if this function exists if (typeof reloadKnowledgeBaseTable === 'function') { reloadKnowledgeBaseTable(); } else { // Fallback: reload content list instead of full page reload loadContent(); } }, 3000); } } // Start processing the first post processNext(0); }); // Initialize - Set WordPress as the active option by default $('.mxchat-import-box[data-option="wordpress"]').addClass('active'); }); // Tab switching functionality - Keep this separate jQuery(document).ready(function($) { // Tab switching functionality // Modified tab switching to check for Pinecone changes $('.mxchat-kb-tab-button').on('click', function() { var tabId = $(this).data('tab'); var $button = $(this); // Switch tabs immediately for all tabs $('.mxchat-kb-tab-button').removeClass('active'); $('.mxchat-kb-tab-content').removeClass('active'); $button.addClass('active'); $('#mxchat-kb-tab-' + tabId).addClass('active'); // Check if Pinecone was changed and we're going to import tab if (tabId === 'import' && sessionStorage.getItem('mxchat_pinecone_changed') === 'true') { sessionStorage.removeItem('mxchat_pinecone_changed'); // Show refresh notice var $knowledgeCard = $('#mxchat-kb-tab-import .mxchat-card').eq(1); if ($knowledgeCard.length > 0 && $knowledgeCard.find('.notice-warning').length === 0) { var refreshNotice = $('
' + '

' + '' + 'Database settings have changed. ' + 'Click here to refresh to see the updated knowledge base.' + '

'); $knowledgeCard.prepend(refreshNotice); } } // Initialize Pinecone functionality when Pinecone tab is activated if (tabId === 'pinecone') { setTimeout(function() { initPineconeFeatures(); }, 100); } }); // Pinecone functionality function initPineconeFeatures() { //console.log('Initializing Pinecone features...'); if ($('#mxchat-kb-tab-pinecone').length === 0) { return; } initPineconeToggle(); initPineconeConnectionTest(); checkPineconeCompatibility(); } function initPineconeToggle() { // Remove any existing handlers to prevent duplicates $('input[name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"]').off('change.pineconeToggle'); // Add the toggle handler for UI only (auto-save will handle the actual saving) $('input[name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"]').on('change.pineconeToggle', function() { var $checkbox = $(this); var isChecked = $checkbox.is(':checked'); var settingsDiv = $('.mxchat-pinecone-settings'); //console.log('Pinecone toggle changed to:', isChecked); // Update the UI immediately if (isChecked) { settingsDiv.slideDown(300); } else { settingsDiv.slideUp(300); } }); // Set initial state based on current checkbox value var currentToggle = $('input[name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"]'); if (currentToggle.length > 0) { var settingsDiv = $('.mxchat-pinecone-settings'); if (currentToggle.is(':checked')) { settingsDiv.show(); } else { settingsDiv.hide(); } } } function initPineconeConnectionTest() { $('#test-pinecone-connection').off('click.pinecone'); $('#test-pinecone-connection').on('click.pinecone', function() { var button = $(this); var resultDiv = $('#connection-test-result'); var apiKey = $('#mxchat_pinecone_api_key').val(); var host = $('#mxchat_pinecone_host').val(); var index = $('#mxchat_pinecone_index').val(); if (!apiKey || !host || !index) { resultDiv.html('

Please fill in all required fields first.

').show(); return; } button.prop('disabled', true).text('Testing...'); resultDiv.hide(); var ajaxUrl = (typeof mxchatPromptsAdmin !== 'undefined') ? mxchatPromptsAdmin.ajax_url : ajaxurl; var nonce = (typeof mxchatPromptsAdmin !== 'undefined') ? mxchatPromptsAdmin.prompts_setting_nonce : (typeof mxchatAdmin !== 'undefined') ? mxchatAdmin.setting_nonce : ''; $.ajax({ url: ajaxUrl, type: 'POST', data: { action: 'mxchat_test_pinecone_connection', _ajax_nonce: nonce, api_key: apiKey, host: host, index_name: index }, success: function(response) { if (response.success) { resultDiv.html('

' + response.data.message + '

'); } else { resultDiv.html('

' + response.data.message + '

'); } resultDiv.show(); }, error: function() { resultDiv.html('

Connection test failed. Please check your settings.

').show(); }, complete: function() { button.prop('disabled', false).text('Test Connection'); } }); }); } function checkPineconeCompatibility() { if ($('.mxchat-pinecone-compatibility-notice').length > 0) { return; } var hasOldAddon = $('body').hasClass('mxchat-pinecone-addon-active') || $('.pcm-card').length > 0; if (hasOldAddon) { var compatibilityNotice = $(`

Pinecone Integration Notice: We've detected you have the Pinecone add-on installed. Pinecone functionality is now built into the core plugin. You can safely deactivate the separate Pinecone add-on after confirming your settings are migrated below.

`); $('#mxchat-kb-tab-pinecone .mxchat-card').prepend(compatibilityNotice); migratePineconeSettings(); } } function migratePineconeSettings() { if (typeof ajaxurl !== 'undefined') { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_migrate_pinecone_settings', _ajax_nonce: (typeof mxchatAdmin !== 'undefined') ? mxchatAdmin.setting_nonce : '' }, success: function(response) { if (response.success && response.data.migrated) { location.reload(); } }, error: function() { //console.log('Pinecone settings migration not available'); } }); } } });