PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.0.3
MxChat – AI Chatbot & Content Generation for WordPress v2.0.3
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-admin.js

mxchat-admin.js in MxChat – AI Chatbot & Content Generation for WordPress 2.0.3, at js/mxchat-admin.js

553 lines 21.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // Simple debounce function implementation
2 function debounce(func, wait) {
3 let timeout;
4 return function executedFunction(...args) {
5 const later = () => {
6 clearTimeout(timeout);
7 func(...args);
8 };
9 clearTimeout(timeout);
10 timeout = setTimeout(later, wait);
11 };
12 }
13
14 // Helper function to open edit modal
15 function mxchatOpenEditModal(intentId, phrases) {
16 const modal = document.getElementById('mxchat-edit-modal');
17 if (!modal) return;
18
19 // Get form fields
20 const intentIdField = document.getElementById('edit_intent_id');
21 const phrasesField = document.getElementById('edit_phrases');
22
23 // Set values
24 intentIdField.value = intentId;
25 phrasesField.value = phrases;
26
27 // Show modal with animation
28 modal.style.display = 'flex';
29 requestAnimationFrame(() => {
30 modal.classList.add('active');
31 });
32
33 // Set up close handlers
34 const closeModal = () => {
35 modal.classList.remove('active');
36 setTimeout(() => {
37 modal.style.display = 'none';
38 }, 300); // Match the CSS transition time
39 };
40
41 // Close button handler
42 const closeBtn = modal.querySelector('.mxchat-modal-close');
43 if (closeBtn) {
44 closeBtn.onclick = closeModal;
45 }
46
47 // Cancel button handler
48 const cancelBtn = modal.querySelector('.mxchat-modal-cancel');
49 if (cancelBtn) {
50 cancelBtn.onclick = closeModal;
51 }
52
53 // Click outside modal to close
54 modal.onclick = (e) => {
55 if (e.target === modal) {
56 closeModal();
57 }
58 };
59
60 // Focus the textarea
61 phrasesField.focus();
62 }
63
64 // Initialize event listeners
65 document.addEventListener('DOMContentLoaded', () => {
66 // Set up edit button handlers
67 document.querySelectorAll('.mxchat-edit-button').forEach(button => {
68 button.onclick = () => {
69 const intentId = button.dataset.intentId;
70 const phrases = button.dataset.phrases;
71 mxchatOpenEditModal(intentId, phrases);
72 };
73 });
74 });
75
76 jQuery(document).ready(function($) {
77 // Ensure we have a debounce function (use lodash if available, otherwise use our implementation)
78 const useDebounce = (window._ && window._.debounce) ? window._.debounce : debounce;
79
80 // --- AJAX Auto-Save ---
81 const $autosaveSections = $('.mxchat-autosave-section');
82
83 if ($autosaveSections.length) {
84 // Handle real-time range slider value updates
85 $autosaveSections.find('input[type="range"]').on('input', function() {
86 const value = $(this).val();
87 $('#threshold_value').text(value);
88 });
89
90 // Handle all input changes (including range slider)
91 $autosaveSections.find('input, textarea, select').on('change', function() {
92 const $field = $(this);
93 const name = $field.attr('name');
94 let value;
95
96 // Handle different input types
97 if ($field.attr('type') === 'checkbox') {
98 value = $field.is(':checked') ? 'on' : 'off';
99 } else {
100 value = $field.val();
101 }
102
103 // Create feedback container
104 const feedbackContainer = $('<div class="feedback-container"></div>');
105 const spinner = $('<div class="saving-spinner"></div>');
106 const successIcon = $('<div class="success-icon">✔</div>');
107
108 // Position feedback container based on input type
109 if ($field.closest('.toggle-switch').length) {
110 $field.closest('td').append(feedbackContainer);
111 } else if ($field.closest('.mxchat-toggle-switch').length) {
112 $field.closest('.mxchat-toggle-container').append(feedbackContainer);
113 } else if ($field.closest('.slider-container').length) {
114 $field.closest('.slider-container').after(feedbackContainer);
115 } else {
116 $field.after(feedbackContainer);
117 }
118 feedbackContainer.append(spinner);
119
120 // Determine which AJAX action and nonce to use:
121 var ajaxAction, nonce;
122 // Use the new AJAX action for submenu fields:
123 if ( name.indexOf('mxchat_prompts_options') !== -1 ||
124 name === 'mxchat_auto_sync_posts' ||
125 name === 'mxchat_auto_sync_pages' ) {
126 ajaxAction = 'mxchat_save_prompts_setting';
127 nonce = mxchatPromptsAdmin.prompts_setting_nonce;
128 } else {
129 // Otherwise, use the existing AJAX action.
130 ajaxAction = 'mxchat_save_setting';
131 nonce = mxchatAdmin.setting_nonce;
132 }
133
134 // AJAX save request
135 $.ajax({
136 url: (ajaxAction === 'mxchat_save_prompts_setting') ? mxchatPromptsAdmin.ajax_url : mxchatAdmin.ajax_url,
137 type: 'POST',
138 data: {
139 action: ajaxAction,
140 name: name,
141 value: value,
142 _ajax_nonce: nonce
143 },
144 success: function(response) {
145 if (response.success) {
146 spinner.fadeOut(200, function() {
147 feedbackContainer.append(successIcon);
148 successIcon.fadeIn(200).delay(1000).fadeOut(200, function() {
149 feedbackContainer.remove();
150 });
151 });
152 } else {
153 alert('Error saving: ' + (response.data?.message || 'Unknown error'));
154 if ($field.attr('type') === 'checkbox') {
155 $field.prop('checked', !$field.is(':checked'));
156 }
157 feedbackContainer.remove();
158 }
159 },
160 error: function() {
161 alert('An error occurred while saving.');
162 if ($field.attr('type') === 'checkbox') {
163 $field.prop('checked', !$field.is(':checked'));
164 }
165 feedbackContainer.remove();
166 }
167 });
168 });
169
170 // Initialize color pickers with debouncing
171 $autosaveSections.find('.my-color-field').each(function() {
172 const $colorField = $(this);
173
174 $(this).wpColorPicker({
175 change: useDebounce(function(event, ui) {
176 // Safety check - ensure we have a valid field and value
177 if (!$colorField || !$colorField.val()) {
178 console.warn('Color picker not ready');
179 return;
180 }
181
182 const name = $colorField.attr('name');
183 const value = $colorField.val();
184
185 if (!name || !value) {
186 console.warn('Missing required color picker values');
187 return;
188 }
189
190 // Create feedback container
191 const feedbackContainer = $('<div class="feedback-container"></div>');
192 const spinner = $('<div class="saving-spinner"></div>');
193 const successIcon = $('<div class="success-icon">✔</div>');
194
195 // Position feedback container
196 $colorField.closest('.wp-picker-container').after(feedbackContainer);
197 feedbackContainer.append(spinner);
198
199 // Determine AJAX action and nonce for color fields:
200 var ajaxAction, nonce;
201 if ( name.indexOf('mxchat_prompts_options') !== -1 ||
202 name === 'mxchat_auto_sync_posts' ||
203 name === 'mxchat_auto_sync_pages' ) {
204 ajaxAction = 'mxchat_save_prompts_setting';
205 nonce = mxchatPromptsAdmin.prompts_setting_nonce;
206 } else {
207 ajaxAction = 'mxchat_save_setting';
208 nonce = mxchatAdmin.setting_nonce;
209 }
210
211 // AJAX save request
212 $.ajax({
213 url: (ajaxAction === 'mxchat_save_prompts_setting') ? mxchatPromptsAdmin.ajax_url : mxchatAdmin.ajax_url,
214 type: 'POST',
215 data: {
216 action: ajaxAction,
217 name: name,
218 value: value,
219 _ajax_nonce: nonce
220 },
221 success: function(response) {
222 if (response.success) {
223 spinner.fadeOut(200, function() {
224 feedbackContainer.append(successIcon);
225 successIcon.fadeIn(200).delay(1000).fadeOut(200, function() {
226 feedbackContainer.remove();
227 });
228 });
229 } else {
230 alert('Error saving: ' + (response.data?.message || 'Unknown error'));
231 feedbackContainer.remove();
232 }
233 },
234 error: function() {
235 alert('An error occurred while saving.');
236 feedbackContainer.remove();
237 }
238 });
239 }, 500)
240 });
241 });
242
243 // Reinitialize color pickers when switching tabs
244 $('.mxchat-nav-tab').on('click.mxchat', function() {
245 setTimeout(function() {
246 $('.my-color-field:visible').wpColorPicker('close');
247 }, 100);
248 });
249 }
250
251 // Initialize tabs system
252 function initTabs() {
253 // Remove any existing handlers first
254 $('.mxchat-nav-tab').off('click.mxchat');
255
256 // Add new click handlers
257 $('.mxchat-nav-tab').on('click.mxchat', function(e) {
258 e.preventDefault();
259 e.stopPropagation();
260
261 var $this = $(this);
262
263 // Get tab ID - try href first, fallback to data-tab, then to default
264 var tabId = $this.attr('href');
265 if (tabId) {
266 tabId = tabId.replace('#', '');
267 } else {
268 tabId = $this.data('tab') || 'chatbot';
269 }
270
271 // Safety check for empty tabId
272 if (!tabId) {
273 console.warn('No tab identifier found');
274 return;
275 }
276
277 // Update tabs
278 $('.mxchat-nav-tab').removeClass('mxchat-nav-tab-active');
279 $this.addClass('mxchat-nav-tab-active');
280
281 // Update content areas - with safety check
282 $('.mxchat-tab-content').removeClass('active').hide();
283 var $targetTab = $('#' + tabId);
284 if ($targetTab.length) {
285 $targetTab.addClass('active').show();
286
287 // Store active tab
288 try {
289 localStorage.setItem('mxchat_active_tab', tabId);
290 } catch (e) {
291 console.warn('LocalStorage not available:', e);
292 }
293 } else {
294 console.warn('Tab content #' + tabId + ' not found');
295 }
296 });
297 }
298
299 // Initialize tabs and handle events
300 initTabs();
301 $(document).on('widget-added widget-updated postbox-toggled', initTabs);
302
303 // Activate initial tab
304 try {
305 var savedTab = localStorage.getItem('mxchat_active_tab');
306 if (savedTab && $('#' + savedTab).length > 0) {
307 $('.mxchat-nav-tab[href="#' + savedTab + '"]').trigger('click.mxchat');
308 } else {
309 $('.mxchat-nav-tab').first().trigger('click.mxchat');
310 }
311 } catch (e) {
312 $('.mxchat-nav-tab').first().trigger('click.mxchat');
313 }
314
315 // Attach edit modal event handler
316 $(document).on('click', '.mxchat-edit-button', function() {
317 const intentId = $(this).data('intent-id');
318 const phrases = $(this).data('phrases');
319 mxchatOpenEditModal(intentId, phrases);
320 });
321
322 // Toggle visibility handlers
323 function toggleVisibility(selector) {
324 $(selector).on('click', function() {
325 var inputField = $(this).prev('input');
326 if (inputField.attr('type') === 'password') {
327 inputField.attr('type', 'text');
328 $(this).text('Hide');
329 } else {
330 inputField.attr('type', 'password');
331 $(this).text('Show');
332 }
333 });
334 }
335
336 // Initialize all toggle visibility buttons
337 [
338 '#toggleApiKeyVisibility',
339 '#toggleWooCommerceSecretVisibility',
340 '#toggleLoopsApiKeyVisibility',
341 '#toggleXaiApiKeyVisibility',
342 '#toggleClaudeApiKeyVisibility',
343 '#toggleBraveApiKeyVisibility',
344 '#toggleWebhookUrlVisibility',
345 '#toggleSecretKeyVisibility',
346 '#toggleBotTokenVisibility',
347 '#toggleDeepSeekApiKeyVisibility'
348 ].forEach(toggleVisibility);
349
350 // Add Intent Form Submission
351 $('#mxchat-add-intent-form').on('submit', function(event) {
352 $('#mxchat-intent-loading').show();
353 $('#mxchat-intent-loading-text').show();
354 $(this).find('button[type="submit"]').hide();
355 });
356
357 // Inline Edit Functionality
358 $('.edit-button').on('click', function() {
359 var row = $(this).closest('tr');
360 row.find('.content-view, .url-view').hide();
361 row.find('.content-edit, .url-edit').show();
362 row.find('.edit-button').hide();
363 row.find('.save-button').show();
364 });
365
366 // Save button handler
367 $('.save-button').on('click', function() {
368 var button = $(this);
369 var row = button.closest('tr');
370 var id = button.data('id');
371 var newContent = row.find('.content-edit').val();
372 var newUrl = row.find('.url-edit').val();
373
374 button.prop('disabled', true);
375 button.text('Saving...');
376
377 $.ajax({
378 url: mxchatAdmin.ajax_url,
379 type: 'POST',
380 data: {
381 action: 'mxchat_save_inline_prompt',
382 id: id,
383 article_content: newContent,
384 article_url: newUrl,
385 _ajax_nonce: mxchatAdmin.inline_edit_nonce
386 },
387 success: function(response) {
388 button.prop('disabled', false);
389 button.text('Save');
390
391 if (response.success) {
392 row.find('.content-view').html(newContent.replace(/\n/g, "<br>"));
393 if (newUrl) {
394 row.find('.url-view').html('<a href="' + newUrl + '" target="_blank">' + newUrl + '</a>');
395 } else {
396 row.find('.url-view').html('N/A');
397 }
398
399 row.find('.content-edit, .url-edit').hide();
400 row.find('.content-view, .url-view').show();
401 row.find('.save-button').hide();
402 row.find('.edit-button').show();
403 } else {
404 alert('Error saving content: ' + (response.data?.message || 'Unknown error'));
405 }
406 },
407 error: function() {
408 button.prop('disabled', false);
409 button.text('Save');
410 alert('An error occurred while saving.');
411 }
412 });
413 });
414
415 // Activation handling
416 const form = $('#mxchat-activation-form');
417 const spinner = $('#mxchat-activation-spinner');
418 const submitButton = $('#activate_license_button');
419 const licenseStatus = $('#mxchat-license-status');
420
421 if (form.length && licenseStatus.length && submitButton.length) {
422 function handleActivationResponse(response) {
423 spinner.hide();
424 if (response.success) {
425 licenseStatus.text('Active');
426 licenseStatus.removeClass('inactive').addClass('active');
427 form.hide();
428 } else {
429 licenseStatus.text('Inactive');
430 alert(response.data || 'Activation failed. Please check your input.');
431 submitButton.prop('disabled', false);
432 }
433 }
434
435 form.on('submit', function(event) {
436 event.preventDefault();
437 spinner.show();
438 submitButton.prop('disabled', true);
439
440 var formData = {
441 action: 'mxchat_activate_license',
442 mxchat_pro_email: $('#mxchat_pro_email').val(),
443 mxchat_activation_key: $('#mxchat_activation_key').val(),
444 security: mxchatAdmin.license_nonce
445 };
446
447 $.post(mxchatAdmin.ajax_url, formData, function(response) {
448 handleActivationResponse(response);
449 }).fail(function() {
450 alert('Server error. Please try again.');
451 spinner.hide();
452 submitButton.prop('disabled', false);
453 });
454 });
455 }
456
457 // Questions handling
458 $('.mxchat-add-question').on('click', function () {
459 const container = $('#mxchat-additional-questions-container');
460 const questionCount = container.find('.mxchat-question-row').length + 4;
461 const questionIndex = container.find('.mxchat-question-row').length;
462
463 const newQuestion = `
464 <div class="mxchat-question-row">
465 <input type="text"
466 name="additional_popular_questions[]"
467 placeholder="Enter Additional Popular Question ${questionCount}"
468 class="regular-text mxchat-question-input"
469 data-question-index="${questionIndex}" />
470 <button type="button" class="button mxchat-remove-question"
471 aria-label="Remove question">Remove</button>
472 </div>
473 `;
474 container.append(newQuestion);
475 });
476
477 $(document).on('click', '.mxchat-remove-question', function () {
478 $(this).closest('.mxchat-question-row').remove();
479 saveQuestions();
480 });
481
482 $(document).on('change', '.mxchat-question-input', function() {
483 saveQuestions();
484 });
485
486 function saveQuestions() {
487 const questions = [];
488 $('.mxchat-question-input').each(function() {
489 const value = $(this).val().trim();
490 if (value) {
491 questions.push(value);
492 }
493 });
494
495 const feedbackContainer = $('<div class="feedback-container"></div>');
496 const spinner = $('<div class="saving-spinner"></div>');
497 const successIcon = $('<div class="success-icon">✔</div>');
498
499 // Append feedback after the add button
500 $('.mxchat-add-question').after(feedbackContainer);
501 feedbackContainer.append(spinner);
502
503 // Save via AJAX
504 $.ajax({
505 url: mxchatAdmin.ajax_url,
506 type: 'POST',
507 data: {
508 action: 'mxchat_save_setting',
509 name: 'additional_popular_questions',
510 value: JSON.stringify(questions),
511 _ajax_nonce: mxchatAdmin.setting_nonce
512 },
513 success: function(response) {
514 if (response.success) {
515 spinner.fadeOut(200, function() {
516 feedbackContainer.append(successIcon);
517 successIcon.fadeIn(200).delay(1000).fadeOut(200, function() {
518 feedbackContainer.remove();
519 });
520 });
521 } else {
522 alert('Error saving questions: ' + (response.data?.message || 'Unknown error'));
523 feedbackContainer.remove();
524 }
525 },
526 error: function() {
527 alert('An error occurred while saving questions.');
528 feedbackContainer.remove();
529 }
530 });
531 }
532
533 // Live agent status handler
534 const statusToggle = document.getElementById('live_agent_status');
535 const statusText = statusToggle?.parentElement.nextElementSibling?.querySelector('.status-text');
536 if (statusToggle && statusText) {
537 statusToggle.addEventListener('change', function() {
538 // Update display text
539 statusText.textContent = this.checked ? 'Online' : 'Offline';
540
541 // Send the correct on/off value to the server
542 if (window.mxchatSaveSetting) {
543 window.mxchatSaveSetting('live_agent_status', this.checked ? 'on' : 'off');
544 }
545 });
546 }
547
548
549 });
550
551
552
553