jQuery(document).ready(function($) { // Current page state let currentPage = 1; const perPage = 50; // Display 50 sessions per page let totalPages = 1; // Track selected sessions for bulk delete let selectedSessions = new Set(); // Select/Deselect All functionality const selectButton = $('#mxchat-select-all-transcripts'); let isSelected = false; selectButton.click(function() { isSelected = !isSelected; $(this).toggleClass('selected'); // Update button text const buttonText = $(this).find('.button-text'); buttonText.text(isSelected ? 'Deselect All' : 'Select All'); // Update session selection (for current page) $('.mxchat-session-header').each(function() { const sessionId = $(this).data('session-id'); const sessionContainer = $(this).closest('.mxchat-session'); if (isSelected) { selectedSessions.add(sessionId); sessionContainer.addClass('selected'); } else { selectedSessions.delete(sessionId); sessionContainer.removeClass('selected'); } }); updateDeleteButtonState(); }); // Search functionality $('#mxchat-search-transcripts').on('input', function() { var searchTerm = $(this).val().toLowerCase(); if (searchTerm.length > 0) { // Reset to first page when searching currentPage = 1; // Load with search filter loadTranscripts(currentPage, searchTerm); } else { // Reset to first page with no search term currentPage = 1; loadTranscripts(currentPage, ''); } }); // Initial load of transcripts loadTranscripts(currentPage, ''); // Function to load transcripts with pagination function loadTranscripts(page, searchTerm = '') { $('#mxchat-transcripts').html('
Loading transcripts...
'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_fetch_chat_history', page: page, per_page: perPage, search: searchTerm }, success: function(response) { $('#mxchat-transcripts').html(response.html); currentPage = response.page; totalPages = response.total_pages; // Reset selection state when page changes (but keep selectedSessions for bulk operations) isSelected = false; selectButton.removeClass('selected'); selectButton.find('.button-text').text('Select All'); // Restore selection state for sessions on this page $('.mxchat-session-header').each(function() { const sessionId = $(this).data('session-id'); const sessionContainer = $(this).closest('.mxchat-session'); if (selectedSessions.has(sessionId)) { sessionContainer.addClass('selected'); } }); updateDeleteButtonState(); // Add click handlers to pagination buttons $('.mxchat-pagination-button').on('click', function() { var pageNum = $(this).data('page'); loadTranscripts(pageNum, searchTerm); // Scroll to top of transcripts $('html, body').animate({ scrollTop: $('#mxchat-transcripts').offset().top - 50 }, 300); }); // Re-attach event handlers for newly loaded content attachDynamicEventHandlers(); }, error: function(xhr, status, error) { $('#mxchat-transcripts').html('
Error loading chat transcripts. Please try again.
'); console.error("AJAX Error: " + status + " - " + error); } }); } // Attach event handlers to dynamically loaded content function attachDynamicEventHandlers() { // Handle individual delete button clicks $('.mxchat-delete-btn').off('click').on('click', function(e) { e.preventDefault(); e.stopPropagation(); const sessionId = $(this).data('session-id'); if (!confirm("Are you sure you want to delete this chat session? This action cannot be undone.")) { return; } // Delete single session deleteSessions([sessionId]); }); // Handle session header clicks for selection (bulk delete) $('.mxchat-session-header').off('click').on('click', function(e) { // Don't trigger if clicking the delete button if ($(e.target).hasClass('mxchat-delete-btn') || $(e.target).closest('.mxchat-delete-btn').length) { return; } const sessionId = $(this).data('session-id'); const sessionContainer = $(this).closest('.mxchat-session'); if (selectedSessions.has(sessionId)) { selectedSessions.delete(sessionId); sessionContainer.removeClass('selected'); } else { selectedSessions.add(sessionId); sessionContainer.addClass('selected'); } updateDeleteButtonState(); }); // Handle clicks on Sources link for RAG context $('.mxchat-rag-link').off('click').on('click', function(e) { e.preventDefault(); e.stopPropagation(); const messageId = $(this).closest('.mxchat-message').data('message-id'); if (messageId) { openRagContextModal(messageId); } }); } // Update delete button state based on selections function updateDeleteButtonState() { const deleteButton = $('.delete-chats-button'); if (selectedSessions.size > 0) { deleteButton.prop('disabled', false); } else { deleteButton.prop('disabled', true); } } // Function to delete sessions function deleteSessions(sessionIds) { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_delete_chat_history', delete_session_ids: sessionIds, security: $('#mxchat_delete_chat_nonce').val() }, success: function(response) { var jsonResponse = JSON.parse(response); if (jsonResponse.success) { alert("Success: " + jsonResponse.success); // Remove deleted sessions from selectedSessions sessionIds.forEach(id => selectedSessions.delete(id)); } else if (jsonResponse.error) { alert("Error: " + jsonResponse.error); } else { //console.log("Unexpected response format."); } // Reload the current page of transcripts loadTranscripts(currentPage); }, error: function(xhr, status, error) { //console.error("AJAX Error: " + status + " - " + error); //console.log(xhr.responseText); alert("An error occurred while deleting chat sessions. Please try again."); } }); } // Delete form submission (bulk delete) $('#mxchat-delete-form').submit(function(e) { e.preventDefault(); if (selectedSessions.size === 0) { alert("Please select at least one chat session to delete."); return; } // Confirm deletion if (!confirm(`Are you sure you want to delete the selected ${selectedSessions.size} chat session(s)? This action cannot be undone.`)) { return; } // Convert Set to Array and delete deleteSessions(Array.from(selectedSessions)); }); // Export functionality - this remains unchanged as it should export all transcripts $('#mxchat-export-transcripts').on('click', function() { var $button = $(this); $button.prop('disabled', true).addClass('loading'); // Create a form and submit it var $form = $('
', { 'method': 'post', 'action': ajaxurl }); $form.append($('', { 'type': 'hidden', 'name': 'action', 'value': 'mxchat_export_transcripts' })); $form.append($('', { 'type': 'hidden', 'name': 'security', 'value': mxchatAdmin.export_nonce })); $form.appendTo('body').submit(); // Re-enable the button after a short delay setTimeout(function() { $button.prop('disabled', false).removeClass('loading'); }, 2000); }); // Chat Email Notification Modal functionality // Open modal $('#mxchat-chat-email-notification-btn').on('click', function(e) { e.preventDefault(); $('#mxchat-chat-email-notification-modal').fadeIn(300); }); // Close modal $('.mxchat-chat-notification-modal-close, .mxchat-chat-notification-modal-cancel').on('click', function() { $('#mxchat-chat-email-notification-modal').fadeOut(300); }); // Close modal on outside click $('#mxchat-chat-email-notification-modal').on('click', function(e) { if ($(e.target).is('#mxchat-chat-email-notification-modal')) { $(this).fadeOut(300); } }); // Handle form submission - Let WordPress handle it normally for settings $('#mxchat-chat-email-notification-form').on('submit', function(e) { // Don't prevent default - let the form submit normally to WordPress options.php var $submitButton = $(this).find('button[type="submit"]'); var originalText = $submitButton.text(); // Just show a loading state $submitButton.text('Saving...').prop('disabled', true); // The form will submit normally and reload the page }); // ========== RAG Context Modal Functions ========== // Open RAG context modal and fetch data function openRagContextModal(messageId) { const $modal = $('#mxchat-rag-context-modal'); const $loading = $modal.find('.mxchat-rag-loading'); const $content = $modal.find('.mxchat-rag-content'); // Show modal with loading state $modal.fadeIn(300); $loading.show(); $content.html(''); // Fetch RAG context via AJAX $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'mxchat_get_rag_context', message_id: messageId }, success: function(response) { $loading.hide(); if (response.success && response.data) { renderRagContext(response.data, $content); } else { $content.html('
Unable to load document context.
'); } }, error: function() { $loading.hide(); $content.html('
Error loading document context. Please try again.
'); } }); } // Render RAG context data in the modal - grouped by URL function renderRagContext(data, $container) { let html = ''; // Summary section html += '
'; html += '
'; html += 'Knowledge Base: '; html += '' + escapeHtml(data.knowledge_base_type || 'WordPress Database') + ''; html += '
'; html += '
'; html += 'Similarity Threshold: '; html += '' + Math.round((data.similarity_threshold || 0.35) * 100) + '%'; html += '
'; html += '
'; html += 'Documents Checked: '; html += '' + (data.total_documents_checked || 0) + ''; html += '
'; html += '
'; // Top matches section - grouped by URL if (data.top_matches && data.top_matches.length > 0) { // Group matches by source URL const groupedByUrl = {}; data.top_matches.forEach(function(match) { const url = match.source_display || 'Unknown'; if (!groupedByUrl[url]) { groupedByUrl[url] = { url: url, isUrl: url.startsWith('http'), bestScore: 0, usedForContext: false, totalChunks: match.total_chunks || 1, matchedChunks: [], isChunked: match.is_chunk || false, roleRestriction: match.role_restriction }; } // Track best score if (match.similarity_percentage > groupedByUrl[url].bestScore) { groupedByUrl[url].bestScore = match.similarity_percentage; } // Track if any chunk was used if (match.used_for_context) { groupedByUrl[url].usedForContext = true; } // Add chunk info groupedByUrl[url].matchedChunks.push({ chunkIndex: match.chunk_index, score: match.similarity_percentage, usedForContext: match.used_for_context, aboveThreshold: match.above_threshold, contentPreview: match.content_preview }); }); // Convert to array and sort by best score const urlGroups = Object.values(groupedByUrl).sort(function(a, b) { return b.bestScore - a.bestScore; }); // Count unique URLs used const usedUrlCount = urlGroups.filter(function(g) { return g.usedForContext; }).length; html += '
'; html += '

