PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.2.6
MxChat – AI Chatbot & Content Generation for WordPress v2.2.6
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 / knowledge-processing.js

knowledge-processing.js in MxChat – AI Chatbot & Content Generation for WordPress 2.2.6, at js/knowledge-processing.js

1,010 lines 42.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1
2 jQuery(document).ready(function($) {
3 // Track if a form has been submitted to trigger updates
4 let formSubmitted = false;
5
6 // Global interval ID to manage the polling
7 let updateIntervalId = null;
8
9
10 $(document).on('click', '.mxchat-dismiss-button', function() {
11 const $button = $(this);
12 const $card = $button.closest('.mxchat-status-card');
13
14 // Determine card type from data attribute or content
15 let cardType = $card.data('card-type');
16 if (!cardType) {
17 // Fallback: determine from content
18 cardType = $card.find('h4').text().includes('PDF') ? 'pdf' : 'sitemap';
19 }
20
21 // Fade out and remove the card
22 $card.fadeOut(300, function() {
23 $(this).remove();
24 });
25
26 // Clear the completed status on the server
27 $.ajax({
28 url: ajaxurl,
29 type: 'POST',
30 data: {
31 action: 'mxchat_dismiss_completed_status',
32 nonce: mxchatAdmin.status_nonce,
33 card_type: cardType
34 },
35 success: function(response) {
36 //console.log('MxChat: Completed status dismissed');
37 },
38 error: function(xhr, status, error) {
39 console.error('MxChat: Error dismissing status:', error);
40 }
41 });
42 });
43
44 // Check if we're on the right admin page with status cards or import forms
45 if ($('.mxchat-status-card').length > 0 || $('.mxchat-import-options').length > 0) {
46 //console.log('MxChat: Status update script initialized');
47 // Initialize AJAX status updates
48 initStatusUpdates();
49 }
50
51 // Initialize status updates
52 // Initialize status updates
53 function initStatusUpdates() {
54 // Get the refresh interval (default to 2 seconds for more responsive updates)
55 const refreshInterval = parseInt(mxchatAdmin.status_refresh_interval || 2000);
56
57 // Check if there are active status cards
58 const hasActiveStatus = $('.mxchat-status-card').length > 0;
59
60 // Set up form submission listeners
61 $('#mxchat-url-form, #mxchat-content-form').on('submit', function() {
62 //console.log('MxChat: Form submitted, will start checking for updates');
63 formSubmitted = true;
64
65 // Store submission info in sessionStorage to persist through redirects
66 sessionStorage.setItem('mxchat_form_submitted', 'true');
67 sessionStorage.setItem('mxchat_form_submitted_time', Date.now());
68
69 // Start checking for status updates right away
70 startPolling(refreshInterval);
71
72 // Create a temporary message
73 if ($('.mxchat-processing-message').length === 0) {
74 const message = $('<div class="mxchat-processing-message" style="text-align: center; padding: 15px; background: #f0f7ff; border-radius: 8px; margin-top: 15px;">Processing request... Status will update automatically.</div>');
75 $('.mxchat-import-section').after(message);
76
77 // Fade out after 5 seconds
78 setTimeout(function() {
79 message.fadeOut(500, function() {
80 $(this).remove();
81 });
82 }, 5000);
83 }
84 });
85
86 // Listen for import option clicks
87 $('.mxchat-import-box').on('click', function() {
88 const option = $(this).data('option');
89 //console.log('MxChat: Import option clicked - ' + option);
90 });
91
92 // Check if we recently submitted a form (within last 60 seconds for sitemap processing)
93 if (sessionStorage.getItem('mxchat_form_submitted') === 'true') {
94 const submittedTime = parseInt(sessionStorage.getItem('mxchat_form_submitted_time') || '0');
95 if (Date.now() - submittedTime < 60000) { // 60 seconds
96 //console.log('MxChat: Detected recent form submission via sessionStorage');
97 formSubmitted = true;
98 } else {
99 // Clear old submission data
100 sessionStorage.removeItem('mxchat_form_submitted');
101 sessionStorage.removeItem('mxchat_form_submitted_time');
102 }
103 }
104
105 // Attach event listener to stop button to clear the interval
106 $('.mxchat-stop-form').on('submit', function() {
107 //console.log('MxChat: Stop processing requested, clearing update interval');
108 stopPolling();
109 sessionStorage.removeItem('mxchat_form_submitted');
110 sessionStorage.removeItem('mxchat_form_submitted_time');
111 });
112
113 // Start the interval for automatic updates if we have status cards or a form was submitted
114 if (hasActiveStatus || formSubmitted) {
115 //console.log('MxChat: Starting automatic status checks');
116 startPolling(refreshInterval);
117 }
118 }
119
120 // Function to start polling
121 function startPolling(interval) {
122 // Clear any existing interval first
123 stopPolling();
124
125 // Do an initial fetch immediately
126 fetchStatusUpdates();
127
128 // Set up new interval
129 updateIntervalId = setInterval(function() {
130 fetchStatusUpdates();
131 }, interval);
132
133 //console.log('MxChat: Polling started with interval', interval);
134 }
135
136 // Function to stop polling
137 function stopPolling() {
138 if (updateIntervalId !== null) {
139 clearInterval(updateIntervalId);
140 updateIntervalId = null;
141 //console.log('MxChat: Polling stopped');
142 }
143 }
144
145 // Fetch status updates from the server
146 function fetchStatusUpdates() {
147 // If user is actively viewing the failed URLs or pages, don't refresh as frequently
148 const $details = $('.mxchat-failed-urls-container details, .mxchat-failed-pages-container details');
149 const isUserViewing = $details.length > 0 && $details.prop('open');
150
151 // If details are open, we'll refresh at a slower rate
152 if (isUserViewing) {
153 // Alternative: Update less frequently when details are open
154 setTimeout(function() {
155 performStatusUpdate(false); // Pass false for normal updates
156 }, 5000); // Slow down updates to every 5 seconds when details are open
157 } else {
158 performStatusUpdate(false); // Pass false for normal updates
159 }
160 }
161
162 // Perform the actual AJAX request
163 function performStatusUpdate(clearCompleted = false) {
164 //console.log('MxChat: Checking for status updates...');
165
166 $.ajax({
167 url: ajaxurl,
168 type: 'POST',
169 data: {
170 action: 'mxchat_get_status_updates',
171 nonce: mxchatAdmin.status_nonce,
172 clear_completed: clearCompleted ? 'true' : 'false'
173 },
174 success: function(response) {
175 //console.log('MxChat: Status update received', response);
176
177 // Log specific status details for debugging
178 if (response.sitemap_status) {
179 //console.log('Sitemap status:', response.sitemap_status.status, 'Processed:', response.sitemap_status.processed_urls, 'Total:', response.sitemap_status.total_urls);
180 }
181
182 // Check for completion BEFORE updating UI
183 let shouldStopPolling = false;
184
185 if (response.sitemap_status && response.sitemap_status.status === 'complete') {
186 //console.log('MxChat: Sitemap processing complete');
187 shouldStopPolling = true;
188 }
189
190 if (response.pdf_status && response.pdf_status.status === 'complete') {
191 //console.log('MxChat: PDF processing complete');
192 shouldStopPolling = true;
193 }
194
195 // Always update UI
196 if ((response && response.is_processing) || formSubmitted || shouldStopPolling) {
197 updateStatusUI(response);
198 }
199
200 // Show single URL status if available and no active processing
201 if (response.single_url_status && !response.is_processing) {
202 updateSingleUrlStatus(response.single_url_status);
203 }
204
205 // Handle completion - REMOVE THE AUTOMATIC PAGE RELOAD
206 if (shouldStopPolling) {
207 // Clear session storage
208 sessionStorage.removeItem('mxchat_form_submitted');
209 sessionStorage.removeItem('mxchat_form_submitted_time');
210
211 // Stop polling
212 stopPolling();
213
214 // DON'T clear the completed status automatically
215 // DON'T reload the page automatically
216
217 return; // Exit early
218 }
219
220 // Reset form submitted flag if no active processing
221 if (!response.is_processing) {
222 formSubmitted = false;
223 sessionStorage.removeItem('mxchat_form_submitted');
224 sessionStorage.removeItem('mxchat_form_submitted_time');
225 stopPolling();
226 }
227 },
228 error: function(xhr, status, error) {
229 console.error('MxChat: Status update failed:', error);
230 }
231 });
232 }
233
234 function addDismissButtonToCompletedCards() {
235 // Add dismiss buttons to completed cards that don't have them
236 $('.mxchat-status-card').each(function() {
237 const $card = $(this);
238 const $badge = $card.find('.mxchat-status-badge');
239
240 // Check if this is a completed card and doesn't already have a dismiss button
241 if (($badge.hasClass('mxchat-status-success') || $badge.hasClass('mxchat-status-warning')) &&
242 $card.find('.mxchat-dismiss-button').length === 0) {
243
244 // Look for existing action buttons container, or create one
245 let $actionContainer = $card.find('.mxchat-action-buttons');
246
247 if ($actionContainer.length === 0) {
248 // Create the action buttons container if it doesn't exist
249 $actionContainer = $('<div class="mxchat-action-buttons"></div>');
250 $card.find('.mxchat-status-header').append($actionContainer);
251 }
252
253 // Add dismiss button WITHOUT inline styles
254 const dismissButton = $('<button type="button" class="mxchat-dismiss-button">Dismiss</button>');
255
256 dismissButton.on('click', function() {
257 // Fade out and remove the card
258 $card.fadeOut(300, function() {
259 $(this).remove();
260 });
261
262 // Clear the completed status on the server
263 $.ajax({
264 url: ajaxurl,
265 type: 'POST',
266 data: {
267 action: 'mxchat_dismiss_completed_status',
268 nonce: mxchatAdmin.status_nonce,
269 card_type: $card.find('h4').text().includes('PDF') ? 'pdf' : 'sitemap'
270 },
271 success: function(response) {
272 //console.log('MxChat: Completed status dismissed');
273 }
274 });
275 });
276
277 $actionContainer.append(dismissButton);
278 }
279 });
280 }
281
282 // Update the UI with status information
283 function updateStatusUI(data) {
284 // Update PDF status if available
285 if (data.pdf_status) {
286 updatePdfStatus(data.pdf_status);
287 }
288
289 // Update sitemap status if available
290 if (data.sitemap_status) {
291 updateSitemapStatus(data.sitemap_status);
292 }
293
294 // Handle single URL status if available and no active processing
295 if (data.single_url_status && !data.is_processing) {
296 updateSingleUrlStatus(data.single_url_status);
297 } else if (data.is_processing) {
298 // Hide single URL status while processing
299 $('#mxchat-single-url-status-container').hide();
300 }
301
302 // Add dismiss buttons to any completed cards
303 addDismissButtonToCompletedCards();
304 }
305
306 // Update PDF status card
307 function updatePdfStatus(status) {
308 // Check if PDF card exists
309 let $pdfCard = $('.mxchat-status-card:contains("PDF Processing")');
310
311 // If no card exists but we have status, create it
312 if ($pdfCard.length === 0 && status) {
313 //console.log('MxChat: Creating new PDF status card');
314 createPdfStatusCard(status);
315 $pdfCard = $('.mxchat-status-card:contains("PDF Processing")');
316 }
317
318 // If card exists, update it
319 if ($pdfCard.length > 0) {
320 // Update progress bar
321 $pdfCard.find('.mxchat-progress-fill').css('width', status.percentage + '%');
322
323 // Update progress text
324 let progressText = 'Progress: ' + status.processed_pages + ' of ' +
325 status.total_pages + ' pages (' + status.percentage + '%)';
326
327 $pdfCard.find('.mxchat-status-details p:first').text(progressText);
328
329 // Update failed pages count if exists
330 const $failedText = $pdfCard.find('.mxchat-status-details p:contains("Failed pages")');
331 if (status.failed_pages && status.failed_pages > 0) {
332 if ($failedText.length === 0) {
333 // Add failed pages text after progress
334 $pdfCard.find('.mxchat-status-details p:first').after(
335 '<p><strong>Failed pages:</strong> ' + status.failed_pages + '</p>'
336 );
337 } else {
338 $failedText.html('<strong>Failed pages:</strong> ' + status.failed_pages);
339 }
340 } else if ($failedText.length > 0) {
341 $failedText.remove();
342 }
343
344 // Update status text
345 const $statusText = $pdfCard.find('.mxchat-status-details p:contains("Status:")');
346 if ($statusText.length > 0) {
347 $statusText.text('Status: ' + status.status.charAt(0).toUpperCase() + status.status.slice(1));
348 }
349
350 // Update last update text
351 const $lastUpdateText = $pdfCard.find('.mxchat-status-details p:contains("Last update:")');
352 if ($lastUpdateText.length > 0) {
353 $lastUpdateText.text('Last update: ' + status.last_update);
354 }
355
356 // Update status badges
357 $pdfCard.find('.mxchat-status-badge').remove();
358 if (status.status === 'error') {
359 $pdfCard.find('.mxchat-status-header').append('<span class="mxchat-status-badge mxchat-status-failed">Error</span>');
360 } else if (status.status === 'complete') {
361 if (status.failed_pages && status.failed_pages > 0) {
362 $pdfCard.find('.mxchat-status-header').append('<span class="mxchat-status-badge mxchat-status-warning">Completed with ' + status.failed_pages + ' failures</span>');
363 } else {
364 $pdfCard.find('.mxchat-status-header').append('<span class="mxchat-status-badge mxchat-status-success">Complete</span>');
365 }
366 }
367
368 // Update or add completion summary
369 if (status.completion_summary) {
370 let $summaryContainer = $pdfCard.find('.mxchat-completion-summary');
371 if ($summaryContainer.length === 0) {
372 const summaryHtml = '<div class="mxchat-completion-summary">' +
373 '<h5>Processing Summary</h5>' +
374 '<p><strong>Total Pages:</strong> ' + status.completion_summary.total_pages + '</p>' +
375 '<p><strong>Successful:</strong> ' + status.completion_summary.successful_pages + '</p>' +
376 '<p><strong>Failed:</strong> ' + status.completion_summary.failed_pages + '</p>' +
377 '<p><strong>Completed:</strong> ' + status.completion_summary.completion_time + '</p>' +
378 '</div>';
379 $pdfCard.find('.mxchat-status-details').append(summaryHtml);
380 }
381 }
382
383 // Update failed pages list if exists
384 if (status.failed_pages_list && status.failed_pages_list.length > 0) {
385 updateFailedPagesList($pdfCard, status.failed_pages_list);
386 }
387
388 // If we have an error, show it
389 if (status.status === 'error' && status.error) {
390 let $errorNotice = $pdfCard.find('.mxchat-error-notice');
391
392 if ($errorNotice.length === 0) {
393 $errorNotice = $('<div class="mxchat-error-notice"><p class="error"></p></div>');
394 $pdfCard.find('.mxchat-status-details').append($errorNotice);
395 }
396
397 $errorNotice.find('p.error').text(status.error);
398 }
399 }
400 }
401
402 // Create a new PDF status card
403 function createPdfStatusCard(status) {
404 let html = '<div class="mxchat-status-card">';
405 html += '<div class="mxchat-status-header">';
406 html += '<h4>PDF Processing Status</h4>';
407
408 // Add stop processing form if processing
409 if (status.status === 'processing') {
410 html += '<form method="post" class="mxchat-stop-form" action="' +
411 mxchatAdmin.admin_url + 'admin-post.php?action=mxchat_stop_processing">';
412 html += '<input type="hidden" name="mxchat_stop_processing_nonce" value="' +
413 mxchatAdmin.stop_nonce + '">';
414 html += '<button type="submit" name="stop_processing" class="mxchat-button-secondary">';
415 html += 'Stop Processing</button></form>';
416 }
417
418 // Add status badges
419 if (status.status === 'error') {
420 html += '<span class="mxchat-status-badge mxchat-status-failed">Error</span>';
421 } else if (status.status === 'complete') {
422 if (status.failed_pages && status.failed_pages > 0) {
423 html += '<span class="mxchat-status-badge mxchat-status-warning">Completed with ' + status.failed_pages + ' failures</span>';
424 } else {
425 html += '<span class="mxchat-status-badge mxchat-status-success">Complete</span>';
426 }
427 }
428
429 html += '</div>'; // End header
430
431 // Progress bar
432 html += '<div class="mxchat-progress-bar">';
433 html += '<div class="mxchat-progress-fill" style="width: ' + status.percentage + '%"></div>';
434 html += '</div>';
435
436 // Status details
437 html += '<div class="mxchat-status-details">';
438 html += '<p>Progress: ' + status.processed_pages + ' of ' +
439 status.total_pages + ' pages (' + status.percentage + '%)</p>';
440
441 // Show failed pages count if any
442 if (status.failed_pages && status.failed_pages > 0) {
443 html += '<p><strong>Failed pages:</strong> ' + status.failed_pages + '</p>';
444 }
445
446 html += '<p>Status: ' + status.status.charAt(0).toUpperCase() + status.status.slice(1) + '</p>';
447 html += '<p>Last update: ' + status.last_update + '</p>';
448
449 // Add completion summary if available
450 if (status.completion_summary) {
451 html += '<div class="mxchat-completion-summary">';
452 html += '<h5>Processing Summary</h5>';
453 html += '<p><strong>Total Pages:</strong> ' + status.completion_summary.total_pages + '</p>';
454 html += '<p><strong>Successful:</strong> ' + status.completion_summary.successful_pages + '</p>';
455 html += '<p><strong>Failed:</strong> ' + status.completion_summary.failed_pages + '</p>';
456 html += '<p><strong>Completed:</strong> ' + status.completion_summary.completion_time + '</p>';
457 html += '</div>';
458 }
459
460 // Add failed pages list if any
461 if (status.failed_pages_list && status.failed_pages_list.length > 0) {
462 html += createFailedPagesHtml(status.failed_pages_list);
463 }
464
465 // Add error message if any
466 if (status.status === 'error' && status.error) {
467 html += '<div class="mxchat-error-notice">';
468 html += '<p class="error">' + status.error + '</p>';
469 html += '</div>';
470 }
471
472 html += '</div>'; // End details
473 html += '</div>'; // End card
474
475 // Insert the card into the page
476 let $importTabContent = $('#mxchat-kb-tab-import');
477 if ($importTabContent.length > 0) {
478 let $sitemapCard = $importTabContent.find('.mxchat-status-card:contains("Sitemap Processing")');
479 if ($sitemapCard.length > 0) {
480 $sitemapCard.before($(html));
481 } else {
482 $importTabContent.find('.mxchat-import-section').after($(html));
483 }
484 } else {
485 let $sitemapCard = $('.mxchat-status-card:contains("Sitemap Processing")');
486 if ($sitemapCard.length > 0) {
487 $sitemapCard.before($(html));
488 } else {
489 $('.mxchat-import-section').after($(html));
490 }
491 }
492 }
493
494 // Update sitemap status card
495 function updateSitemapStatus(status) {
496 // Check if sitemap card exists
497 let $sitemapCard = $('.mxchat-status-card:contains("Sitemap Processing")');
498
499 // If no card exists but we have status, create it
500 if ($sitemapCard.length === 0 && status) {
501 //console.log('MxChat: Creating new sitemap status card');
502 createSitemapStatusCard(status);
503 $sitemapCard = $('.mxchat-status-card:contains("Sitemap Processing")');
504 }
505
506 // If card exists, update it
507 if ($sitemapCard.length > 0) {
508 // Update progress bar
509 $sitemapCard.find('.mxchat-progress-fill').css('width', status.percentage + '%');
510
511 // Update progress text
512 let progressText = 'Progress: ' + status.processed_urls + ' of ' +
513 status.total_urls + ' URLs (' + status.percentage + '%)';
514
515 $sitemapCard.find('.mxchat-status-details p:first').text(progressText);
516
517 // Update failed URLs count if exists
518 const $failedText = $sitemapCard.find('.mxchat-status-details p:contains("Failed URLs")');
519 if (status.failed_urls && status.failed_urls > 0) {
520 if ($failedText.length === 0) {
521 // Add failed URLs text after progress
522 $sitemapCard.find('.mxchat-status-details p:first').after(
523 '<p><strong>Failed URLs:</strong> ' + status.failed_urls + '</p>'
524 );
525 } else {
526 $failedText.html('<strong>Failed URLs:</strong> ' + status.failed_urls);
527 }
528 } else if ($failedText.length > 0) {
529 $failedText.remove();
530 }
531
532 // Update status badges
533 $sitemapCard.find('.mxchat-status-badge').remove();
534 if (status.status === 'error') {
535 $sitemapCard.find('.mxchat-status-header').append('<span class="mxchat-status-badge mxchat-status-failed">Error</span>');
536 } else if (status.status === 'complete') {
537 if (status.failed_urls && status.failed_urls > 0) {
538 $sitemapCard.find('.mxchat-status-header').append('<span class="mxchat-status-badge mxchat-status-warning">Completed with ' + status.failed_urls + ' failures</span>');
539 } else {
540 $sitemapCard.find('.mxchat-status-header').append('<span class="mxchat-status-badge mxchat-status-success">Complete</span>');
541 }
542 }
543
544 // Update or add completion summary
545 if (status.completion_summary) {
546 let $summaryContainer = $sitemapCard.find('.mxchat-completion-summary');
547 if ($summaryContainer.length === 0) {
548 const summaryHtml = '<div class="mxchat-completion-summary">' +
549 '<h5>Processing Summary</h5>' +
550 '<p><strong>Total URLs:</strong> ' + status.completion_summary.total_urls + '</p>' +
551 '<p><strong>Successful:</strong> ' + status.completion_summary.successful_urls + '</p>' +
552 '<p><strong>Failed:</strong> ' + status.completion_summary.failed_urls + '</p>' +
553 '<p><strong>Completed:</strong> ' + status.completion_summary.completion_time + '</p>' +
554 '</div>';
555 $sitemapCard.find('.mxchat-status-details').append(summaryHtml);
556 }
557 }
558
559 // Check if details is already open before updating
560 const isDetailsOpen = $sitemapCard.find('.mxchat-failed-urls-container details').prop('open');
561
562 // Update errors display
563 let $errorContainer = $sitemapCard.find('.mxchat-error-notice');
564
565 if ($errorContainer.length === 0 &&
566 (status.error || status.last_error || (status.failed_urls_list && status.failed_urls_list.length > 0))) {
567 // Create error container if it doesn't exist
568 $errorContainer = $('<div class="mxchat-error-notice"></div>');
569 $sitemapCard.find('.mxchat-status-details').append($errorContainer);
570 }
571
572 // Update or create error notices
573 if ($errorContainer.length > 0) {
574 let errorHTML = '';
575
576 if (status.error) {
577 errorHTML += '<p class="error">' + status.error + '</p>';
578 }
579
580 if (status.last_error) {
581 errorHTML += '<p class="last-error">Last error: ' + status.last_error + '</p>';
582 }
583
584 // Add failed URLs list
585 if (status.failed_urls_list && status.failed_urls_list.length > 0) {
586 errorHTML += '<div class="mxchat-failed-urls-container">';
587 errorHTML += '<h5>Failed URLs (' + status.failed_urls_list.length + ')</h5>';
588
589 // Set the 'open' attribute based on previous state
590 errorHTML += '<details' + (isDetailsOpen ? ' open' : '') + '>';
591 errorHTML += '<summary>Show Failed URLs</summary>';
592 errorHTML += '<div class="mxchat-failed-urls-list">';
593
594 // Create table for failed URLs
595 errorHTML += '<table class="widefat striped">';
596 errorHTML += '<thead><tr><th>URL</th><th>Error</th><th>Retries</th><th>Time</th></tr></thead>';
597 errorHTML += '<tbody>';
598
599 // Sort failed URLs by most recent
600 const sortedFailedUrls = [...status.failed_urls_list].sort((a, b) => b.time - a.time);
601
602 // Show up to 50 failed URLs
603 const displayUrls = sortedFailedUrls.slice(0, 50);
604
605 displayUrls.forEach(item => {
606 const timeAgo = formatTimeAgo(item.time);
607 const retries = item.retries || 'N/A';
608 errorHTML += '<tr>';
609 errorHTML += '<td style="word-break: break-all;">';
610 errorHTML += '<a href="' + item.url + '" target="_blank" rel="noopener noreferrer">';
611 errorHTML += truncateUrl(item.url) + '</a></td>';
612 errorHTML += '<td style="word-break: break-word;">' + item.error + '</td>';
613 errorHTML += '<td>' + retries + '</td>';
614 errorHTML += '<td>' + timeAgo + '</td>';
615 errorHTML += '</tr>';
616 });
617
618 errorHTML += '</tbody></table>';
619
620 if (status.failed_urls_list.length > 50) {
621 errorHTML += '<div class="mxchat-failed-urls-more">+ ' +
622 (status.failed_urls_list.length - 50) +
623 ' more failed URLs not shown</div>';
624 }
625
626 errorHTML += '</div>'; // End of failed-urls-list
627 errorHTML += '</details>';
628 errorHTML += '</div>'; // End of failed-urls-container
629 }
630
631 $errorContainer.html(errorHTML);
632
633 // Additionally, add a click handler to pause refreshes when viewing details
634 $sitemapCard.find('.mxchat-failed-urls-container details').on('toggle', function() {
635 if (this.open) {
636 // User opened the details - set a flag
637 $(this).data('user-opened', true);
638 } else {
639 // User closed the details - remove the flag
640 $(this).data('user-opened', false);
641 }
642 });
643 }
644 }
645 }
646
647 function createFailedPagesHtml(failedPagesList) {
648 let html = '<div class="mxchat-error-notice">';
649 html += '<div class="mxchat-failed-pages-container">';
650 html += '<h5>Failed Pages (' + failedPagesList.length + ')</h5>';
651 html += '<details>';
652 html += '<summary>Show Failed Pages</summary>';
653 html += '<div class="mxchat-failed-pages-list">';
654
655 // Create table for failed pages
656 html += '<table class="widefat striped">';
657 html += '<thead><tr><th>Page</th><th>Error</th><th>Retries</th><th>Time</th></tr></thead>';
658 html += '<tbody>';
659
660 // Sort failed pages by most recent
661 const sortedFailedPages = [...failedPagesList].sort((a, b) => b.time - a.time);
662
663 sortedFailedPages.forEach(item => {
664 const timeAgo = formatTimeAgo(item.time);
665 html += '<tr>';
666 html += '<td>Page ' + item.page + '</td>';
667 html += '<td style="word-break: break-word;">' + item.error + '</td>';
668 html += '<td>' + item.retries + '</td>';
669 html += '<td>' + timeAgo + '</td>';
670 html += '</tr>';
671 });
672
673 html += '</tbody></table>';
674 html += '</div>'; // End of failed-pages-list
675 html += '</details>';
676 html += '</div>'; // End of failed-pages-container
677 html += '</div>'; // End of error-notice
678
679 return html;
680 }
681
682 // NEW: Update failed pages list in existing card
683 function updateFailedPagesList($pdfCard, failedPagesList) {
684 // Check if details is already open before updating
685 const isDetailsOpen = $pdfCard.find('.mxchat-failed-pages-container details').prop('open');
686
687 let $errorContainer = $pdfCard.find('.mxchat-failed-pages-container').parent();
688
689 if ($errorContainer.length === 0) {
690 // Create new failed pages container
691 $pdfCard.find('.mxchat-status-details').append(createFailedPagesHtml(failedPagesList));
692 } else {
693 // Update existing container
694 $errorContainer.html(createFailedPagesHtml(failedPagesList));
695
696 // Restore open state if it was open before
697 if (isDetailsOpen) {
698 $pdfCard.find('.mxchat-failed-pages-container details').prop('open', true);
699 }
700 }
701 }
702
703
704 // Create a new sitemap status card
705 function createSitemapStatusCard(status) {
706 let html = '<div class="mxchat-status-card">';
707 html += '<div class="mxchat-status-header">';
708 html += '<h4>Sitemap Processing Status</h4>';
709
710 // Add stop processing form if processing
711 if (status.status === 'processing') {
712 html += '<form method="post" class="mxchat-stop-form" action="' +
713 mxchatAdmin.admin_url + 'admin-post.php?action=mxchat_stop_processing">';
714 html += '<input type="hidden" name="mxchat_stop_processing_nonce" value="' +
715 mxchatAdmin.stop_nonce + '">';
716 html += '<button type="submit" name="stop_processing" class="mxchat-button-secondary">';
717 html += 'Stop Processing</button></form>';
718 }
719
720 // Add error badge if error
721 if (status.status === 'error') {
722 html += '<span class="mxchat-status-badge mxchat-status-failed">Error</span>';
723 }
724
725 html += '</div>'; // End header
726
727 // Progress bar
728 html += '<div class="mxchat-progress-bar">';
729 html += '<div class="mxchat-progress-fill" style="width: ' + status.percentage + '%"></div>';
730 html += '</div>';
731
732 // Status details
733 html += '<div class="mxchat-status-details">';
734 html += '<p>Progress: ' + status.processed_urls + ' of ' +
735 status.total_urls + ' URLs (' + status.percentage + '%)</p>';
736
737 // Add error message if any
738 if ((status.error || status.last_error) && status.status === 'error') {
739 html += '<div class="mxchat-error-notice">';
740
741 if (status.error) {
742 html += '<p class="error">' + status.error + '</p>';
743 }
744
745 if (status.last_error) {
746 html += '<p class="last-error">Last error: ' + status.last_error + '</p>';
747 }
748
749 html += '</div>';
750 }
751
752 html += '</div>'; // End details
753 html += '</div>'; // End card
754
755 // Try to find the import tab content to insert the status card into
756 let $importTabContent = $('#mxchat-kb-tab-import');
757 if ($importTabContent.length > 0) {
758 // For the tabbed interface, add to the import tab
759 let $pdfCard = $importTabContent.find('.mxchat-status-card:contains("PDF Processing")');
760 if ($pdfCard.length > 0) {
761 $pdfCard.after($(html));
762 } else {
763 $importTabContent.find('.mxchat-import-section').after($(html));
764 }
765 } else {
766 // Fallback to the old method
767 let $pdfCard = $('.mxchat-status-card:contains("PDF Processing")');
768 if ($pdfCard.length > 0) {
769 $pdfCard.after($(html));
770 } else {
771 $('.mxchat-import-section').after($(html));
772 }
773 }
774 }
775
776 // Update single URL status
777 function updateSingleUrlStatus(status) {
778 // Check if container exists
779 let $container = $('#mxchat-single-url-status-container');
780
781 if ($container.length === 0) {
782 // Create container
783 $container = $('<div id="mxchat-single-url-status-container"></div>');
784
785 // Try to find the import tab content to insert the status card into
786 let $importTabContent = $('#mxchat-kb-tab-import');
787 if ($importTabContent.length > 0) {
788 // For the tabbed interface, add to the import tab
789 let $lastStatusCard = $importTabContent.find('.mxchat-status-card').last();
790 if ($lastStatusCard.length > 0) {
791 $lastStatusCard.after($container);
792 } else {
793 $importTabContent.find('.mxchat-import-section').after($container);
794 }
795 } else {
796 // Fallback to the old method
797 let $lastStatusCard = $('.mxchat-status-card').last();
798 if ($lastStatusCard.length > 0) {
799 $lastStatusCard.after($container);
800 } else {
801 $('.mxchat-import-section').after($container);
802 }
803 }
804 }
805
806 // Update container content
807 let html = '<div class="mxchat-status-card">';
808 html += '<div class="mxchat-status-header">';
809 html += '<h4>Last URL Submission</h4>';
810
811 if (status.status === 'failed') {
812 html += '<span class="mxchat-status-badge mxchat-status-failed">Failed</span>';
813 } else {
814 html += '<span class="mxchat-status-badge mxchat-status-success">Success</span>';
815 }
816
817 html += '</div>'; // End header
818
819 html += '<div class="mxchat-status-details">';
820 html += '<p><strong>URL:</strong> ';
821 html += '<a href="' + status.url + '" target="_blank">';
822
823 // Truncate URL if needed
824 const displayUrl = status.url.length > 60 ? status.url.substring(0, 57) + '...' : status.url;
825 html += displayUrl;
826
827 html += '</a></p>';
828 html += '<p><strong>Submitted:</strong> ' + status.human_time + '</p>';
829
830 if (status.status === 'failed' && status.error) {
831 html += '<div class="mxchat-error-notice">';
832 html += '<p class="error">' + status.error + '</p>';
833 html += '</div>';
834 }
835
836 if (status.status === 'complete') {
837 html += '<p><strong>Content Length:</strong> ' + status.content_length + ' characters</p>';
838 html += '<p><strong>Embedding Dimensions:</strong> ' + status.embedding_dimensions + '</p>';
839 }
840
841 html += '</div>'; // End details
842 html += '</div>'; // End card
843
844 $container.html(html).show();
845 }
846
847 // Helper function to format time ago
848 function formatTimeAgo(timestamp) {
849 const now = Math.floor(Date.now() / 1000);
850 const seconds = now - timestamp;
851
852 if (seconds < 60) {
853 return seconds + ' seconds ago';
854 } else if (seconds < 3600) {
855 return Math.floor(seconds / 60) + ' minutes ago';
856 } else if (seconds < 86400) {
857 return Math.floor(seconds / 3600) + ' hours ago';
858 } else {
859 return Math.floor(seconds / 86400) + ' days ago';
860 }
861 }
862
863 // Helper function to truncate long URLs
864 function truncateUrl(url) {
865 const maxLength = 50;
866 if (url.length <= maxLength) return url;
867
868 // Remove protocol
869 let displayUrl = url.replace(/^https?:\/\//, '');
870
871 if (displayUrl.length <= maxLength) return displayUrl;
872
873 // Keep the domain and truncate the path
874 const domainMatch = displayUrl.match(/^([^\/]+)\//);
875 if (domainMatch) {
876 const domain = domainMatch[1];
877 const path = displayUrl.substring(domain.length);
878
879 if (path.length > 10) {
880 return domain + path.substring(0, maxLength - domain.length - 3) + '...';
881 }
882 }
883
884 // Final fallback for very long strings
885 return displayUrl.substring(0, maxLength - 3) + '...';
886 }
887
888 // Manual Batch Processing Button Handler
889 $(document).on('click', '.mxchat-manual-batch-btn', function() {
890 const $btn = $(this);
891 const processType = $btn.data('process-type');
892 const url = $btn.data('url');
893
894 //console.log('Manual batch processing requested:', processType, url);
895
896 // Disable button and show loading
897 $btn.prop('disabled', true).text('Processing...');
898
899 $.ajax({
900 url: ajaxurl,
901 type: 'POST',
902 data: {
903 action: 'mxchat_manual_batch_process',
904 nonce: mxchatAdmin.status_nonce,
905 process_type: processType,
906 url: url
907 },
908 success: function(response) {
909 //console.log('Manual batch response:', response);
910
911 if (response.success) {
912 // Show success message briefly
913 $btn.text('✓ Processed ' + response.data.processed);
914
915 // Re-enable button after 2 seconds
916 setTimeout(function() {
917 $btn.prop('disabled', false).text('Process Batch');
918 }, 2000);
919
920 // Trigger status update to show progress
921 setTimeout(function() {
922 fetchStatusUpdates();
923 }, 1000); // Wait 1 second then refresh status
924
925 } else {
926 console.error('Manual batch failed:', response.data);
927 alert('Error: ' + response.data);
928 $btn.prop('disabled', false).text('Process Batch');
929 }
930 },
931 error: function(xhr, status, error) {
932 console.error('Manual batch AJAX error:', error);
933 alert('Processing failed. Please try again.');
934 $btn.prop('disabled', false).text('Process Batch');
935 }
936 });
937 });
938 });
939
940
941 // Handle AJAX delete for Pinecone
942 jQuery(document).on('click', '.delete-button-ajax', function(e) {
943 e.preventDefault();
944
945 if (!confirm('Are you sure you want to delete this entry?')) {
946 return;
947 }
948
949 var $button = jQuery(this);
950 var $row = $button.closest('tr');
951 var vectorId = $button.data('vector-id');
952 var nonce = $button.data('nonce');
953
954 // Disable button and show loading
955 $button.prop('disabled', true);
956 $button.find('.dashicons').removeClass('dashicons-trash').addClass('dashicons-update-alt');
957 $row.addClass('mxchat-row-deleting');
958
959 jQuery.ajax({
960 url: ajaxurl,
961 type: 'POST',
962 data: {
963 action: 'mxchat_delete_pinecone_prompt',
964 nonce: nonce,
965 vector_id: vectorId
966 },
967 success: function(response) {
968 if (response.success) {
969 // Immediately remove the row with animation
970 $row.fadeOut(500, function() {
971 jQuery(this).remove();
972
973 // Update record count
974 var $countSpan = jQuery('.mxchat-record-count');
975 if ($countSpan.length) {
976 var currentText = $countSpan.text();
977 var matches = currentText.match(/\((\d+)/);
978 if (matches) {
979 var currentCount = parseInt(matches[1]);
980 var newCount = Math.max(0, currentCount - 1);
981 $countSpan.text($countSpan.text().replace(/\(\d+/, '(' + newCount));
982 }
983 }
984 });
985
986 // Show success message
987 jQuery('<div class="notice notice-success is-dismissible"><p>Entry deleted successfully from Pinecone.</p></div>')
988 .insertAfter('.mxchat-hero')
989 .delay(3000)
990 .fadeOut();
991
992 } else {
993 // Re-enable button and show error
994 $button.prop('disabled', false);
995 $button.find('.dashicons').removeClass('dashicons-update-alt').addClass('dashicons-trash');
996 $row.removeClass('mxchat-row-deleting');
997
998 alert('Error: ' + response.data);
999 }
1000 },
1001 error: function() {
1002 // Re-enable button
1003 $button.prop('disabled', false);
1004 $button.find('.dashicons').removeClass('dashicons-update-alt').addClass('dashicons-trash');
1005 $row.removeClass('mxchat-row-deleting');
1006
1007 alert('Network error occurred');
1008 }
1009 });
1010 });