PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.2.0
MxChat – AI Chatbot & Content Generation for WordPress v2.2.0
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 2.2.0, at js/content-selector.js

568 lines 22.4 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');
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
38 // Handle import option box clicks
39 $('.mxchat-import-box').on('click', function() {
40 const $box = $(this);
41 const option = $box.data('option');
42
43 // Update active state
44 $('.mxchat-import-box').removeClass('active');
45 $box.addClass('active');
46
47 // Hide all input areas
48 $('#mxchat-url-input-area, #mxchat-content-input-area').hide();
49
50 // Handle different import options
51 switch (option) {
52 case 'wordpress':
53 // WordPress import opens the modal directly
54 $modal.show();
55 // Reset to first page when opening the modal
56 currentPage = 1;
57 loadContent();
58 break;
59
60 case 'pdf':
61 case 'sitemap':
62 case 'url':
63 // Show URL input area with appropriate placeholder
64 $('#mxchat-url-input-area').show();
65 $('#sitemap_url').attr('placeholder', $box.data('placeholder'));
66 $('#import_type').val(option);
67
68 // Update the description text based on the import type
69 let descriptionText = '';
70 if (option === 'pdf') {
71 descriptionText = 'Import a PDF document by entering its URL above.';
72 } else if (option === 'sitemap') {
73 descriptionText = 'Enter a content-specific sub-sitemap URL, not the sitemap index.';
74 } else if (option === 'url') {
75 descriptionText = 'Import content from any webpage by entering its URL.';
76 }
77 $('#url-description-text').text(descriptionText);
78 break;
79
80 case 'content':
81 // Show content input area
82 $('#mxchat-content-input-area').show();
83 break;
84 }
85 });
86
87 // Load content via AJAX
88 function loadContent() {
89 $loading.show();
90 $contentList.find('.mxchat-kb-content-item').remove();
91
92 const data = {
93 action: 'mxchat_get_content_list',
94 nonce: mxchatSelector.nonce,
95 page: currentPage,
96 per_page: 20,
97 search: $searchInput.val(),
98 post_type: $typeFilter.val(),
99 post_status: $statusFilter.val(),
100 processed_filter: $processedFilter.val() // This needs to match the parameter name in PHP
101 };
102
103 //console.log('Loading content for page', currentPage, 'with filters:', data);
104
105 $.ajax({
106 url: mxchatSelector.ajaxurl,
107 data: data,
108 method: 'GET',
109 dataType: 'json',
110 success: function(response) {
111 $loading.hide();
112
113 if (response.success && response.data.items && response.data.items.length > 0) {
114 // Store the items directly
115 let items = response.data.items;
116
117 if (items.length > 0) {
118 renderContentItems(items);
119 renderPagination(parseInt(response.data.current_page), parseInt(response.data.total_pages));
120
121 // Update state
122 allItems = items;
123 totalPages = parseInt(response.data.total_pages);
124 currentPage = parseInt(response.data.current_page);
125
126 // Update select all checkbox based on current selection
127 updateSelectAllState();
128 } else {
129 displayNoResults($processedFilter.val());
130 }
131 } else {
132 displayNoResults($processedFilter.val());
133 }
134 },
135 error: function(xhr, status, error) {
136 $loading.hide();
137 console.error('AJAX Error:', status, error);
138 $contentList.html('<div class="mxchat-kb-error">Error loading content. Please try again.</div>');
139 // Clear pagination on error
140 $pagination.empty();
141 }
142 });
143 }
144
145 // Helper function to display appropriate "no results" message
146 function displayNoResults(processedStatus) {
147 let message = 'No content found matching your criteria.';
148
149 if (processedStatus === 'processed') {
150 message = 'No content found in knowledge base.';
151 } else if (processedStatus === 'unprocessed') {
152 message = 'All content is already in knowledge base.';
153 }
154
155 $contentList.html('<div class="mxchat-kb-no-results">' + message + '</div>');
156 // Clear pagination when no results
157 $pagination.empty();
158 }
159
160 // Render content items
161 function renderContentItems(items) {
162 let html = '';
163
164 items.forEach(function(item) {
165 const isSelected = selectedItems.has(item.id);
166 const isProcessed = item.already_processed;
167
168 // Updated badge text
169 const badgeText = isProcessed ? 'In Knowledge Base' : 'Not In Knowledge Base';
170 const badgeClass = isProcessed ? 'mxchat-kb-processed-badge' : 'mxchat-kb-unprocessed-badge';
171
172
173 html += `
174 <div class="mxchat-kb-content-item ${isProcessed ? 'processed' : ''}" data-id="${item.id}">
175 <div class="mxchat-kb-content-checkbox">
176 <input type="checkbox" id="content-${item.id}" ${isSelected ? 'checked' : ''}>
177 </div>
178 <div class="mxchat-kb-content-details">
179 <div class="mxchat-kb-content-title">
180 <a href="${item.permalink}" target="_blank">${item.title}</a>
181 <span class="${badgeClass}">${badgeText}</span>
182 ${isProcessed ? '<span class="mxchat-kb-last-updated">Last updated: ' + item.processed_date + '</span>' : ''}
183 </div>
184 <div class="mxchat-kb-content-meta">
185 <span class="mxchat-kb-content-type">${item.type}</span>
186 <span class="mxchat-kb-content-date">${item.date}</span>
187 <span class="mxchat-kb-content-words">${item.word_count} words</span>
188 </div>
189 <div class="mxchat-kb-content-excerpt">${item.excerpt}</div>
190 </div>
191 </div>
192 `;
193 });
194
195 $contentList.html(html);
196
197 // Add event listeners for checkboxes using delegation for better performance
198 $contentList.off('change', 'input[type="checkbox"]').on('change', 'input[type="checkbox"]', function() {
199 const $checkbox = $(this);
200 const itemId = parseInt($checkbox.closest('.mxchat-kb-content-item').data('id'));
201
202 if ($checkbox.is(':checked')) {
203 selectedItems.add(itemId);
204 } else {
205 selectedItems.delete(itemId);
206 }
207
208 updateSelection();
209 });
210 }
211
212 // Render pagination - FIXED VERSION
213 function renderPagination(currentPage, totalPages) {
214 // Clear existing pagination first
215 $pagination.empty();
216
217 // Don't render pagination if only one page
218 if (totalPages <= 1) {
219 return;
220 }
221
222 let html = '<div class="mxchat-kb-pagination-links">';
223
224 // Previous button
225 if (currentPage > 1) {
226 html += '<a href="#" class="mxchat-kb-page-link prev" data-page="' + (currentPage - 1) + '">&laquo; Previous</a>';
227 }
228
229 // Page numbers
230 const startPage = Math.max(1, currentPage - 2);
231 const endPage = Math.min(totalPages, startPage + 4);
232
233 for (let i = startPage; i <= endPage; i++) {
234 if (i === currentPage) {
235 html += '<span class="mxchat-kb-page-current">' + i + '</span>';
236 } else {
237 html += '<a href="#" class="mxchat-kb-page-link" data-page="' + i + '">' + i + '</a>';
238 }
239 }
240
241 // Next button
242 if (currentPage < totalPages) {
243 html += '<a href="#" class="mxchat-kb-page-link next" data-page="' + (currentPage + 1) + '">Next &raquo;</a>';
244 }
245
246 html += '</div>';
247
248 $pagination.html(html);
249 }
250
251 // Handle pagination clicks directly on the document
252 $(document).on('click', '.mxchat-kb-page-link', function(e) {
253 e.preventDefault();
254 const newPage = parseInt($(this).data('page'));
255 //console.log('Pagination clicked: changing from page', currentPage, 'to', newPage);
256
257 // Only reload if the page actually changed
258 if (currentPage !== newPage) {
259 currentPage = newPage;
260 loadContent();
261 }
262 });
263
264 // Update selection counts and button state
265 function updateSelection() {
266 const selectedCount = selectedItems.size;
267 $selectionCount.text(selectedCount + ' ' + (selectedCount === 1 ? 'selected' : 'selected'));
268 $('.mxchat-kb-selected-count').text('(' + selectedCount + ')');
269
270 // Determine if any selected items are already processed
271 const hasProcessedItems = Array.from(selectedItems).some(id => {
272 const item = allItems.find(item => item.id === id);
273 return item && item.already_processed;
274 });
275
276 if (selectedCount > 0) {
277 $processButton.prop('disabled', false);
278
279 // Update button text based on selection
280 if (hasProcessedItems && selectedCount === 1) {
281 $processButton.text('Update Selected Content (1)').addClass('update-mode');
282 } else if (hasProcessedItems && selectedCount > 1) {
283 $processButton.text('Process/Update Selected (' + selectedCount + ')').addClass('mixed-mode');
284 } else {
285 $processButton.text('Process Selected Content (' + selectedCount + ')').removeClass('update-mode mixed-mode');
286 }
287 } else {
288 $processButton.prop('disabled', true);
289 $processButton.text('Process Selected Content').removeClass('update-mode mixed-mode');
290 $('.mxchat-kb-selected-count').text('(0)');
291 }
292
293 updateSelectAllState();
294 }
295
296 // Update "Select All" checkbox state
297 function updateSelectAllState() {
298 const availableItems = allItems.length;
299 const selectedAvailableItems = allItems.filter(item => selectedItems.has(item.id)).length;
300
301 if (availableItems === 0) {
302 $selectAll.prop('checked', false);
303 $selectAll.prop('disabled', true);
304 } else if (selectedAvailableItems === availableItems) {
305 $selectAll.prop('checked', true);
306 } else {
307 $selectAll.prop('checked', false);
308 }
309 }
310
311 // Handle Select All checkbox
312 $selectAll.on('change', function() {
313 const isChecked = $(this).is(':checked');
314
315 $contentList.find('.mxchat-kb-content-item input[type="checkbox"]').each(function() {
316 const $checkbox = $(this);
317 const $item = $checkbox.closest('.mxchat-kb-content-item');
318 const itemId = parseInt($item.data('id'));
319
320 $checkbox.prop('checked', isChecked);
321
322 if (isChecked) {
323 selectedItems.add(itemId);
324 } else {
325 selectedItems.delete(itemId);
326 }
327 });
328
329 updateSelection();
330 });
331
332 // Handle search input
333 let searchTimer;
334 $searchInput.on('keyup', function() {
335 clearTimeout(searchTimer);
336 searchTimer = setTimeout(function() {
337 currentPage = 1; // Reset to first page on new search
338 loadContent();
339 }, 500);
340 });
341
342 // Handle filter changes
343 $typeFilter.add($statusFilter).add($processedFilter).on('change', function() {
344 currentPage = 1; // Reset to first page on filter change
345 selectedItems.clear(); // Clear selection when filter changes
346 loadContent();
347 });
348
349 // Process selected content
350 $processButton.on('click', function() {
351 if (selectedItems.size === 0) {
352 return;
353 }
354
355 const $button = $(this);
356 $button.prop('disabled', true);
357
358 // Update button text based on mode
359 if ($button.hasClass('update-mode')) {
360 $button.text('Updating...');
361 } else if ($button.hasClass('mixed-mode')) {
362 $button.text('Processing/Updating...');
363 } else {
364 $button.text('Processing...');
365 }
366
367 // Convert selected items to array
368 const selectedPostIds = Array.from(selectedItems);
369 const totalToProcess = selectedPostIds.length;
370 let processed = 0;
371 let updated = 0;
372 let failed = 0;
373 const results = {
374 success: [],
375 updated: [],
376 failed: []
377 };
378
379 // Create a modal to show progress
380 const $progressModal = $('<div class="mxchat-kb-processing-overlay">' +
381 '<div class="mxchat-kb-processing-content">' +
382 '<h3>Processing Content</h3>' +
383 '<p class="mxchat-kb-processing-status">Processing 1 of ' + totalToProcess + '...</p>' +
384 '<div class="mxchat-kb-progress-bar"><div class="mxchat-kb-progress-fill" style="width: 0%"></div></div>' +
385 '<p class="mxchat-kb-current-item"></p>' +
386 '</div>' +
387 '</div>');
388
389 $('body').append($progressModal);
390
391 // Process posts one by one
392 function processNext(index) {
393 if (index >= selectedPostIds.length) {
394 // All done
395 finishProcessing();
396 return;
397 }
398
399 const postId = selectedPostIds[index];
400 const percent = Math.round((index / totalToProcess) * 100);
401 const item = allItems.find(item => item.id === postId);
402 const isUpdate = item && item.already_processed;
403
404 // Update progress UI
405 $progressModal.find('.mxchat-kb-processing-status')
406 .text((isUpdate ? 'Updating' : 'Processing') + ' ' + (index + 1) + ' of ' + totalToProcess + '...');
407 $progressModal.find('.mxchat-kb-progress-fill').css('width', percent + '%');
408
409 // Make AJAX request for this post
410 $.ajax({
411 url: mxchatSelector.ajaxurl,
412 method: 'POST',
413 data: {
414 action: 'mxchat_process_selected_content',
415 nonce: mxchatSelector.nonce,
416 post_ids: [postId],
417 is_update: isUpdate
418 },
419 dataType: 'json',
420 success: function(response) {
421 if (response.success) {
422 if (isUpdate) {
423 updated++;
424 results.updated.push({
425 id: postId,
426 title: response.data.title || ('ID: ' + postId)
427 });
428 } else {
429 processed++;
430 results.success.push({
431 id: postId,
432 title: response.data.title || ('ID: ' + postId)
433 });
434 }
435
436 $progressModal.find('.mxchat-kb-current-item')
437 .text('Successfully ' + (isUpdate ? 'updated' : 'processed') + ': ' + response.data.title);
438 } else {
439 failed++;
440 results.failed.push({
441 id: postId,
442 error: response.data || 'Unknown error'
443 });
444
445 $progressModal.find('.mxchat-kb-current-item')
446 .text('Failed to ' + (isUpdate ? 'update' : 'process') + ' ID: ' + postId);
447 }
448
449 // Process next post
450 setTimeout(function() {
451 processNext(index + 1);
452 }, 500); // Small delay between requests
453 },
454 error: function(xhr, status, error) {
455 failed++;
456 results.failed.push({
457 id: postId,
458 error: error || 'Server error'
459 });
460
461 $progressModal.find('.mxchat-kb-current-item')
462 .text('Error ' + (isUpdate ? 'updating' : 'processing') + ' ID: ' + postId);
463
464 // Process next post
465 setTimeout(function() {
466 processNext(index + 1);
467 }, 500);
468 }
469 });
470 }
471
472 // Function to finish processing and show results
473 function finishProcessing() {
474 // Remove progress modal
475 $progressModal.remove();
476
477 // Determine notification type based on results
478 let notificationClass = 'success';
479 if (failed > 0) {
480 notificationClass = processed > 0 || updated > 0 ? 'warning' : 'error';
481 }
482
483 // Create summary message
484 let resultHTML = '<div class="mxchat-kb-notification ' + notificationClass + '">' +
485 '<h4>';
486
487 if (processed > 0 && updated > 0) {
488 resultHTML += 'Processed ' + processed + ' new items and updated ' + updated + ' existing items';
489 } else if (processed > 0) {
490 resultHTML += 'Processed ' + processed + ' items successfully';
491 } else if (updated > 0) {
492 resultHTML += 'Updated ' + updated + ' items successfully';
493 } else {
494 resultHTML += 'No items were processed successfully';
495 }
496
497 if (failed > 0) {
498 resultHTML += ' with ' + failed + ' failures';
499 }
500
501 resultHTML += '</h4>';
502
503 // Add details if there were failures
504 if (failed > 0) {
505 resultHTML += '<div class="mxchat-kb-results-details">';
506 resultHTML += '<h5>Failed Items:</h5><ul>';
507
508 results.failed.forEach(function(item) {
509 resultHTML += '<li><strong>ID: ' + item.id + '</strong>: ' + item.error + '</li>';
510 });
511
512 resultHTML += '</ul></div>';
513 }
514
515 resultHTML += '</div>';
516
517 // Show results in modal
518 $modal.find('.mxchat-kb-modal-content').prepend($(resultHTML));
519
520 // Clear selection
521 selectedItems.clear();
522 updateSelection();
523
524 // Enable button
525 $button.prop('disabled', false)
526 .text('Process Selected Content')
527 .removeClass('update-mode mixed-mode');
528 $('.mxchat-kb-selected-count').text('(0)');
529
530 // Only reload if there were successful operations
531 if (processed > 0 || updated > 0) {
532 // Reload after delay
533 setTimeout(function() {
534 // Reload the knowledge base table - if this function exists
535 if (typeof reloadKnowledgeBaseTable === 'function') {
536 reloadKnowledgeBaseTable();
537 } else {
538 // Fallback: reload content list instead of full page reload
539 loadContent();
540 }
541 }, 3000);
542 }
543 }
544
545 // Start processing the first post
546 processNext(0);
547 });
548
549 // Initialize - Set WordPress as the active option by default
550 $('.mxchat-import-box[data-option="wordpress"]').addClass('active');
551 });
552
553 // Tab switching functionality - Keep this separate
554 jQuery(document).ready(function($) {
555 // Tab switching functionality
556 $('.mxchat-kb-tab-button').on('click', function() {
557 // Remove active class from all buttons and content
558 $('.mxchat-kb-tab-button').removeClass('active');
559 $('.mxchat-kb-tab-content').removeClass('active');
560
561 // Add active class to clicked button
562 $(this).addClass('active');
563
564 // Show the corresponding tab content
565 var tabId = $(this).data('tab');
566 $('#mxchat-kb-tab-' + tabId).addClass('active');
567 });
568 });