Retrieved Documents

'; html += '

' + usedUrlCount + ' entr' + (usedUrlCount === 1 ? 'y' : 'ies') + ' used for response'; html += ' (from ' + data.top_matches.length + ' chunk matches)

'; urlGroups.forEach(function(group, groupIndex) { const cardClass = group.usedForContext ? 'mxchat-rag-match-used' : 'mxchat-rag-match-below'; const statusIcon = group.usedForContext ? '✅' : '❌'; const statusLabel = group.usedForContext ? 'Used for Response' : 'Not Used'; // Build chunk summary badge let chunkBadge = ''; if (group.isChunked && group.totalChunks > 1) { const usedChunkCount = group.matchedChunks.filter(function(c) { return c.usedForContext; }).length; chunkBadge = '' + usedChunkCount + '/' + group.totalChunks + ' chunks'; } html += '
'; html += '
'; html += '' + group.bestScore + '%'; html += chunkBadge; html += ''; html += statusIcon + ' ' + statusLabel; html += ''; html += '
'; html += '
'; if (group.isUrl) { html += ''; html += '🔗 ' + escapeHtml(group.url); html += ''; } else { html += '📄 ' + escapeHtml(group.url); } html += '
'; // Show role restriction if not public if (group.roleRestriction && group.roleRestriction !== 'public') { html += '
'; html += '🔒 ' + escapeHtml(group.roleRestriction) + ''; html += '
'; } // Expandable chunk details if multiple chunks if (group.matchedChunks.length > 1) { html += '
▶ Show ' + group.matchedChunks.length + ' matched chunks
'; html += '
'; // Sort chunks by index const sortedChunks = group.matchedChunks.slice().sort(function(a, b) { return (a.chunkIndex || 0) - (b.chunkIndex || 0); }); sortedChunks.forEach(function(chunk) { const chunkNum = (chunk.chunkIndex !== null && chunk.chunkIndex !== undefined) ? chunk.chunkIndex + 1 : '?'; const chunkClass = chunk.usedForContext ? 'chunk-used' : 'chunk-not-used'; const chunkIcon = chunk.usedForContext ? '✓' : '○'; html += '
'; html += '' + chunkIcon + ''; html += 'Chunk ' + chunkNum + ''; html += '' + chunk.score + '%'; html += '
'; }); html += '
'; } html += '
'; }); html += '
'; } else { html += '
No document matches found for this response.
'; } // Approved URLs section if (data.approved_urls && data.approved_urls.length > 0) { html += '
'; html += '

Approved URLs for Citations (' + data.approved_urls.length + ')

'; html += ''; html += '
'; } $container.html(html); // Add click handlers for chunk toggles $container.find('.mxchat-rag-chunk-toggle').on('click', function() { const groupId = $(this).data('group'); const $details = $container.find('.mxchat-rag-chunk-details[data-group="' + groupId + '"]'); const isExpanded = $details.is(':visible'); if (isExpanded) { $details.slideUp(200); $(this).text('▶ Show ' + $details.find('.mxchat-rag-chunk-row').length + ' matched chunks'); } else { $details.slideDown(200); $(this).text('▼ Hide chunks'); } }); } // Helper function to escape HTML function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Close RAG context modal $('.mxchat-rag-modal-close').on('click', function() { $('#mxchat-rag-context-modal').fadeOut(300); }); // Close modal on outside click $('#mxchat-rag-context-modal').on('click', function(e) { if ($(e.target).is('#mxchat-rag-context-modal')) { $(this).fadeOut(300); } }); // Close modal on Escape key $(document).on('keydown', function(e) { if (e.key === 'Escape') { $('#mxchat-rag-context-modal').fadeOut(300); } }); });