PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.0.7
MxChat – AI Chatbot & Content Generation for WordPress v3.0.7
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_actions.js

mxchat_actions.js in MxChat – AI Chatbot & Content Generation for WordPress 3.0.7, at js/mxchat_actions.js

937 lines 34.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * MxChat Actions Page JavaScript - v2.0
3 * Split-panel layout with action list and editor
4 */
5 jQuery(document).ready(function($) {
6 // ==========================================================================
7 // Sidebar Navigation
8 // ==========================================================================
9
10 // Desktop sidebar navigation
11 $('.mxch-nav-link').on('click', function(e) {
12 e.preventDefault();
13 const target = $(this).data('target');
14
15 // Update active states
16 $('.mxch-nav-link').removeClass('active');
17 $(this).addClass('active');
18
19 // Show target section
20 $('.mxch-section').removeClass('active');
21 $('#' + target).addClass('active');
22
23 // Also update mobile nav if open
24 $('.mxch-mobile-nav-link').removeClass('active');
25 $('.mxch-mobile-nav-link[data-target="' + target + '"]').addClass('active');
26
27 // Load actions when switching to all-actions
28 if (target === 'all-actions' && !actionsLoaded) {
29 loadActionList(1, '', '');
30 }
31 });
32
33 // Mobile menu toggle
34 $('.mxch-mobile-menu-btn').on('click', function() {
35 $('.mxch-mobile-menu').addClass('open');
36 $('.mxch-mobile-overlay').addClass('open');
37 });
38
39 // Close mobile menu
40 $('.mxch-mobile-menu-close, .mxch-mobile-overlay').on('click', function() {
41 $('.mxch-mobile-menu').removeClass('open');
42 $('.mxch-mobile-overlay').removeClass('open');
43 });
44
45 // Mobile navigation
46 $('.mxch-mobile-nav-link').on('click', function(e) {
47 e.preventDefault();
48 const target = $(this).data('target');
49
50 $('.mxch-mobile-nav-link').removeClass('active');
51 $(this).addClass('active');
52
53 $('.mxch-section').removeClass('active');
54 $('#' + target).addClass('active');
55
56 $('.mxch-nav-link').removeClass('active');
57 $('.mxch-nav-link[data-target="' + target + '"]').addClass('active');
58
59 $('.mxch-mobile-menu').removeClass('open');
60 $('.mxch-mobile-overlay').removeClass('open');
61 });
62
63 // Quick action buttons
64 $('.mxch-quick-action-btn[data-action="view-actions"]').on('click', function() {
65 $('.mxch-nav-link[data-target="all-actions"]').trigger('click');
66 });
67
68 $('#mxch-add-action-dashboard-btn').on('click', function() {
69 $('.mxch-nav-link[data-target="all-actions"]').trigger('click');
70 setTimeout(function() {
71 openActionEditor();
72 }, 100);
73 });
74
75 // ==========================================================================
76 // Mobile Detection & Panel Management
77 // ==========================================================================
78
79 function isMobile() {
80 return window.innerWidth <= 782;
81 }
82
83 function showMobileDetailPanel() {
84 if (isMobile()) {
85 $('.mxch-action-detail-panel').addClass('mobile-active');
86 $('body').addClass('mxch-mobile-panel-open');
87 }
88 }
89
90 function hideMobileDetailPanel() {
91 $('.mxch-action-detail-panel').removeClass('mobile-active');
92 $('body').removeClass('mxch-mobile-panel-open');
93 }
94
95 // Mobile back button handler
96 $(document).on('click', '.mxch-mobile-back-btn', function(e) {
97 e.preventDefault();
98 hideMobileDetailPanel();
99 resetEditorPanel();
100 $('.mxch-action-item').removeClass('active');
101 currentActionId = null;
102 });
103
104 // ==========================================================================
105 // Action List - Split Panel
106 // ==========================================================================
107
108 let currentPage = 1;
109 const perPage = 50;
110 let totalPages = 1;
111 let currentActionId = null;
112 let actionsLoaded = false;
113 let selectedActions = new Set();
114 let currentSortOrder = 'desc';
115 let currentFilter = '';
116
117 // Load action list on page load
118 loadActionList(1, '', '');
119
120 // Search functionality with debounce
121 let searchTimeout;
122 $('#mxch-search-actions').on('input', function() {
123 clearTimeout(searchTimeout);
124 const searchTerm = $(this).val().toLowerCase();
125
126 searchTimeout = setTimeout(function() {
127 currentPage = 1;
128 loadActionList(currentPage, searchTerm, currentFilter);
129 }, 300);
130 });
131
132 // Filter by type
133 $('#mxch-filter-actions').on('change', function() {
134 currentFilter = $(this).val();
135 currentPage = 1;
136 loadActionList(currentPage, $('#mxch-search-actions').val(), currentFilter);
137 });
138
139 // Refresh button
140 $('#mxch-refresh-actions').on('click', function() {
141 const $btn = $(this);
142 $btn.addClass('spinning');
143 loadActionList(currentPage, $('#mxch-search-actions').val(), currentFilter);
144 setTimeout(() => $btn.removeClass('spinning'), 500);
145 });
146
147 // ==========================================================================
148 // Bulk Selection & Actions
149 // ==========================================================================
150
151 // Select all checkbox
152 $('#mxch-select-all-actions').on('change', function() {
153 const isChecked = $(this).is(':checked');
154 $('.mxch-action-checkbox').prop('checked', isChecked);
155
156 if (isChecked) {
157 $('.mxch-action-item').each(function() {
158 selectedActions.add($(this).data('action-id'));
159 $(this).addClass('selected');
160 });
161 $('#mxch-action-list').addClass('selection-mode');
162 } else {
163 selectedActions.clear();
164 $('.mxch-action-item').removeClass('selected');
165 $('#mxch-action-list').removeClass('selection-mode');
166 }
167
168 updateSelectionUI();
169 });
170
171 // Update selection UI
172 function updateSelectionUI() {
173 const count = selectedActions.size;
174 const $countEl = $('#mxch-selected-action-count');
175 const $deleteBtn = $('#mxch-delete-selected-actions');
176
177 if (count > 0) {
178 $countEl.text(count + ' selected').addClass('has-selection');
179 $deleteBtn.prop('disabled', false);
180 $('#mxch-action-list').addClass('selection-mode');
181 } else {
182 $countEl.removeClass('has-selection');
183 $deleteBtn.prop('disabled', true);
184 $('#mxch-action-list').removeClass('selection-mode');
185 }
186
187 // Update select all checkbox state
188 const totalItems = $('.mxch-action-checkbox').length;
189 const checkedItems = $('.mxch-action-checkbox:checked').length;
190 $('#mxch-select-all-actions').prop('checked', totalItems > 0 && checkedItems === totalItems);
191 $('#mxch-select-all-actions').prop('indeterminate', checkedItems > 0 && checkedItems < totalItems);
192 }
193
194 // Delete selected button
195 $('#mxch-delete-selected-actions').on('click', function() {
196 const count = selectedActions.size;
197 if (count === 0) return;
198
199 if (!confirm(mxchActionsData.i18n.confirmBulkDelete)) {
200 return;
201 }
202
203 deleteMultipleActions(Array.from(selectedActions));
204 });
205
206 // Delete multiple actions
207 function deleteMultipleActions(actionIds) {
208 showLoading();
209
210 $.ajax({
211 url: mxchActionsData.ajaxUrl,
212 type: 'POST',
213 data: {
214 action: 'mxchat_bulk_delete_actions',
215 action_ids: actionIds,
216 security: mxchActionsData.deleteNonce
217 },
218 success: function(response) {
219 hideLoading();
220
221 if (response.success) {
222 // Clear selection
223 selectedActions.clear();
224 $('#mxch-select-all-actions').prop('checked', false);
225 updateSelectionUI();
226
227 // If current action was deleted, reset panel
228 if (actionIds.includes(currentActionId)) {
229 currentActionId = null;
230 resetEditorPanel();
231 }
232
233 // Reload list
234 loadActionList(currentPage, $('#mxch-search-actions').val(), currentFilter);
235 } else {
236 alert(response.data || mxchActionsData.i18n.error);
237 }
238 },
239 error: function() {
240 hideLoading();
241 alert(mxchActionsData.i18n.error);
242 }
243 });
244 }
245
246 // Load action list function
247 function loadActionList(page, searchTerm, filter) {
248 const $container = $('#mxch-action-list');
249 $container.html('<div class="mxch-list-loading"><span class="spinner is-active"></span></div>');
250
251 $.ajax({
252 url: mxchActionsData.ajaxUrl,
253 type: 'POST',
254 data: {
255 action: 'mxchat_fetch_actions_list',
256 page: page,
257 per_page: perPage,
258 search: searchTerm,
259 callback_filter: filter,
260 sort_order: currentSortOrder,
261 security: mxchActionsData.nonce
262 },
263 success: function(response) {
264 actionsLoaded = true;
265
266 if (response.success && response.data.actions && response.data.actions.length > 0) {
267 renderActionList(response.data.actions);
268 currentPage = response.data.page;
269 totalPages = response.data.total_pages;
270 updateActionCount(response.data.showing_start, response.data.showing_end, response.data.total_actions);
271 renderPagination(response.data.page, response.data.total_pages, searchTerm, filter);
272 } else {
273 $container.html('<div class="mxch-list-empty"><p>No actions found</p></div>');
274 updateActionCount(0, 0, 0);
275 $('#mxch-actions-pagination').html('');
276 }
277 },
278 error: function() {
279 $container.html('<div class="mxch-list-empty"><p>Error loading actions</p></div>');
280 }
281 });
282 }
283
284 // Render action list items
285 function renderActionList(actions) {
286 const $container = $('#mxch-action-list');
287 let html = '';
288
289 actions.forEach(function(action) {
290 const isActive = action.id == currentActionId ? ' active' : '';
291 const isSelected = selectedActions.has(action.id) ? ' selected' : '';
292 const isChecked = selectedActions.has(action.id) ? ' checked' : '';
293 const isDisabled = !action.enabled ? ' disabled' : '';
294 const statusClass = action.enabled ? 'enabled' : 'disabled';
295 const statusText = action.enabled ? 'Enabled' : 'Disabled';
296
297 html += `
298 <div class="mxch-action-item${isActive}${isSelected}${isDisabled}"
299 data-action-id="${action.id}"
300 data-label="${escapeHtml(action.label)}"
301 data-phrases="${escapeHtml(action.phrases)}"
302 data-threshold="${action.threshold}"
303 data-callback="${escapeHtml(action.callback_function)}"
304 data-enabled="${action.enabled ? '1' : '0'}"
305 data-enabled-bots='${escapeHtml(JSON.stringify(action.enabled_bots))}'>
306 <input type="checkbox" class="mxch-action-checkbox"${isChecked}>
307 <div class="mxch-action-icon">
308 <span class="dashicons dashicons-${escapeHtml(action.icon || 'admin-generic')}"></span>
309 </div>
310 <div class="mxch-action-info">
311 <div class="mxch-action-name">${escapeHtml(action.label)}</div>
312 <div class="mxch-action-type-label">${escapeHtml(action.callback_label)}</div>
313 </div>
314 <div class="mxch-action-meta">
315 <span class="mxch-action-status ${statusClass}">${statusText}</span>
316 </div>
317 </div>
318 `;
319 });
320
321 $container.html(html);
322
323 // Attach checkbox handlers
324 $('.mxch-action-checkbox').on('click', function(e) {
325 e.stopPropagation();
326 const $item = $(this).closest('.mxch-action-item');
327 const actionId = $item.data('action-id');
328
329 if ($(this).is(':checked')) {
330 selectedActions.add(actionId);
331 $item.addClass('selected');
332 } else {
333 selectedActions.delete(actionId);
334 $item.removeClass('selected');
335 }
336
337 updateSelectionUI();
338 });
339
340 // Attach click handlers for selecting action
341 $('.mxch-action-item').on('click', function(e) {
342 if ($(e.target).is('.mxch-action-checkbox')) return;
343
344 const actionId = $(this).data('action-id');
345 selectAction(actionId, $(this));
346
347 // Update active state
348 $('.mxch-action-item').removeClass('active');
349 $(this).addClass('active');
350 });
351
352 // Update selection UI after render
353 updateSelectionUI();
354 }
355
356 // Update action count display
357 function updateActionCount(start, end, total) {
358 if (total === 0) {
359 $('#mxch-action-count').text('0 actions');
360 } else {
361 $('#mxch-action-count').text(`${start}-${end} / ${total} actions`);
362 }
363 }
364
365 // Render pagination
366 function renderPagination(currentPage, totalPages, searchTerm, filter) {
367 const $container = $('#mxch-actions-pagination');
368
369 if (totalPages <= 1) {
370 $container.html('');
371 return;
372 }
373
374 let html = '<div class="mxch-pagination-btns">';
375
376 if (currentPage > 1) {
377 html += `<button class="mxch-page-btn" data-page="${currentPage - 1}">&laquo;</button>`;
378 }
379
380 html += `<span class="mxch-page-info">${currentPage} / ${totalPages}</span>`;
381
382 if (currentPage < totalPages) {
383 html += `<button class="mxch-page-btn" data-page="${currentPage + 1}">&raquo;</button>`;
384 }
385
386 html += '</div>';
387 $container.html(html);
388
389 // Pagination click handlers
390 $('.mxch-page-btn').on('click', function() {
391 const pageNum = $(this).data('page');
392 loadActionList(pageNum, searchTerm, filter);
393 });
394 }
395
396 // ==========================================================================
397 // Action Editor Panel
398 // ==========================================================================
399
400 // Select and view an action
401 function selectAction(actionId, $item) {
402 currentActionId = actionId;
403
404 // Get data from item attributes
405 const data = {
406 id: actionId,
407 label: $item.data('label'),
408 phrases: $item.data('phrases'),
409 threshold: $item.data('threshold'),
410 callback_function: $item.data('callback'),
411 enabled: $item.data('enabled') == '1',
412 enabled_bots: $item.data('enabled-bots') || ['default']
413 };
414
415 showActionView(data);
416 }
417
418 // Show action view (details)
419 function showActionView(action) {
420 $('#mxch-action-empty').hide();
421 $('#mxch-action-editor').show();
422
423 // Hide editor steps, show view
424 $('.mxch-editor-step').removeClass('active');
425 $('#mxch-action-view').addClass('active');
426
427 // Show detail panel on mobile
428 showMobileDetailPanel();
429
430 // Populate view
431 $('#mxch-view-title').text(action.label);
432 $('#mxch-view-label').text(action.label);
433
434 // Get callback label from filter dropdown
435 const callbackLabel = $('#mxch-filter-actions option[value="' + action.callback_function + '"]').text() || action.callback_function;
436 $('#mxch-view-type').text(callbackLabel);
437
438 // Get icon from type grid
439 const $typeCard = $('.mxch-type-card[data-value="' + action.callback_function + '"]');
440 const iconClass = $typeCard.find('.dashicons').attr('class') || 'dashicons dashicons-admin-generic';
441 $('#mxch-view-icon').html('<span class="' + iconClass + '"></span>');
442 $('#mxch-detail-icon').html('<span class="' + iconClass + '"></span>');
443
444 // Phrases
445 const phrases = action.phrases.split(',').map(p => p.trim()).filter(p => p);
446 let phrasesHtml = '';
447 phrases.forEach(function(phrase) {
448 phrasesHtml += '<span class="mxch-phrase-tag">' + escapeHtml(phrase) + '</span>';
449 });
450 $('#mxch-view-phrases').html(phrasesHtml);
451
452 // Settings
453 $('#mxch-view-threshold').text(action.threshold + '%');
454
455 const statusText = action.enabled ? 'Enabled' : 'Disabled';
456 const statusClass = action.enabled ? 'mxch-status-enabled' : 'mxch-status-disabled';
457 $('#mxch-view-status').text(statusText).removeClass('mxch-status-enabled mxch-status-disabled').addClass(statusClass);
458
459 // Toggle switch
460 $('#mxch-action-enabled-toggle').prop('checked', action.enabled);
461
462 // Bots
463 let botsHtml = '';
464 const enabledBots = Array.isArray(action.enabled_bots) ? action.enabled_bots : ['default'];
465 enabledBots.forEach(function(bot) {
466 const botName = bot === 'default' ? 'Default Bot' : bot;
467 botsHtml += '<span class="mxch-bot-tag">' + escapeHtml(botName) + '</span>';
468 });
469 $('#mxch-view-bots').html(botsHtml);
470
471 // Store current action data for editing
472 $('#mxch-action-view').data('current-action', action);
473 }
474
475 // Open action editor (for creating new)
476 function openActionEditor() {
477 currentActionId = null;
478
479 $('#mxch-action-empty').hide();
480 $('#mxch-action-editor').show();
481
482 // Show step 1
483 $('.mxch-editor-step').removeClass('active');
484 $('#mxch-editor-step-1').addClass('active');
485
486 // Reset form
487 resetForm();
488
489 // Show detail panel on mobile
490 showMobileDetailPanel();
491 }
492
493 // Open action editor for editing
494 function openActionEditorForEdit(action) {
495 currentActionId = action.id;
496
497 $('#mxch-action-empty').hide();
498 $('#mxch-action-editor').show();
499
500 // Go directly to step 2
501 $('.mxch-editor-step').removeClass('active');
502 $('#mxch-editor-step-2').addClass('active');
503
504 // Populate form
505 $('#mxch-action-id').val(action.id);
506 $('#mxch-callback-function').val(action.callback_function);
507 $('#mxch-form-mode').val('edit');
508 $('#mxch-action-label').val(action.label);
509 $('#mxch-action-phrases').val(action.phrases);
510 $('#mxch-action-threshold').val(action.threshold);
511 $('#mxch-threshold-value').text(action.threshold + '%');
512
513 // Set selected type display
514 const $typeCard = $('.mxch-type-card[data-value="' + action.callback_function + '"]');
515 const iconHtml = $typeCard.find('.mxch-type-icon').html();
516 const typeLabel = $typeCard.data('label');
517 const typeDesc = $typeCard.find('p').text();
518
519 $('#mxch-selected-type-icon').html(iconHtml);
520 $('#mxch-selected-type-label').text(typeLabel);
521 $('#mxch-selected-type-desc').text(typeDesc);
522 $('#mxch-config-title').text('Edit Action');
523
524 // Set enabled bots
525 const enabledBots = Array.isArray(action.enabled_bots) ? action.enabled_bots : ['default'];
526 $('#mxch-bot-selector input[type="checkbox"]').each(function() {
527 $(this).prop('checked', enabledBots.includes($(this).val()));
528 });
529
530 // Update save button text
531 $('#mxch-save-action').text('Update Action');
532 }
533
534 // Reset form
535 function resetForm() {
536 $('#mxch-action-id').val('');
537 $('#mxch-callback-function').val('');
538 $('#mxch-form-mode').val('add');
539 $('#mxch-action-label').val('');
540 $('#mxch-action-phrases').val('');
541 $('#mxch-action-threshold').val(85);
542 $('#mxch-threshold-value').text('85%');
543 $('#mxch-config-title').text('Configure Action');
544 $('#mxch-save-action').text('Save Action');
545
546 // Reset bot selection
547 $('#mxch-bot-selector input[type="checkbox"]').prop('checked', false);
548 $('#mxch-bot-selector input[value="default"]').prop('checked', true);
549
550 // Reset type selection
551 $('.mxch-type-card').removeClass('selected');
552 }
553
554 // Reset editor panel to empty state
555 function resetEditorPanel() {
556 $('#mxch-action-editor').hide();
557 $('#mxch-action-empty').show();
558 $('.mxch-editor-step').removeClass('active');
559
560 // Hide mobile panel
561 hideMobileDetailPanel();
562 }
563
564 // ==========================================================================
565 // Editor Event Handlers
566 // ==========================================================================
567
568 // Add new action buttons
569 $('#mxch-add-action-btn, #mxch-create-first-action-btn').on('click', function() {
570 openActionEditor();
571 });
572
573 // Close editor buttons
574 $('#mxch-close-editor, #mxch-close-editor-2').on('click', function() {
575 resetEditorPanel();
576 $('.mxch-action-item').removeClass('active');
577 currentActionId = null;
578 });
579
580 // Cancel button
581 $('#mxch-cancel-action').on('click', function() {
582 if (currentActionId && $('#mxch-form-mode').val() === 'edit') {
583 // Go back to view mode
584 const action = $('#mxch-action-view').data('current-action');
585 if (action) {
586 showActionView(action);
587 }
588 } else {
589 resetEditorPanel();
590 }
591 });
592
593 // Back to step 1
594 $('#mxch-back-to-step-1').on('click', function() {
595 if ($('#mxch-form-mode').val() === 'edit') {
596 // If editing, go back to view
597 const action = $('#mxch-action-view').data('current-action');
598 if (action) {
599 showActionView(action);
600 }
601 } else {
602 // If creating, go back to step 1
603 $('.mxch-editor-step').removeClass('active');
604 $('#mxch-editor-step-1').addClass('active');
605 }
606 });
607
608 // Edit action button
609 $('#mxch-edit-action-btn').on('click', function() {
610 const action = $('#mxch-action-view').data('current-action');
611 if (action) {
612 openActionEditorForEdit(action);
613 }
614 });
615
616 // Toggle enabled status
617 $('#mxch-action-enabled-toggle').on('change', function() {
618 if (!currentActionId) return;
619
620 const isEnabled = $(this).is(':checked');
621 toggleActionStatus(currentActionId, isEnabled);
622 });
623
624 // Delete action button
625 $('#mxch-delete-action-btn').on('click', function() {
626 if (!currentActionId) return;
627 showDeleteModal(currentActionId);
628 });
629
630 // ==========================================================================
631 // Action Type Selection (Step 1)
632 // ==========================================================================
633
634 // Type search
635 $('#mxch-type-search-input').on('input', function() {
636 const searchTerm = $(this).val().toLowerCase();
637 filterTypeCards(searchTerm, 'all');
638 });
639
640 // Category buttons
641 $('.mxch-category-btn').on('click', function() {
642 $('.mxch-category-btn').removeClass('active');
643 $(this).addClass('active');
644
645 const category = $(this).data('category');
646 const searchTerm = $('#mxch-type-search-input').val().toLowerCase();
647 filterTypeCards(searchTerm, category);
648 });
649
650 // Filter type cards
651 function filterTypeCards(searchTerm, category) {
652 $('.mxch-type-card').each(function() {
653 const $card = $(this);
654 const cardCategory = $card.data('category');
655 const cardLabel = $card.data('label').toLowerCase();
656 const cardDesc = $card.find('p').text().toLowerCase();
657
658 const matchesCategory = category === 'all' || cardCategory === category;
659 const matchesSearch = !searchTerm || cardLabel.includes(searchTerm) || cardDesc.includes(searchTerm);
660
661 if (matchesCategory && matchesSearch) {
662 $card.show();
663 } else {
664 $card.hide();
665 }
666 });
667 }
668
669 // Type card click
670 $('.mxch-type-card').on('click', function() {
671 const $card = $(this);
672
673 // Check if pro required
674 if ($card.hasClass('mxch-type-pro') && !mxchActionsData.isActivated) {
675 alert(mxchActionsData.i18n.proRequired);
676 return;
677 }
678
679 // Check if addon required
680 if ($card.hasClass('mxch-type-addon') && $card.data('installed') !== true) {
681 alert(mxchActionsData.i18n.addonRequired);
682 return;
683 }
684
685 // Select this card
686 $('.mxch-type-card').removeClass('selected');
687 $card.addClass('selected');
688
689 // Get card data
690 const callback = $card.data('value');
691 const label = $card.data('label');
692 const iconHtml = $card.find('.mxch-type-icon').html();
693 const desc = $card.find('p').text();
694
695 // Populate step 2
696 $('#mxch-callback-function').val(callback);
697 $('#mxch-selected-type-icon').html(iconHtml);
698 $('#mxch-selected-type-label').text(label);
699 $('#mxch-selected-type-desc').text(desc);
700
701 // Go to step 2
702 $('.mxch-editor-step').removeClass('active');
703 $('#mxch-editor-step-2').addClass('active');
704 });
705
706 // ==========================================================================
707 // Form Handling
708 // ==========================================================================
709
710 // Threshold slider
711 $('#mxch-action-threshold').on('input', function() {
712 $('#mxch-threshold-value').text($(this).val() + '%');
713 });
714
715 // Form submission
716 $('#mxch-action-form').on('submit', function(e) {
717 e.preventDefault();
718
719 const formMode = $('#mxch-form-mode').val();
720 const actionId = $('#mxch-action-id').val();
721 const callbackFunction = $('#mxch-callback-function').val();
722 const label = $('#mxch-action-label').val().trim();
723 const phrases = $('#mxch-action-phrases').val().trim();
724 const threshold = $('#mxch-action-threshold').val();
725
726 // Validation
727 if (!callbackFunction) {
728 alert('Please select an action type.');
729 return;
730 }
731
732 if (!label) {
733 alert('Please enter an action label.');
734 $('#mxch-action-label').focus();
735 return;
736 }
737
738 if (!phrases) {
739 alert('Please enter at least one trigger phrase.');
740 $('#mxch-action-phrases').focus();
741 return;
742 }
743
744 // Get enabled bots
745 const enabledBots = [];
746 $('#mxch-bot-selector input[type="checkbox"]:checked').each(function() {
747 enabledBots.push($(this).val());
748 });
749
750 if (enabledBots.length === 0) {
751 enabledBots.push('default');
752 }
753
754 showLoading();
755
756 const data = {
757 action: formMode === 'edit' ? 'mxchat_edit_intent_ajax' : 'mxchat_add_intent_ajax',
758 intent_label: label,
759 phrases: phrases,
760 similarity_threshold: threshold,
761 callback_function: callbackFunction,
762 enabled_bots: enabledBots,
763 security: formMode === 'edit' ? mxchActionsData.editNonce : mxchActionsData.addNonce
764 };
765
766 if (formMode === 'edit') {
767 data.intent_id = actionId;
768 }
769
770 $.ajax({
771 url: mxchActionsData.ajaxUrl,
772 type: 'POST',
773 data: data,
774 success: function(response) {
775 hideLoading();
776
777 if (response.success) {
778 // Reload list
779 loadActionList(currentPage, $('#mxch-search-actions').val(), currentFilter);
780
781 // Reset and close editor
782 resetEditorPanel();
783 currentActionId = null;
784
785 // Show success message (could use a toast notification)
786 // For now, just reload
787 } else {
788 alert(response.data || mxchActionsData.i18n.error);
789 }
790 },
791 error: function() {
792 hideLoading();
793 alert(mxchActionsData.i18n.error);
794 }
795 });
796 });
797
798 // ==========================================================================
799 // Toggle Action Status
800 // ==========================================================================
801
802 function toggleActionStatus(actionId, isEnabled) {
803 // Optimistic UI update - update immediately before AJAX completes
804 const $item = $('.mxch-action-item[data-action-id="' + actionId + '"]');
805 const $status = $item.find('.mxch-action-status');
806 const $toggle = $('#mxch-action-enabled-toggle');
807
808 // Immediately update UI
809 $item.data('enabled', isEnabled ? '1' : '0');
810 if (isEnabled) {
811 $status.removeClass('disabled').addClass('enabled').text('Enabled');
812 $item.removeClass('disabled');
813 } else {
814 $status.removeClass('enabled').addClass('disabled').text('Disabled');
815 $item.addClass('disabled');
816 }
817
818 // Update view status immediately
819 const statusText = isEnabled ? 'Enabled' : 'Disabled';
820 const statusClass = isEnabled ? 'mxch-status-enabled' : 'mxch-status-disabled';
821 $('#mxch-view-status').text(statusText).removeClass('mxch-status-enabled mxch-status-disabled').addClass(statusClass);
822
823 // Update stored data immediately
824 const currentData = $('#mxch-action-view').data('current-action');
825 if (currentData) {
826 currentData.enabled = isEnabled;
827 $('#mxch-action-view').data('current-action', currentData);
828 }
829
830 $.ajax({
831 url: mxchActionsData.ajaxUrl,
832 type: 'POST',
833 data: {
834 action: 'mxchat_toggle_action_status',
835 action_id: actionId,
836 enabled: isEnabled ? 1 : 0,
837 security: mxchActionsData.toggleNonce
838 },
839 success: function(response) {
840 if (!response.success) {
841 // Revert optimistic update on failure
842 revertToggleUI(!isEnabled, $item, $status, $toggle);
843 alert(response.data || mxchActionsData.i18n.error);
844 }
845 },
846 error: function() {
847 // Revert optimistic update on error
848 revertToggleUI(!isEnabled, $item, $status, $toggle);
849 alert(mxchActionsData.i18n.error);
850 }
851 });
852 }
853
854 // Helper to revert toggle UI on error
855 function revertToggleUI(revertEnabled, $item, $status, $toggle) {
856 $toggle.prop('checked', revertEnabled);
857 $item.data('enabled', revertEnabled ? '1' : '0');
858 if (revertEnabled) {
859 $status.removeClass('disabled').addClass('enabled').text('Enabled');
860 $item.removeClass('disabled');
861 } else {
862 $status.removeClass('enabled').addClass('disabled').text('Disabled');
863 $item.addClass('disabled');
864 }
865 const statusText = revertEnabled ? 'Enabled' : 'Disabled';
866 const statusClass = revertEnabled ? 'mxch-status-enabled' : 'mxch-status-disabled';
867 $('#mxch-view-status').text(statusText).removeClass('mxch-status-enabled mxch-status-disabled').addClass(statusClass);
868 }
869
870 // ==========================================================================
871 // Delete Modal
872 // ==========================================================================
873
874 function showDeleteModal(actionId) {
875 $('#mxch-delete-modal').show();
876 $('#mxch-confirm-delete').data('action-id', actionId);
877 }
878
879 $('#mxch-delete-modal .mxch-modal-close, #mxch-cancel-delete').on('click', function() {
880 $('#mxch-delete-modal').hide();
881 });
882
883 $('#mxch-confirm-delete').on('click', function() {
884 const actionId = $(this).data('action-id');
885 $('#mxch-delete-modal').hide();
886 deleteAction(actionId);
887 });
888
889 function deleteAction(actionId) {
890 showLoading();
891
892 $.ajax({
893 url: mxchActionsData.ajaxUrl,
894 type: 'POST',
895 data: {
896 action: 'mxchat_delete_intent_ajax',
897 intent_id: actionId,
898 security: mxchActionsData.deleteNonce
899 },
900 success: function(response) {
901 hideLoading();
902
903 if (response.success) {
904 currentActionId = null;
905 resetEditorPanel();
906 loadActionList(currentPage, $('#mxch-search-actions').val(), currentFilter);
907 } else {
908 alert(response.data || mxchActionsData.i18n.error);
909 }
910 },
911 error: function() {
912 hideLoading();
913 alert(mxchActionsData.i18n.error);
914 }
915 });
916 }
917
918 // ==========================================================================
919 // Utility Functions
920 // ==========================================================================
921
922 function showLoading() {
923 $('#mxch-action-loading').show();
924 }
925
926 function hideLoading() {
927 $('#mxch-action-loading').hide();
928 }
929
930 function escapeHtml(text) {
931 if (!text) return '';
932 const div = document.createElement('div');
933 div.appendChild(document.createTextNode(text));
934 return div.innerHTML;
935 }
936 });
937