PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.6.0
MxChat – AI Chatbot & Content Generation for WordPress v2.6.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 / mxchat_transcripts.js

mxchat_transcripts.js in MxChat – AI Chatbot & Content Generation for WordPress 2.6.0, at js/mxchat_transcripts.js

439 lines 17.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 jQuery(document).ready(function($) {
2 // Current page state
3 let currentPage = 1;
4 const perPage = 50; // Display 50 sessions per page
5 let totalPages = 1;
6
7 // Track selected sessions for bulk delete
8 let selectedSessions = new Set();
9
10 // Select/Deselect All functionality
11 const selectButton = $('#mxchat-select-all-transcripts');
12 let isSelected = false;
13
14 selectButton.click(function() {
15 isSelected = !isSelected;
16 $(this).toggleClass('selected');
17
18 // Update button text
19 const buttonText = $(this).find('.button-text');
20 buttonText.text(isSelected ? 'Deselect All' : 'Select All');
21
22 // Update session selection (for current page)
23 $('.mxchat-session-header').each(function() {
24 const sessionId = $(this).data('session-id');
25 const sessionContainer = $(this).closest('.mxchat-session');
26 if (isSelected) {
27 selectedSessions.add(sessionId);
28 sessionContainer.addClass('selected');
29 } else {
30 selectedSessions.delete(sessionId);
31 sessionContainer.removeClass('selected');
32 }
33 });
34
35 updateDeleteButtonState();
36 });
37
38 // Search functionality
39 $('#mxchat-search-transcripts').on('input', function() {
40 var searchTerm = $(this).val().toLowerCase();
41
42 if (searchTerm.length > 0) {
43 // Reset to first page when searching
44 currentPage = 1;
45
46 // Load with search filter
47 loadTranscripts(currentPage, searchTerm);
48 } else {
49 // Reset to first page with no search term
50 currentPage = 1;
51 loadTranscripts(currentPage, '');
52 }
53 });
54
55 // Initial load of transcripts
56 loadTranscripts(currentPage, '');
57
58 // Function to load transcripts with pagination
59 function loadTranscripts(page, searchTerm = '') {
60 $('#mxchat-transcripts').html('<div class="mxchat-loading">Loading transcripts...</div>');
61
62 $.ajax({
63 url: ajaxurl,
64 type: 'POST',
65 data: {
66 action: 'mxchat_fetch_chat_history',
67 page: page,
68 per_page: perPage,
69 search: searchTerm
70 },
71 success: function(response) {
72 $('#mxchat-transcripts').html(response.html);
73 currentPage = response.page;
74 totalPages = response.total_pages;
75
76 // Reset selection state when page changes (but keep selectedSessions for bulk operations)
77 isSelected = false;
78 selectButton.removeClass('selected');
79 selectButton.find('.button-text').text('Select All');
80
81 // Restore selection state for sessions on this page
82 $('.mxchat-session-header').each(function() {
83 const sessionId = $(this).data('session-id');
84 const sessionContainer = $(this).closest('.mxchat-session');
85 if (selectedSessions.has(sessionId)) {
86 sessionContainer.addClass('selected');
87 }
88 });
89
90 updateDeleteButtonState();
91
92 // Add click handlers to pagination buttons
93 $('.mxchat-pagination-button').on('click', function() {
94 var pageNum = $(this).data('page');
95 loadTranscripts(pageNum, searchTerm);
96
97 // Scroll to top of transcripts
98 $('html, body').animate({
99 scrollTop: $('#mxchat-transcripts').offset().top - 50
100 }, 300);
101 });
102
103 // Re-attach event handlers for newly loaded content
104 attachDynamicEventHandlers();
105 },
106 error: function(xhr, status, error) {
107 $('#mxchat-transcripts').html('<div class="mxchat-error">Error loading chat transcripts. Please try again.</div>');
108 console.error("AJAX Error: " + status + " - " + error);
109 }
110 });
111 }
112
113 // Attach event handlers to dynamically loaded content
114 function attachDynamicEventHandlers() {
115 // Handle individual delete button clicks
116 $('.mxchat-delete-btn').off('click').on('click', function(e) {
117 e.preventDefault();
118 e.stopPropagation();
119
120 const sessionId = $(this).data('session-id');
121
122 if (!confirm("Are you sure you want to delete this chat session? This action cannot be undone.")) {
123 return;
124 }
125
126 // Delete single session
127 deleteSessions([sessionId]);
128 });
129
130 // Handle session header clicks for selection (bulk delete)
131 $('.mxchat-session-header').off('click').on('click', function(e) {
132 // Don't trigger if clicking the delete button
133 if ($(e.target).hasClass('mxchat-delete-btn') || $(e.target).closest('.mxchat-delete-btn').length) {
134 return;
135 }
136
137 const sessionId = $(this).data('session-id');
138 const sessionContainer = $(this).closest('.mxchat-session');
139
140 if (selectedSessions.has(sessionId)) {
141 selectedSessions.delete(sessionId);
142 sessionContainer.removeClass('selected');
143 } else {
144 selectedSessions.add(sessionId);
145 sessionContainer.addClass('selected');
146 }
147
148 updateDeleteButtonState();
149 });
150
151 // Handle clicks on Sources link for RAG context
152 $('.mxchat-rag-link').off('click').on('click', function(e) {
153 e.preventDefault();
154 e.stopPropagation();
155
156 const messageId = $(this).closest('.mxchat-message').data('message-id');
157 if (messageId) {
158 openRagContextModal(messageId);
159 }
160 });
161 }
162
163 // Update delete button state based on selections
164 function updateDeleteButtonState() {
165 const deleteButton = $('.delete-chats-button');
166 if (selectedSessions.size > 0) {
167 deleteButton.prop('disabled', false);
168 } else {
169 deleteButton.prop('disabled', true);
170 }
171 }
172
173 // Function to delete sessions
174 function deleteSessions(sessionIds) {
175 $.ajax({
176 url: ajaxurl,
177 type: 'POST',
178 data: {
179 action: 'mxchat_delete_chat_history',
180 delete_session_ids: sessionIds,
181 security: $('#mxchat_delete_chat_nonce').val()
182 },
183 success: function(response) {
184 var jsonResponse = JSON.parse(response);
185 if (jsonResponse.success) {
186 alert("Success: " + jsonResponse.success);
187
188 // Remove deleted sessions from selectedSessions
189 sessionIds.forEach(id => selectedSessions.delete(id));
190
191 } else if (jsonResponse.error) {
192 alert("Error: " + jsonResponse.error);
193 } else {
194 //console.log("Unexpected response format.");
195 }
196
197 // Reload the current page of transcripts
198 loadTranscripts(currentPage);
199 },
200 error: function(xhr, status, error) {
201 //console.error("AJAX Error: " + status + " - " + error);
202 //console.log(xhr.responseText);
203 alert("An error occurred while deleting chat sessions. Please try again.");
204 }
205 });
206 }
207
208 // Delete form submission (bulk delete)
209 $('#mxchat-delete-form').submit(function(e) {
210 e.preventDefault();
211
212 if (selectedSessions.size === 0) {
213 alert("Please select at least one chat session to delete.");
214 return;
215 }
216
217 // Confirm deletion
218 if (!confirm(`Are you sure you want to delete the selected ${selectedSessions.size} chat session(s)? This action cannot be undone.`)) {
219 return;
220 }
221
222 // Convert Set to Array and delete
223 deleteSessions(Array.from(selectedSessions));
224 });
225
226 // Export functionality - this remains unchanged as it should export all transcripts
227 $('#mxchat-export-transcripts').on('click', function() {
228 var $button = $(this);
229 $button.prop('disabled', true).addClass('loading');
230
231 // Create a form and submit it
232 var $form = $('<form>', {
233 'method': 'post',
234 'action': ajaxurl
235 });
236
237 $form.append($('<input>', {
238 'type': 'hidden',
239 'name': 'action',
240 'value': 'mxchat_export_transcripts'
241 }));
242
243 $form.append($('<input>', {
244 'type': 'hidden',
245 'name': 'security',
246 'value': mxchatAdmin.export_nonce
247 }));
248
249 $form.appendTo('body').submit();
250
251 // Re-enable the button after a short delay
252 setTimeout(function() {
253 $button.prop('disabled', false).removeClass('loading');
254 }, 2000);
255 });
256
257 // Chat Email Notification Modal functionality
258
259 // Open modal
260 $('#mxchat-chat-email-notification-btn').on('click', function(e) {
261 e.preventDefault();
262 $('#mxchat-chat-email-notification-modal').fadeIn(300);
263 });
264
265 // Close modal
266 $('.mxchat-chat-notification-modal-close, .mxchat-chat-notification-modal-cancel').on('click', function() {
267 $('#mxchat-chat-email-notification-modal').fadeOut(300);
268 });
269
270 // Close modal on outside click
271 $('#mxchat-chat-email-notification-modal').on('click', function(e) {
272 if ($(e.target).is('#mxchat-chat-email-notification-modal')) {
273 $(this).fadeOut(300);
274 }
275 });
276
277 // Handle form submission - Let WordPress handle it normally for settings
278 $('#mxchat-chat-email-notification-form').on('submit', function(e) {
279 // Don't prevent default - let the form submit normally to WordPress options.php
280 var $submitButton = $(this).find('button[type="submit"]');
281 var originalText = $submitButton.text();
282
283 // Just show a loading state
284 $submitButton.text('Saving...').prop('disabled', true);
285
286 // The form will submit normally and reload the page
287 });
288
289 // ========== RAG Context Modal Functions ==========
290
291 // Open RAG context modal and fetch data
292 function openRagContextModal(messageId) {
293 const $modal = $('#mxchat-rag-context-modal');
294 const $loading = $modal.find('.mxchat-rag-loading');
295 const $content = $modal.find('.mxchat-rag-content');
296
297 // Show modal with loading state
298 $modal.fadeIn(300);
299 $loading.show();
300 $content.html('');
301
302 // Fetch RAG context via AJAX
303 $.ajax({
304 url: ajaxurl,
305 type: 'POST',
306 data: {
307 action: 'mxchat_get_rag_context',
308 message_id: messageId
309 },
310 success: function(response) {
311 $loading.hide();
312 if (response.success && response.data) {
313 renderRagContext(response.data, $content);
314 } else {
315 $content.html('<div class="mxchat-rag-error">Unable to load document context.</div>');
316 }
317 },
318 error: function() {
319 $loading.hide();
320 $content.html('<div class="mxchat-rag-error">Error loading document context. Please try again.</div>');
321 }
322 });
323 }
324
325 // Render RAG context data in the modal
326 function renderRagContext(data, $container) {
327 let html = '';
328
329 // Summary section
330 html += '<div class="mxchat-rag-summary">';
331 html += '<div class="mxchat-rag-summary-item">';
332 html += '<span class="mxchat-rag-label">Knowledge Base:</span> ';
333 html += '<span class="mxchat-rag-value">' + escapeHtml(data.knowledge_base_type || 'WordPress Database') + '</span>';
334 html += '</div>';
335 html += '<div class="mxchat-rag-summary-item">';
336 html += '<span class="mxchat-rag-label">Similarity Threshold:</span> ';
337 html += '<span class="mxchat-rag-value">' + Math.round((data.similarity_threshold || 0.35) * 100) + '%</span>';
338 html += '</div>';
339 html += '<div class="mxchat-rag-summary-item">';
340 html += '<span class="mxchat-rag-label">Documents Checked:</span> ';
341 html += '<span class="mxchat-rag-value">' + (data.total_documents_checked || 0) + '</span>';
342 html += '</div>';
343 html += '</div>';
344
345 // Top matches section
346 if (data.top_matches && data.top_matches.length > 0) {
347 html += '<div class="mxchat-rag-matches">';
348 html += '<h3>Retrieved Documents</h3>';
349
350 data.top_matches.forEach(function(match, index) {
351 const isUsed = match.used_for_context;
352 const isAboveThreshold = match.above_threshold;
353 const cardClass = isUsed ? 'mxchat-rag-match-used' : (isAboveThreshold ? 'mxchat-rag-match-above' : 'mxchat-rag-match-below');
354 const statusIcon = isUsed ? '�
355 ' : (isAboveThreshold ? '⚠️' : '');
356 const statusLabel = isUsed ? 'Used for Response' : (isAboveThreshold ? 'Above Threshold' : 'Below Threshold');
357
358 html += '<div class="mxchat-rag-match-card ' + cardClass + '">';
359 html += '<div class="mxchat-rag-match-header">';
360 html += '<span class="mxchat-rag-match-rank">#' + (index + 1) + '</span>';
361 html += '<span class="mxchat-rag-match-score">' + match.similarity_percentage + '%</span>';
362 html += '<span class="mxchat-rag-match-status ' + (isUsed ? 'status-used' : (isAboveThreshold ? 'status-above' : 'status-below')) + '">';
363 html += statusIcon + ' ' + statusLabel;
364 html += '</span>';
365 html += '</div>';
366
367 html += '<div class="mxchat-rag-match-source">';
368 if (match.source_display && match.source_display.startsWith('http')) {
369 html += '<a href="' + escapeHtml(match.source_display) + '" target="_blank" rel="noopener noreferrer">';
370 html += '🔗 ' + escapeHtml(match.source_display);
371 html += '</a>';
372 } else {
373 html += '📄 ' + escapeHtml(match.source_display || 'Content snippet');
374 }
375 html += '</div>';
376
377 if (match.content_preview) {
378 html += '<div class="mxchat-rag-match-preview">';
379 html += escapeHtml(match.content_preview);
380 html += '</div>';
381 }
382
383 // Show role restriction if not public
384 if (match.role_restriction && match.role_restriction !== 'public') {
385 html += '<div class="mxchat-rag-match-meta">';
386 html += '<span class="mxchat-rag-role-badge">🔒 ' + escapeHtml(match.role_restriction) + '</span>';
387 html += '</div>';
388 }
389
390 html += '</div>';
391 });
392
393 html += '</div>';
394 } else {
395 html += '<div class="mxchat-rag-no-matches">No document matches found for this response.</div>';
396 }
397
398 // Approved URLs section
399 if (data.approved_urls && data.approved_urls.length > 0) {
400 html += '<div class="mxchat-rag-urls">';
401 html += '<h3>Approved URLs for Citations (' + data.approved_urls.length + ')</h3>';
402 html += '<ul class="mxchat-rag-url-list">';
403 data.approved_urls.forEach(function(url) {
404 html += '<li><a href="' + escapeHtml(url) + '" target="_blank" rel="noopener noreferrer">' + escapeHtml(url) + '</a></li>';
405 });
406 html += '</ul>';
407 html += '</div>';
408 }
409
410 $container.html(html);
411 }
412
413 // Helper function to escape HTML
414 function escapeHtml(text) {
415 if (!text) return '';
416 const div = document.createElement('div');
417 div.textContent = text;
418 return div.innerHTML;
419 }
420
421 // Close RAG context modal
422 $('.mxchat-rag-modal-close').on('click', function() {
423 $('#mxchat-rag-context-modal').fadeOut(300);
424 });
425
426 // Close modal on outside click
427 $('#mxchat-rag-context-modal').on('click', function(e) {
428 if ($(e.target).is('#mxchat-rag-context-modal')) {
429 $(this).fadeOut(300);
430 }
431 });
432
433 // Close modal on Escape key
434 $(document).on('keydown', function(e) {
435 if (e.key === 'Escape') {
436 $('#mxchat-rag-context-modal').fadeOut(300);
437 }
438 });
439 });