PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.6.5
MxChat – AI Chatbot & Content Generation for WordPress v1.6.5
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 1.6.5, at js/mxchat-admin.js

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