PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.2
MxChat – AI Chatbot & Content Generation for WordPress v3.2.2
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / js / content-selector.js

content-selector.js in MxChat – AI Chatbot & Content Generation for WordPress 3.2.2, at js/content-selector.js

966 lines 39.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 jQuery(document).ready(function($) {
2 // Modal elements
3 const $modal = $('#mxchat-kb-content-selector-modal');
4 const $openButton = $('#mxchat-open-content-selector');
5 const $closeButtons = $('.mxchat-kb-modal-close');
6 const $contentList = $('.mxchat-kb-content-list');
7 const $loading = $('.mxchat-kb-loading');
8 const $pagination = $('.mxchat-kb-pagination');
9 const $processButton = $('#mxchat-kb-process-selected');
10 const $selectAll = $('#mxchat-kb-select-all');
11 const $selectionCount = $('.mxchat-kb-selection-count');
12
13 // Filter elements
14 const $searchInput = $('#mxchat-kb-content-search');1
15 const $typeFilter = $('#mxchat-kb-content-type-filter');
16 const $statusFilter = $('#mxchat-kb-content-status-filter');
17 const $processedFilter = $('#mxchat-kb-processed-filter');
18
19 // Current state - using let for variables that change
20 let currentPage = 1;
21 let totalPages = 1;
22 let selectedItems = new Set();
23 let allItems = [];
24
25 // Open modal when WordPress import button is clicked
26 $openButton.on('click', function() {
27 $modal.show();
28 // Reset to first page when opening the modal
29 currentPage = 1;
30 loadContent();
31 });
32
33 // Close modal
34 $closeButtons.on('click', function() {
35 $modal.hide();
36 });
37 // Handle import option box clicks (for non-WordPress options)
38 $('.mxchat-import-box').on('click', function() {
39 const $box = $(this);
40 const option = $box.data('option');
41
42 // Skip if this is the WordPress option (it has its own handler)
43 if (option === 'wordpress') {
44 return;
45 }
46
47 // Update active state
48 $('.mxchat-import-box').removeClass('active');
49 $box.addClass('active');
50
51 // Hide all input areas
52 $('#mxchat-url-input-area, #mxchat-content-input-area, #mxchat-pdf-upload-area').hide();
53
54 // Hide sitemap-specific sections (but NOT for sitemap option - let detection logic handle it)
55 if (option !== 'sitemap') {
56 $('#mxchat-detected-sitemaps, #mxchat-no-sitemaps, #mxchat-sitemaps-loading').hide();
57 }
58
59 // Handle different import options
60 switch (option) {
61 case 'pdf-url':
62 case 'sitemap':
63 case 'url':
64 // Show URL input area with appropriate placeholder
65 $('#mxchat-url-input-area').show();
66 $('#sitemap_url').attr('placeholder', $box.data('placeholder'));
67 $('#import_type').val(option === 'pdf-url' ? 'pdf' : option);
68
69 // UPDATED: Add or update bot_id hidden field for URL forms
70 updateBotIdInForm('#mxchat-url-form');
71
72 // Update the description text based on the import type
73 let descriptionText = '';
74 if (option === 'pdf-url') {
75 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.';
76 } else if (option === 'sitemap') {
77 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.';
78 // Re-show sitemap sections if they were previously loaded
79 const $sitemapsList = $('#mxchat-sitemaps-list');
80 if ($sitemapsList.children().length > 0) {
81 // Sitemaps were already loaded, just show the container
82 $('#mxchat-detected-sitemaps').show();
83 } else if ($('#mxchat-no-sitemaps').data('was-shown')) {
84 // No sitemaps message was shown before
85 $('#mxchat-no-sitemaps').show();
86 }
87 // Note: If neither condition is true, initSitemapDetection will show loading state
88 } else if (option === 'url') {
89 descriptionText = 'Import content from any webpage by entering its URL.';
90 }
91 $('#url-description-text').text(descriptionText);
92 break;
93
94 case 'content':
95 // Show content input area
96 $('#mxchat-content-input-area').show();
97
98 // UPDATED: Add or update bot_id hidden field for content forms
99 updateBotIdInForm('#mxchat-content-form');
100 break;
101
102 case 'pdf-upload':
103 // Show PDF file upload area
104 $('#mxchat-pdf-upload-area').show();
105
106 // Add or update bot_id hidden field for PDF upload form
107 updateBotIdInForm('#mxchat-pdf-upload-form');
108 break;
109 }
110 });
111
112 // Helper function to add/update bot_id hidden field in forms
113 function updateBotIdInForm(formSelector) {
114 const $form = $(formSelector);
115 if ($form.length === 0) return;
116
117 // Get current bot_id from the bot selector dropdown
118 const currentBotId = $('#mxchat-bot-selector').val();
119
120 // Only add bot_id field if multi-bot is active and bot is not 'default'
121 if (currentBotId && currentBotId !== 'default') {
122 // Remove existing bot_id field if it exists
123 $form.find('input[name="bot_id"]').remove();
124
125 // Add new bot_id field
126 $form.append('<input type="hidden" name="bot_id" value="' + currentBotId + '">');
127
128 console.log('Updated bot_id in form ' + formSelector + ' to: ' + currentBotId);
129 } else {
130 // Remove bot_id field if bot is default
131 $form.find('input[name="bot_id"]').remove();
132 }
133 }
134
135 // Load content via AJAX
136 function loadContent() {
137 $loading.show();
138 $contentList.find('.mxchat-kb-content-item').remove();
139
140 const data = {
141 action: 'mxchat_get_content_list',
142 nonce: mxchatSelector.nonce,
143 page: currentPage,
144 per_page: 100,
145 search: $searchInput.val(),
146 post_type: $typeFilter.val(),
147 post_status: $statusFilter.val(),
148 processed_filter: $processedFilter.val()
149 };
150
151 //console.log('Loading content for page', currentPage, 'with filters:', data);
152
153 $.ajax({
154 url: mxchatSelector.ajaxurl,
155 data: data,
156 method: 'GET',
157 dataType: 'json',
158 success: function(response) {
159 $loading.hide();
160
161 if (response.success && response.data.items && response.data.items.length > 0) {
162 // Store the items directly
163 let items = response.data.items;
164
165 if (items.length > 0) {
166 renderContentItems(items);
167 renderPagination(parseInt(response.data.current_page), parseInt(response.data.total_pages));
168
169 // Update state
170 allItems = items;
171 totalPages = parseInt(response.data.total_pages);
172 currentPage = parseInt(response.data.current_page);
173
174 // Update select all checkbox based on current selection
175 updateSelectAllState();
176 } else {
177 displayNoResults($processedFilter.val());
178 }
179 } else {
180 displayNoResults($processedFilter.val());
181 }
182 },
183 error: function(xhr, status, error) {
184 $loading.hide();
185 console.error('AJAX Error:', status, error);
186 $contentList.html('<div class="mxchat-kb-error">Error loading content. Please try again.</div>');
187 // Clear pagination on error
188 $pagination.empty();
189 }
190 });
191 }
192
193 // Helper function to display appropriate "no results" message
194 function displayNoResults(processedStatus) {
195 let message = 'No content found matching your criteria.';
196
197 if (processedStatus === 'processed') {
198 message = 'No content found in knowledge base.';
199 } else if (processedStatus === 'unprocessed') {
200 message = 'All content is already in knowledge base.';
201 }
202
203 $contentList.html('<div class="mxchat-kb-no-results">' + message + '</div>');
204 // Clear pagination when no results
205 $pagination.empty();
206 }
207
208 // Render content items
209 function renderContentItems(items) {
210 let html = '';
211
212 items.forEach(function(item) {
213 const isSelected = selectedItems.has(item.id);
214 const isProcessed = item.already_processed;
215 const chunkCount = item.chunk_count || 0;
216
217 // Updated badge text - include chunk count if > 1
218 let badgeText = 'Not In Knowledge Base';
219 if (isProcessed) {
220 badgeText = chunkCount > 1 ? `In Knowledge Base (${chunkCount} chunks)` : 'In Knowledge Base';
221 }
222 const badgeClass = isProcessed ? 'mxchat-kb-processed-badge' : 'mxchat-kb-unprocessed-badge';
223
224
225 html += `
226 <div class="mxchat-kb-content-item ${isProcessed ? 'processed' : ''}" data-id="${item.id}">
227 <div class="mxchat-kb-content-checkbox">
228 <input type="checkbox" id="content-${item.id}" ${isSelected ? 'checked' : ''}>
229 </div>
230 <div class="mxchat-kb-content-details">
231 <div class="mxchat-kb-content-title">
232 <a href="${item.permalink}" target="_blank">${item.title}</a>
233 <span class="${badgeClass}">${badgeText}</span>
234 ${isProcessed ? '<span class="mxchat-kb-last-updated">Last updated: ' + item.processed_date + '</span>' : ''}
235 </div>
236 <div class="mxchat-kb-content-meta">
237 <span class="mxchat-kb-content-type">${item.type}</span>
238 <span class="mxchat-kb-content-date">${item.date}</span>
239 <span class="mxchat-kb-content-words">${item.word_count} words</span>
240 </div>
241 <div class="mxchat-kb-content-excerpt">${item.excerpt}</div>
242 </div>
243 </div>
244 `;
245 });
246
247 $contentList.html(html);
248
249 // Add event listeners for checkboxes using delegation for better performance
250 $contentList.off('change', 'input[type="checkbox"]').on('change', 'input[type="checkbox"]', function() {
251 const $checkbox = $(this);
252 const itemId = parseInt($checkbox.closest('.mxchat-kb-content-item').data('id'));
253
254 if ($checkbox.is(':checked')) {
255 selectedItems.add(itemId);
256 } else {
257 selectedItems.delete(itemId);
258 }
259
260 updateSelection();
261 });
262 }
263
264 // Render pagination - FIXED VERSION
265 function renderPagination(currentPage, totalPages) {
266 // Clear existing pagination first
267 $pagination.empty();
268
269 // Don't render pagination if only one page
270 if (totalPages <= 1) {
271 return;
272 }
273
274 let html = '<div class="mxchat-kb-pagination-links">';
275
276 // Previous button
277 if (currentPage > 1) {
278 html += '<a href="#" class="mxchat-kb-page-link prev" data-page="' + (currentPage - 1) + '">&laquo; Previous</a>';
279 }
280
281 // Page numbers
282 const startPage = Math.max(1, currentPage - 2);
283 const endPage = Math.min(totalPages, startPage + 4);
284
285 for (let i = startPage; i <= endPage; i++) {
286 if (i === currentPage) {
287 html += '<span class="mxchat-kb-page-current">' + i + '</span>';
288 } else {
289 html += '<a href="#" class="mxchat-kb-page-link" data-page="' + i + '">' + i + '</a>';
290 }
291 }
292
293 // Next button
294 if (currentPage < totalPages) {
295 html += '<a href="#" class="mxchat-kb-page-link next" data-page="' + (currentPage + 1) + '">Next &raquo;</a>';
296 }
297
298 html += '</div>';
299
300 $pagination.html(html);
301 }
302
303 // Handle pagination clicks directly on the document
304 $(document).on('click', '.mxchat-kb-page-link', function(e) {
305 e.preventDefault();
306 const newPage = parseInt($(this).data('page'));
307 //console.log('Pagination clicked: changing from page', currentPage, 'to', newPage);
308
309 // Only reload if the page actually changed
310 if (currentPage !== newPage) {
311 currentPage = newPage;
312 loadContent();
313 }
314 });
315
316 // Update selection counts and button state
317 function updateSelection() {
318 const selectedCount = selectedItems.size;
319 $selectionCount.text(selectedCount + ' ' + (selectedCount === 1 ? 'selected' : 'selected'));
320 $('.mxchat-kb-selected-count').text('(' + selectedCount + ')');
321
322 // Show/hide clear all selections link
323 let $clearAllLink = $('.mxchat-kb-clear-all-selections');
324 if (selectedCount > 0) {
325 if ($clearAllLink.length === 0) {
326 $clearAllLink = $('<a href="#" class="mxchat-kb-clear-all-selections" style="margin-left: 10px; font-size: 12px; color: var(--mxch-error, #dc2626);">Clear all</a>');
327 $selectionCount.after($clearAllLink);
328 $clearAllLink.on('click', function(e) {
329 e.preventDefault();
330 selectedItems.clear();
331 $('.mxchat-kb-content-item input[type="checkbox"]').prop('checked', false);
332 updateSelection();
333 });
334 }
335 $clearAllLink.show();
336 } else {
337 $clearAllLink.hide();
338 }
339
340 // Determine if any selected items are already processed
341 const hasProcessedItems = Array.from(selectedItems).some(id => {
342 const item = allItems.find(item => item.id === id);
343 return item && item.already_processed;
344 });
345
346 if (selectedCount > 0) {
347 $processButton.prop('disabled', false);
348
349 // Update button text based on selection
350 if (hasProcessedItems && selectedCount === 1) {
351 $processButton.text('Update Selected Content (1)').addClass('update-mode');
352 } else if (hasProcessedItems && selectedCount > 1) {
353 $processButton.text('Process/Update Selected (' + selectedCount + ')').addClass('mixed-mode');
354 } else {
355 $processButton.text('Process Selected Content (' + selectedCount + ')').removeClass('update-mode mixed-mode');
356 }
357 } else {
358 $processButton.prop('disabled', true);
359 $processButton.text('Process Selected Content').removeClass('update-mode mixed-mode');
360 $('.mxchat-kb-selected-count').text('(0)');
361 }
362
363 updateSelectAllState();
364 }
365
366 // Update "Select All" checkbox state
367 function updateSelectAllState() {
368 const availableItems = allItems.length;
369 const selectedAvailableItems = allItems.filter(item => selectedItems.has(item.id)).length;
370
371 if (availableItems === 0) {
372 $selectAll.prop('checked', false);
373 $selectAll.prop('disabled', true);
374 } else if (selectedAvailableItems === availableItems) {
375 $selectAll.prop('checked', true);
376 } else {
377 $selectAll.prop('checked', false);
378 }
379 }
380
381 // Handle Select All checkbox
382 $selectAll.on('change', function() {
383 const isChecked = $(this).is(':checked');
384
385 $contentList.find('.mxchat-kb-content-item input[type="checkbox"]').each(function() {
386 const $checkbox = $(this);
387 const $item = $checkbox.closest('.mxchat-kb-content-item');
388 const itemId = parseInt($item.data('id'));
389
390 $checkbox.prop('checked', isChecked);
391
392 if (isChecked) {
393 selectedItems.add(itemId);
394 } else {
395 selectedItems.delete(itemId);
396 }
397 });
398
399 updateSelection();
400 });
401
402 // Handle search input
403 let searchTimer;
404 $searchInput.on('keyup', function() {
405 clearTimeout(searchTimer);
406 searchTimer = setTimeout(function() {
407 currentPage = 1; // Reset to first page on new search
408 loadContent();
409 }, 500);
410 });
411
412 // Handle filter changes
413 $typeFilter.add($statusFilter).add($processedFilter).on('change', function() {
414 currentPage = 1; // Reset to first page on filter change
415 selectedItems.clear(); // Clear selection when filter changes
416 loadContent();
417 });
418
419 // Process selected content
420 $processButton.on('click', function() {
421 if (selectedItems.size === 0) {
422 return;
423 }
424
425 const $button = $(this);
426 $button.prop('disabled', true);
427
428 // Update button text based on mode
429 if ($button.hasClass('update-mode')) {
430 $button.text('Updating...');
431 } else if ($button.hasClass('mixed-mode')) {
432 $button.text('Processing/Updating...');
433 } else {
434 $button.text('Processing...');
435 }
436
437 // Convert selected items to array
438 const selectedPostIds = Array.from(selectedItems);
439 const totalToProcess = selectedPostIds.length;
440 let processed = 0;
441 let updated = 0;
442 let failed = 0;
443 const results = {
444 success: [],
445 updated: [],
446 failed: []
447 };
448
449 // UPDATED: Get current bot_id for WordPress content processing
450 const currentBotId = $('#mxchat-bot-selector').val();
451
452 // Flag to track if processing should be aborted
453 let abortProcessing = false;
454 let currentXHR = null;
455
456 // Create a modal to show progress with stop button
457 const $progressModal = $('<div class="mxchat-kb-processing-overlay">' +
458 '<div class="mxchat-kb-processing-content">' +
459 '<h3>Processing Content</h3>' +
460 '<p class="mxchat-kb-processing-status">Processing 1 of ' + totalToProcess + '...</p>' +
461 '<div class="mxchat-kb-progress-bar"><div class="mxchat-kb-progress-fill" style="width: 0%"></div></div>' +
462 '<p class="mxchat-kb-current-item"></p>' +
463 '<button type="button" class="mxchat-kb-stop-processing mxch-btn mxch-btn-secondary" style="margin-top: 15px;">' +
464 '<span class="dashicons dashicons-controls-pause" style="margin-right: 5px;"></span>Stop Processing</button>' +
465 '</div>' +
466 '</div>');
467
468 $('body').append($progressModal);
469
470 // Handle stop button click
471 $progressModal.find('.mxchat-kb-stop-processing').on('click', function() {
472 abortProcessing = true;
473 $(this).prop('disabled', true).html('<span class="dashicons dashicons-update spin" style="margin-right: 5px;"></span>Stopping...');
474 if (currentXHR) {
475 currentXHR.abort();
476 }
477 });
478
479 // Process posts one by one
480 function processNext(index) {
481 // Check if processing was aborted
482 if (abortProcessing) {
483 finishProcessing(true); // Pass true to indicate abort
484 return;
485 }
486
487 if (index >= selectedPostIds.length) {
488 // All done
489 finishProcessing();
490 return;
491 }
492
493 const postId = selectedPostIds[index];
494 const percent = Math.round((index / totalToProcess) * 100);
495 const item = allItems.find(item => item.id === postId);
496 const isUpdate = item && item.already_processed;
497
498 // Update progress UI
499 $progressModal.find('.mxchat-kb-processing-status')
500 .text((isUpdate ? 'Updating' : 'Processing') + ' ' + (index + 1) + ' of ' + totalToProcess + '...');
501 $progressModal.find('.mxchat-kb-progress-fill').css('width', percent + '%');
502
503 // UPDATED: Prepare AJAX data with bot_id
504 const ajaxData = {
505 action: 'mxchat_process_selected_content',
506 nonce: mxchatSelector.nonce,
507 post_ids: [postId],
508 is_update: isUpdate
509 };
510
511 // Add bot_id if multi-bot is active and not default
512 if (currentBotId && currentBotId !== 'default') {
513 ajaxData.bot_id = currentBotId;
514 }
515
516 // Make AJAX request for this post (store reference for potential abort)
517 currentXHR = $.ajax({
518 url: mxchatSelector.ajaxurl,
519 method: 'POST',
520 data: ajaxData,
521 dataType: 'json',
522 success: function(response) {
523 if (response.success) {
524 if (isUpdate) {
525 updated++;
526 results.updated.push({
527 id: postId,
528 title: response.data.title || ('ID: ' + postId)
529 });
530 } else {
531 processed++;
532 results.success.push({
533 id: postId,
534 title: response.data.title || ('ID: ' + postId)
535 });
536 }
537
538 $progressModal.find('.mxchat-kb-current-item')
539 .text('Successfully ' + (isUpdate ? 'updated' : 'processed') + ': ' + response.data.title);
540 } else {
541 failed++;
542 results.failed.push({
543 id: postId,
544 error: response.data || 'Unknown error'
545 });
546
547 $progressModal.find('.mxchat-kb-current-item')
548 .text('Failed to ' + (isUpdate ? 'update' : 'process') + ' ID: ' + postId);
549 }
550
551 // Process next post
552 setTimeout(function() {
553 processNext(index + 1);
554 }, 500); // Small delay between requests
555 },
556 error: function(xhr, status, error) {
557 failed++;
558 results.failed.push({
559 id: postId,
560 error: error || 'Server error'
561 });
562
563 $progressModal.find('.mxchat-kb-current-item')
564 .text('Error ' + (isUpdate ? 'updating' : 'processing') + ' ID: ' + postId);
565
566 // Process next post
567 setTimeout(function() {
568 processNext(index + 1);
569 }, 500);
570 }
571 });
572 }
573
574 // Function to finish processing and show results
575 function finishProcessing(wasAborted) {
576 // Remove progress modal
577 $progressModal.remove();
578
579 // Determine notification type based on results
580 let notificationClass = 'success';
581 if (wasAborted) {
582 notificationClass = processed > 0 || updated > 0 ? 'warning' : 'info';
583 } else if (failed > 0) {
584 notificationClass = processed > 0 || updated > 0 ? 'warning' : 'error';
585 }
586
587 // Create summary message
588 let resultHTML = '<div class="mxchat-kb-notification ' + notificationClass + '">' +
589 '<h4>';
590
591 if (wasAborted) {
592 resultHTML += 'Processing stopped. ';
593 if (processed > 0 || updated > 0) {
594 resultHTML += 'Completed ' + (processed + updated) + ' of ' + totalToProcess + ' items before stopping';
595 } else {
596 resultHTML += 'No items were processed before stopping';
597 }
598 } else if (processed > 0 && updated > 0) {
599 resultHTML += 'Processed ' + processed + ' new items and updated ' + updated + ' existing items';
600 } else if (processed > 0) {
601 resultHTML += 'Processed ' + processed + ' items successfully';
602 } else if (updated > 0) {
603 resultHTML += 'Updated ' + updated + ' items successfully';
604 } else {
605 resultHTML += 'No items were processed successfully';
606 }
607
608 if (failed > 0 && !wasAborted) {
609 resultHTML += ' with ' + failed + ' failures';
610 }
611
612 resultHTML += '</h4>';
613
614 // Add details if there were failures
615 if (failed > 0) {
616 resultHTML += '<div class="mxchat-kb-results-details">';
617 resultHTML += '<h5>Failed Items:</h5><ul>';
618
619 results.failed.forEach(function(item) {
620 resultHTML += '<li><strong>ID: ' + item.id + '</strong>: ' + item.error + '</li>';
621 });
622
623 resultHTML += '</ul></div>';
624 }
625
626 resultHTML += '</div>';
627
628 // Show results in modal
629 $modal.find('.mxchat-kb-modal-content').prepend($(resultHTML));
630
631 // Clear selection
632 selectedItems.clear();
633 updateSelection();
634
635 // Enable button
636 $button.prop('disabled', false)
637 .text('Process Selected Content')
638 .removeClass('update-mode mixed-mode');
639 $('.mxchat-kb-selected-count').text('(0)');
640
641 // Only reload if there were successful operations
642 if (processed > 0 || updated > 0) {
643 // Refresh the knowledge base table with properly grouped entries
644 if (typeof window.refreshKnowledgeBaseTable === 'function') {
645 window.refreshKnowledgeBaseTable();
646 }
647
648 // Reload content list to update "already processed" status
649 setTimeout(function() {
650 loadContent();
651 }, 1000);
652 }
653 }
654
655 // Start processing the first post
656 processNext(0);
657 });
658
659 // Initialize - Set WordPress as the active option by default
660 $('.mxchat-import-box[data-option="wordpress"]').addClass('active');
661 });
662
663 // Navigation functionality for Knowledge Base page
664 jQuery(document).ready(function($) {
665
666 // Hook into the new navigation system using .mxch-nav-link
667 $(document).on('click', '.mxch-nav-link[data-target], .mxch-mobile-nav-link[data-target]', function() {
668 var target = $(this).data('target');
669
670 // Initialize Pinecone functionality when Pinecone section is activated
671 if (target === 'pinecone') {
672 setTimeout(function() {
673 if (typeof initPineconeFeatures === 'function') {
674 initPineconeFeatures();
675 }
676 }, 100);
677 }
678
679 // Initialize OpenAI Vector Store functionality when Vector Store section is activated
680 if (target === 'openai-vectorstore') {
681 setTimeout(function() {
682 if (typeof initVectorStoreFeatures === 'function') {
683 initVectorStoreFeatures();
684 }
685 }, 100);
686 }
687
688 // Check if Pinecone was changed and we're going to import section
689 if (target === 'import' && sessionStorage.getItem('mxchat_pinecone_changed') === 'true') {
690 sessionStorage.removeItem('mxchat_pinecone_changed');
691
692 // Show refresh notice
693 var $knowledgeCard = $('#import .mxch-card').eq(1);
694 if ($knowledgeCard.length > 0 && $knowledgeCard.find('.notice-warning').length === 0) {
695 var refreshNotice = $('<div class="notice notice-warning" style="margin: 15px 0; padding: 10px 15px;">' +
696 '<p style="margin: 0;">' +
697 '<span class="dashicons dashicons-info" style="color: #f0ad4e; margin-right: 5px;"></span>' +
698 'Database settings have changed. ' +
699 '<a href="#" onclick="location.reload(); return false;" style="font-weight: bold;">Click here to refresh</a> to see the updated knowledge base.' +
700 '</p></div>');
701
702 $knowledgeCard.prepend(refreshNotice);
703 }
704 }
705 });
706
707 // Also check on page load if we're already on one of these sections
708 setTimeout(function() {
709 if ($('#pinecone').is(':visible') || $('#pinecone.active').length > 0) {
710 if (typeof initPineconeFeatures === 'function') {
711 initPineconeFeatures();
712 }
713 }
714
715 if ($('#openai-vectorstore').is(':visible') || $('#openai-vectorstore.active').length > 0) {
716 if (typeof initVectorStoreFeatures === 'function') {
717 initVectorStoreFeatures();
718 }
719 }
720 }, 200);
721 });
722
723 // Pinecone and Vector Store functionality - global functions
724 var initPineconeFeatures, initVectorStoreFeatures;
725
726 (function($) {
727
728 // Helper function to update sidebar badge when integration is toggled
729 function updateSidebarBadge(section, isActive) {
730 // Find the nav item for this section (both desktop and mobile)
731 var $desktopNavItem = $('.mxch-nav-item[data-section="' + section + '"] .mxch-nav-link');
732 var $mobileNavItem = $('.mxch-mobile-nav-link[data-target="' + section + '"]');
733
734 // Remove existing badge if any
735 $desktopNavItem.find('.mxch-active-badge').remove();
736 $mobileNavItem.find('.mxch-active-badge').remove();
737
738 // Add badge if active
739 if (isActive) {
740 var badgeHtml = '<span class="mxch-nav-link-badge mxch-active-badge">Active</span>';
741 $desktopNavItem.append(badgeHtml);
742 $mobileNavItem.append(badgeHtml);
743 }
744 }
745
746 // Pinecone functionality
747 initPineconeFeatures = function() {
748 // Check for either old or new section ID
749 if ($('#pinecone').length === 0 && $('#mxchat-kb-tab-pinecone').length === 0) {
750 return;
751 }
752
753 initPineconeToggle();
754 initPineconeConnectionTest();
755 checkPineconeCompatibility();
756 };
757
758 function initPineconeToggle() {
759 // Remove any existing handlers to prevent duplicates
760 var $toggleInput = $('input[name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"]');
761 $toggleInput.off('change.pineconeToggle');
762
763 // Ensure the success notice exists in the settings div (add if not present)
764 // Check for both the JS-added class and any existing PHP-rendered success notice
765 var settingsDiv = $('.mxchat-pinecone-settings');
766 if (settingsDiv.length > 0 && settingsDiv.find('.mxch-notice-success').length === 0) {
767 var successNotice = $('<div class="mxch-notice mxch-notice-success mxchat-pinecone-enabled-notice" style="margin-bottom: 20px; display: none;">' +
768 '<svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>' +
769 '<span>Pinecone is enabled. All new knowledge base content will be stored in Pinecone.</span>' +
770 '</div>');
771 settingsDiv.prepend(successNotice);
772 }
773
774 // Add the toggle handler for UI only (auto-save will handle the actual saving)
775 $toggleInput.on('change.pineconeToggle', function() {
776 var $checkbox = $(this);
777 var isChecked = $checkbox.is(':checked');
778 var settingsDiv = $('.mxchat-pinecone-settings');
779 var enabledNotice = settingsDiv.find('.mxchat-pinecone-enabled-notice, .mxch-notice-success');
780
781 // Update the UI immediately
782 if (isChecked) {
783 settingsDiv.slideDown(300);
784 enabledNotice.slideDown(300);
785 } else {
786 enabledNotice.slideUp(300);
787 settingsDiv.slideUp(300);
788 }
789
790 // Update sidebar badge for Pinecone
791 updateSidebarBadge('pinecone', isChecked);
792 });
793
794 // Set initial state based on current checkbox value
795 var currentToggle = $('input[name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"]');
796 if (currentToggle.length > 0) {
797 var settingsDiv = $('.mxchat-pinecone-settings');
798 var enabledNotice = settingsDiv.find('.mxchat-pinecone-enabled-notice, .mxch-notice-success');
799 if (currentToggle.is(':checked')) {
800 settingsDiv.show();
801 enabledNotice.show();
802 } else {
803 settingsDiv.hide();
804 enabledNotice.hide();
805 }
806 }
807 }
808
809 function initPineconeConnectionTest() {
810 $('#test-pinecone-connection').off('click.pinecone');
811
812 $('#test-pinecone-connection').on('click.pinecone', function() {
813 var button = $(this);
814 var resultDiv = $('#connection-test-result');
815
816 var apiKey = $('#mxchat_pinecone_api_key').val();
817 var host = $('#mxchat_pinecone_host').val();
818 var index = $('#mxchat_pinecone_index').val();
819
820 if (!apiKey || !host || !index) {
821 resultDiv.html('<div class="notice notice-error"><p>Please fill in all required fields first.</p></div>').show();
822 return;
823 }
824
825 button.prop('disabled', true).text('Testing...');
826 resultDiv.hide();
827
828 var ajaxUrl = (typeof mxchatPromptsAdmin !== 'undefined') ? mxchatPromptsAdmin.ajax_url : ajaxurl;
829 var nonce = (typeof mxchatPromptsAdmin !== 'undefined') ? mxchatPromptsAdmin.prompts_setting_nonce :
830 (typeof mxchatAdmin !== 'undefined') ? mxchatAdmin.setting_nonce : '';
831
832 $.ajax({
833 url: ajaxUrl,
834 type: 'POST',
835 data: {
836 action: 'mxchat_test_pinecone_connection',
837 _ajax_nonce: nonce,
838 api_key: apiKey,
839 host: host,
840 index_name: index
841 },
842 success: function(response) {
843 if (response.success) {
844 resultDiv.html('<div class="notice notice-success"><p><span class="dashicons dashicons-yes-alt"></span> ' + response.data.message + '</p></div>');
845 } else {
846 resultDiv.html('<div class="notice notice-error"><p><span class="dashicons dashicons-warning"></span> ' + response.data.message + '</p></div>');
847 }
848 resultDiv.show();
849 },
850 error: function() {
851 resultDiv.html('<div class="notice notice-error"><p>Connection test failed. Please check your settings.</p></div>').show();
852 },
853 complete: function() {
854 button.prop('disabled', false).text('Test Connection');
855 }
856 });
857 });
858 }
859
860 function checkPineconeCompatibility() {
861 if ($('.mxchat-pinecone-compatibility-notice').length > 0) {
862 return;
863 }
864
865 var hasOldAddon = $('body').hasClass('mxchat-pinecone-addon-active') ||
866 $('.pcm-card').length > 0;
867
868 if (hasOldAddon) {
869 var compatibilityNotice = $(`
870 <div class="notice notice-info mxchat-pinecone-compatibility-notice">
871 <p><strong>Pinecone Integration Notice:</strong> We've detected you have the Pinecone add-on installed.
872 Pinecone functionality is now built into the core plugin. You can safely deactivate the separate
873 Pinecone add-on after confirming your settings are migrated below.</p>
874 </div>
875 `);
876
877 $('#mxchat-kb-tab-pinecone .mxchat-card').prepend(compatibilityNotice);
878
879 migratePineconeSettings();
880 }
881 }
882
883 function migratePineconeSettings() {
884 if (typeof ajaxurl !== 'undefined') {
885 $.ajax({
886 url: ajaxurl,
887 type: 'POST',
888 data: {
889 action: 'mxchat_migrate_pinecone_settings',
890 _ajax_nonce: (typeof mxchatAdmin !== 'undefined') ? mxchatAdmin.setting_nonce : ''
891 },
892 success: function(response) {
893 if (response.success && response.data.migrated) {
894 location.reload();
895 }
896 },
897 error: function() {
898 //console.log('Pinecone settings migration not available');
899 }
900 });
901 }
902 }
903
904 // ============================================
905 // OpenAI Vector Store functionality
906 // ============================================
907 initVectorStoreFeatures = function() {
908 if ($('#openai-vectorstore').length === 0) {
909 return;
910 }
911
912 initVectorStoreToggle();
913 };
914
915 function initVectorStoreToggle() {
916 // Remove any existing handlers to prevent duplicates
917 var $toggleInput = $('input[name="mxchat_openai_vectorstore_options[mxchat_use_openai_vectorstore]"]');
918 $toggleInput.off('change.vectorstoreToggle');
919
920 // Ensure the success notice exists in the settings div (add if not present)
921 // Check for both the JS-added class and any existing PHP-rendered success notice
922 var settingsDiv = $('.mxchat-vectorstore-settings');
923 if (settingsDiv.length > 0 && settingsDiv.find('.mxch-notice-success').length === 0) {
924 var successNotice = $('<div class="mxch-notice mxch-notice-success mxchat-vectorstore-enabled-notice" style="margin-bottom: 20px; display: none;">' +
925 '<svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>' +
926 '<span>OpenAI Vector Store is enabled. Queries will search your Vector Store for relevant content.</span>' +
927 '</div>');
928 settingsDiv.prepend(successNotice);
929 }
930
931 // Add the toggle handler for UI only (form submit will handle the actual saving)
932 $toggleInput.on('change.vectorstoreToggle', function() {
933 var $checkbox = $(this);
934 var isChecked = $checkbox.is(':checked');
935 var settingsDiv = $('.mxchat-vectorstore-settings');
936 var enabledNotice = settingsDiv.find('.mxchat-vectorstore-enabled-notice, .mxch-notice-success');
937
938 // Update the UI immediately
939 if (isChecked) {
940 settingsDiv.slideDown(300);
941 enabledNotice.slideDown(300);
942 } else {
943 enabledNotice.slideUp(300);
944 settingsDiv.slideUp(300);
945 }
946
947 // Update sidebar badge for OpenAI Vector Store
948 updateSidebarBadge('openai-vectorstore', isChecked);
949 });
950
951 // Set initial state based on current checkbox value
952 var currentToggle = $('input[name="mxchat_openai_vectorstore_options[mxchat_use_openai_vectorstore]"]');
953 if (currentToggle.length > 0) {
954 var settingsDiv = $('.mxchat-vectorstore-settings');
955 var enabledNotice = settingsDiv.find('.mxchat-vectorstore-enabled-notice, .mxch-notice-success');
956 if (currentToggle.is(':checked')) {
957 settingsDiv.show();
958 enabledNotice.show();
959 } else {
960 settingsDiv.hide();
961 enabledNotice.hide();
962 }
963 }
964 }
965
966 })(jQuery);