/**
* MxChat Actions Page JavaScript - v2.0
* Split-panel layout with action list and editor
*/
jQuery(document).ready(function($) {
// ==========================================================================
// Sidebar Navigation
// ==========================================================================
// Desktop sidebar navigation
$('.mxch-nav-link').on('click', function(e) {
e.preventDefault();
const target = $(this).data('target');
// Update active states
$('.mxch-nav-link').removeClass('active');
$(this).addClass('active');
// Show target section
$('.mxch-section').removeClass('active');
$('#' + target).addClass('active');
// Also update mobile nav if open
$('.mxch-mobile-nav-link').removeClass('active');
$('.mxch-mobile-nav-link[data-target="' + target + '"]').addClass('active');
// Load actions when switching to all-actions
if (target === 'all-actions' && !actionsLoaded) {
loadActionList(1, '', '');
}
});
// Mobile menu toggle
$('.mxch-mobile-menu-btn').on('click', function() {
$('.mxch-mobile-menu').addClass('open');
$('.mxch-mobile-overlay').addClass('open');
});
// Close mobile menu
$('.mxch-mobile-menu-close, .mxch-mobile-overlay').on('click', function() {
$('.mxch-mobile-menu').removeClass('open');
$('.mxch-mobile-overlay').removeClass('open');
});
// Mobile navigation
$('.mxch-mobile-nav-link').on('click', function(e) {
e.preventDefault();
const target = $(this).data('target');
$('.mxch-mobile-nav-link').removeClass('active');
$(this).addClass('active');
$('.mxch-section').removeClass('active');
$('#' + target).addClass('active');
$('.mxch-nav-link').removeClass('active');
$('.mxch-nav-link[data-target="' + target + '"]').addClass('active');
$('.mxch-mobile-menu').removeClass('open');
$('.mxch-mobile-overlay').removeClass('open');
});
// ==========================================================================
// AI Tools: autosave the function-calling toggle + per-tool checklist
// (matches MxChat's autosave UX — no Save button). plan a41dee.
// ==========================================================================
var $fc = $('#mxch-fc-settings');
if ($fc.length) {
var fcNonce = $fc.data('fc-nonce');
var $fcStatus = $('#mxch-fc-save-status');
var fcTimer = null;
function fcCollectAndSave() {
// Presence = active (plan d450a7): the global on/off toggle is gone, so
// the gate is derived from whether any tool is enabled. The server
// derives this too; we send it for an immediate, correct nav badge.
var enabled = $fc.find('input[name="mxchat_fc_tools[]"]:checked').length > 0 ? '1' : '0';
var allTools = $fc.find('input[name="mxchat_fc_all_tools[]"]').map(function () { return $(this).val(); }).get();
var checked = $fc.find('input[name="mxchat_fc_tools[]"]:checked').map(function () { return $(this).val(); }).get();
// Per-tool "when to use" hints, keyed by callback.
var hints = {};
$fc.find('.mxch-fc-hint-input').each(function () {
var cb = $(this).data('fc-callback');
if (cb) { hints[cb] = $(this).val(); }
});
if ($fcStatus.length) { $fcStatus.text('Saving…').removeClass('is-saved is-error'); }
$.post(mxchActionsData.ajaxUrl, {
action: 'mxchat_fc_autosave',
nonce: fcNonce,
enabled: enabled,
all_tools: allTools,
tools: checked,
hints: hints
}).done(function () {
if ($fcStatus.length) {
$fcStatus.text('Saved').addClass('is-saved');
setTimeout(function () { $fcStatus.text('').removeClass('is-saved'); }, 2000);
}
}).fail(function () {
if ($fcStatus.length) { $fcStatus.text('Save failed — try again').addClass('is-error'); }
});
}
// The hidden store's checkboxes are toggled programmatically by the
// list/detail/modal flow (which calls fcCollectAndSave() directly). Keep
// a change-bound autosave as a safety net for any direct mutation.
$fc.on('change', 'input[name="mxchat_fc_tools[]"]', function () {
clearTimeout(fcTimer);
fcTimer = setTimeout(fcCollectAndSave, 200);
});
// ======================================================================
// AI Tools MASTER-DETAIL list + detail panel (plan d450a7).
// Clones the Trigger Phrases list/detail UX: the left list shows the
// ACTIVE tools (a tool in the list = active — no global toggle, no
// per-tool enable checkbox); selecting one shows its detail on the right
// with Edit (usage note) + Remove. "Add" opens the same two-step
// capability picker. Pure view layer over the hidden #mxch-fc-tool-store
// inputs — the unchanged mxchat_fc_autosave contract (mxchat_fc_tools[] /
// mxchat_fc_all_tools[] + hints) stays the source of truth. FC-specific
// IDs/classes keep the Trigger Phrases JS off these elements.
// ======================================================================
var $fcStore = $('#mxch-fc-tool-store');
if ($fcStore.length) {
var $fcPanel = $('#mxch-fc-panel');
var $fcList = $('#mxch-fc-list');
var $fcCount = $('#mxch-fc-count');
var $fcEmpty = $('#mxch-fc-empty');
var $fcView = $('#mxch-fc-view');
var $fcDetail = $('#mxch-fc-detail-panel');
var $fcSearch = $('#mxch-fc-search');
var $fcModal = $('#mxch-fc-tool-modal');
var fcCurrentCb = null; // tool being added/edited in the modal
var fcViewCb = null; // tool currently shown in the detail panel
var fcSelected = new Set(); // callbacks ticked for bulk remove (plan 5f7409)
var fcI18n = {
tools: $fcPanel.data('i18n-tools') || 'tools',
tool: $fcPanel.data('i18n-tool') || 'tool',
active: $fcPanel.data('i18n-active') || 'Active',
addon: $fcPanel.data('i18n-addon') || 'Add-on',
sensitive: $fcPanel.data('i18n-sensitive') || 'Sensitive',
noHint: $fcPanel.data('i18n-nohint') || 'No usage note — the AI decides on its own when to use this.'
};
// Localized Add/Edit titles + button labels read from the modal DOM.
var $fcStep2Title = $('#mxch-fc-modal-step2-title');
var $fcSaveBtn = $('#mxch-fc-modal-save');
var FC_TITLE_ADD = $fcStep2Title.data('add-title') || 'Add Tool';
var FC_TITLE_EDIT = $fcStep2Title.data('edit-title') || 'Edit Tool';
var FC_BTN_ADD = $fcSaveBtn.data('add-label') || 'Add Tool';
var FC_BTN_SAVE = $fcSaveBtn.data('save-label') || 'Save Changes';
function fcEsc(s) { return $('
').text(s == null ? '' : String(s)).html(); }
function fcEntry(cb) {
return $fcStore.find('.mxch-fc-tool').filter(function () {
return String($(this).data('fc-callback')) === String(cb);
});
}
function fcIsOn($t) { return $t.find('input[name="mxchat_fc_tools[]"]').is(':checked'); }
function fcIsMobile() { return window.innerWidth <= 782; }
// Show the empty "Select a tool" state in the detail panel.
function fcShowEmpty() {
fcViewCb = null;
if ($fcView.length) $fcView.hide();
if ($fcEmpty.length) $fcEmpty.show();
$fcList.find('.mxch-fc-list-item').removeClass('active');
if (fcIsMobile()) { $fcDetail.removeClass('mobile-active'); $('body').removeClass('mxch-mobile-panel-open'); }
}
// Build the left list from the store (active tools only), applying the
// current search filter. Updates the count + resets the view if the
// shown tool was just removed.
function fcRenderList() {
if (!$fcList.length) return;
var q = $.trim(($fcSearch.val() || '')).toLowerCase();
$fcList.empty();
var total = 0;
$fcStore.find('.mxch-fc-tool').each(function () {
var $t = $(this);
if (!fcIsOn($t)) return;
total++;
var cb = $t.data('fc-callback');
var label = $t.data('fc-label') || cb;
var desc = $t.data('fc-desc') || '';
var icon = $t.data('fc-icon') || 'admin-generic';
var isAddon = String($t.data('fc-addon')) === '1';
var isCautious = String($t.data('fc-cautious')) === '1';
if (q && String(label).toLowerCase().indexOf(q) === -1 && String(desc).toLowerCase().indexOf(q) === -1) return;
var typeBits = [];
if (isAddon) typeBits.push(fcI18n.addon);
if (isCautious) typeBits.push(fcI18n.sensitive);
var typeLabel = typeBits.length ? typeBits.join(' · ') : fcI18n.active;
var activeCls = (String(cb) === String(fcViewCb)) ? ' active' : '';
var isSel = fcSelected.has(String(cb));
var selCls = isSel ? ' selected' : '';
$fcList.append(
'
' +
'' +
'
' +
'
' +
'
' + fcEsc(label) + '
' +
'
' + fcEsc(typeLabel) + '
' +
'
' +
'
' +
'' + fcEsc(fcI18n.active) + '' +
'
' +
'
'
);
});
if ($fcCount.length) {
$fcCount.text(total + ' ' + (total === 1 ? fcI18n.tool : fcI18n.tools));
}
// Drop any selected callbacks that are no longer in the list (e.g.
// filtered out or removed) so the bulk count stays truthful.
var fcPresent = {};
$fcList.find('.mxch-fc-list-item').each(function () {
fcPresent[String($(this).data('fc-callback'))] = true;
});
fcSelected.forEach(function (selCb) {
if (!fcPresent[String(selCb)]) { fcSelected.delete(selCb); }
});
fcUpdateSelectionUI();
// If the tool currently in the detail panel is no longer active, reset.
if (fcViewCb && !fcIsOn(fcEntry(fcViewCb))) { fcShowEmpty(); }
}
// Populate + show the detail view for one tool.
function fcSelectTool(cb) {
var $t = fcEntry(cb);
if (!$t.length || !fcIsOn($t)) { fcShowEmpty(); return; }
fcViewCb = String(cb);
var label = $t.data('fc-label') || cb;
var desc = $t.data('fc-desc') || '';
var icon = $t.data('fc-icon') || 'admin-generic';
var isAddon = String($t.data('fc-addon')) === '1';
var isCautious = String($t.data('fc-cautious')) === '1';
var hint = $.trim($t.find('.mxch-fc-hint-input').val() || '');
var typeBits = [];
if (isAddon) typeBits.push(fcI18n.addon);
if (isCautious) typeBits.push(fcI18n.sensitive);
$('#mxch-fc-view-icon').html('');
$('#mxch-fc-view-label').text(label);
$('#mxch-fc-view-type').text(typeBits.join(' · '));
$('#mxch-fc-view-desc').text(desc);
var $hint = $('#mxch-fc-view-hint');
if (hint) { $hint.text(hint).removeClass('is-muted'); }
else { $hint.text(fcI18n.noHint).addClass('is-muted'); }
// Setup requirement (plan 183856): show the tool's setup note (e.g. the
// Brave-key requirement on Web/Image Search) when present, else hide it.
var setup = $.trim(String($t.data('fc-setup') || ''));
var $setupWrap = $('#mxch-fc-view-setup-wrap');
if (setup) { $('#mxch-fc-view-setup').text(setup); $setupWrap.show(); }
else { $setupWrap.hide(); }
$fcEmpty.hide();
$fcView.show();
$fcList.find('.mxch-fc-list-item').removeClass('active')
.filter(function () { return String($(this).data('fc-callback')) === String(cb); }).addClass('active');
if (fcIsMobile()) { $fcDetail.addClass('mobile-active'); $('body').addClass('mxch-mobile-panel-open'); }
}
// ---- Add/Edit modal (two-step capability picker) ----
function fcShowStep(n) {
$fcModal.find('.mxch-fc-modal-step').hide();
$fcModal.find('.mxch-fc-modal-step[data-step="' + n + '"]').show();
}
function fcCloseModal() { $fcModal.hide(); fcCurrentCb = null; }
// Step 1: grid of tools not yet added.
function fcOpenAddModal() {
var $grid = $('#mxch-fc-modal-grid').empty();
var addable = 0;
$fcStore.find('.mxch-fc-tool').each(function () {
var $t = $(this);
if (fcIsOn($t)) return; // already active
addable++;
var cb = $t.data('fc-callback');
var label = $t.data('fc-label') || cb;
var desc = $t.data('fc-desc') || '';
var icon = $t.data('fc-icon') || 'admin-generic';
var isAddon = String($t.data('fc-addon')) === '1';
var isCautious = String($t.data('fc-cautious')) === '1';
var badges = '';
if (isAddon) { badges += ' ' + fcEsc(fcI18n.addon) + ''; }
if (isCautious) { badges += ' ' + fcEsc(fcI18n.sensitive) + ''; }
$grid.append(
'
' +
'
' +
'
' +
'
' + fcEsc(label) + badges + '
' +
'
' + fcEsc(desc) + '
' +
'
' +
'
'
);
});
$('#mxch-fc-modal-allset').toggle(addable === 0);
$grid.toggle(addable > 0);
fcShowStep(1);
$fcModal.css('display', 'flex');
}
// Step 2: confirm + when-to-use (shared by Add and Edit).
function fcOpenConfigStep(cb) {
var $t = fcEntry(cb);
if (!$t.length) return;
fcCurrentCb = cb;
var editing = fcIsOn($t);
$('#mxch-fc-modal-selected-icon').html('');
$('#mxch-fc-modal-selected-label').text($t.data('fc-label') || cb);
$('#mxch-fc-modal-selected-desc').text($t.data('fc-desc') || '');
$('#mxch-fc-modal-hint').val($t.find('.mxch-fc-hint-input').val() || '');
$('#mxch-fc-modal-sensitive').toggle(String($t.data('fc-cautious')) === '1');
$fcStep2Title.text(editing ? FC_TITLE_EDIT : FC_TITLE_ADD);
$fcSaveBtn.text(editing ? FC_BTN_SAVE : FC_BTN_ADD);
fcShowStep(2);
$fcModal.css('display', 'flex');
}
// ---- wiring ----
// "Add" buttons (panel header + empty state) — delegated on $fc.
$fc.on('click', '.js-mxch-fc-add', fcOpenAddModal);
$fcModal.on('click', '.mxch-fc-type-card', function () { fcOpenConfigStep($(this).data('fc-callback')); });
$('#mxch-fc-modal-back').on('click', fcOpenAddModal);
$fcModal.on('click', '[data-fc-modal-close]', fcCloseModal);
$fcModal.on('click', function (e) { if (e.target === this) fcCloseModal(); }); // backdrop
$(document).on('keydown', function (e) { if (e.key === 'Escape' && $fcModal.is(':visible')) fcCloseModal(); });
// Modal Save (Add or Edit): write the store inputs, autosave, refresh
// the list, and show the tool's detail on the right.
$fcSaveBtn.on('click', function () {
if (!fcCurrentCb) return;
var $t = fcEntry(fcCurrentCb);
if (!$t.length) return;
var cb = fcCurrentCb;
$t.find('.mxch-fc-hint-input').val($('#mxch-fc-modal-hint').val() || '');
$t.find('input[name="mxchat_fc_tools[]"]').prop('checked', true);
fcCloseModal();
fcRenderList();
fcSelectTool(cb);
fcCollectAndSave();
});
// List item click → show that tool's detail.
$fcList.on('click', '.mxch-fc-list-item', function () {
fcSelectTool($(this).data('fc-callback'));
});
// Detail Edit → reopen the config step for the viewed tool.
$('#mxch-fc-edit-btn').on('click', function () {
if (fcViewCb) fcOpenConfigStep(fcViewCb);
});
// Detail Remove → disable the tool (keep its hint for a later re-add),
// refresh the list, and return to the empty state.
$('#mxch-fc-remove-btn').on('click', function () {
if (!fcViewCb) return;
var $t = fcEntry(fcViewCb);
if (!$t.length) return;
$t.find('input[name="mxchat_fc_tools[]"]').prop('checked', false);
fcShowEmpty();
fcRenderList();
fcCollectAndSave();
});
// ---- Bulk selection + bulk remove ----
// Clones the Trigger Phrases #all-actions bulk toolbar behavior, but
// wires removal to the SAME path the single Remove button uses (uncheck
// each tool's store checkbox, then one autosave). FC-specific IDs keep
// this isolated from the Trigger Phrases bulk JS (plan 5f7409).
var $fcSelectAll = $('#mxch-fc-select-all');
var $fcBulkDelete = $('#mxch-fc-delete-selected');
var $fcSelCount = $('#mxch-fc-selected-count');
function fcUpdateSelectionUI() {
var count = fcSelected.size;
if (count > 0) {
$fcSelCount.text(count + ' selected').addClass('has-selection');
$fcBulkDelete.prop('disabled', false);
$fcList.addClass('selection-mode');
} else {
$fcSelCount.removeClass('has-selection');
$fcBulkDelete.prop('disabled', true);
$fcList.removeClass('selection-mode');
}
var totalItems = $fcList.find('.mxch-fc-checkbox').length;
var checkedItems = $fcList.find('.mxch-fc-checkbox:checked').length;
$fcSelectAll.prop('checked', totalItems > 0 && checkedItems === totalItems);
$fcSelectAll.prop('indeterminate', checkedItems > 0 && checkedItems < totalItems);
}
// Select-all toggles every visible tool row.
$fcSelectAll.on('change', function () {
var on = $(this).is(':checked');
fcSelected.clear();
$fcList.find('.mxch-fc-list-item').each(function () {
var $row = $(this);
var cb = String($row.data('fc-callback'));
$row.find('.mxch-fc-checkbox').prop('checked', on);
if (on) { fcSelected.add(cb); $row.addClass('selected'); }
else { $row.removeClass('selected'); }
});
fcUpdateSelectionUI();
});
// Per-row checkbox: toggle selection without opening the detail view.
$fcList.on('click', '.mxch-fc-checkbox', function (e) {
e.stopPropagation();
var $row = $(this).closest('.mxch-fc-list-item');
var cb = String($row.data('fc-callback'));
if ($(this).is(':checked')) { fcSelected.add(cb); $row.addClass('selected'); }
else { fcSelected.delete(cb); $row.removeClass('selected'); }
fcUpdateSelectionUI();
});
// Bulk remove: same removal path as single Remove — uncheck each tool's
// store checkbox (presence = active), then refresh + autosave once.
$fcBulkDelete.on('click', function () {
if (!fcSelected.size) return;
var msg = (window.mxchActionsData && mxchActionsData.i18n && mxchActionsData.i18n.confirmBulkDelete)
|| 'Remove the selected tools? They stay available to add back later.';
if (!window.confirm(msg)) return;
var removingViewed = false;
fcSelected.forEach(function (cb) {
var $t = fcEntry(cb);
if ($t.length) { $t.find('input[name="mxchat_fc_tools[]"]').prop('checked', false); }
if (String(cb) === String(fcViewCb)) { removingViewed = true; }
});
fcSelected.clear();
if (removingViewed) { fcShowEmpty(); }
fcRenderList();
fcCollectAndSave();
});
// Search filter.
$fcSearch.on('input', function () { fcRenderList(); });
// Refresh — re-render the list from the store (parity with the Trigger
// Phrases refresh affordance; the list is client-built so this re-syncs
// the view rather than re-fetching).
$('#mxch-fc-refresh').on('click', function () {
var $btn = $(this);
$btn.addClass('spinning');
fcRenderList();
setTimeout(function () { $btn.removeClass('spinning'); }, 500);
});
fcRenderList();
}
}
// Quick action buttons
$('.mxch-quick-action-btn[data-action="view-actions"]').on('click', function() {
$('.mxch-nav-link[data-target="all-actions"]').trigger('click');
});
// Dashboard explainer cards — generic "jump to this section" (e.g. Set up AI Tools).
$('.mxch-approach-btn[data-approach-target]').on('click', function() {
var target = $(this).data('approach-target');
$('.mxch-nav-link[data-target="' + target + '"]').trigger('click');
});
// "Add Trigger Phrase" on the dashboard → jump to Trigger Phrases + open the editor.
$('#mxch-add-action-dashboard-btn').on('click', function() {
$('.mxch-nav-link[data-target="all-actions"]').trigger('click');
setTimeout(function() {
openActionEditor();
}, 100);
});
// ==========================================================================
// Mobile Detection & Panel Management
// ==========================================================================
function isMobile() {
return window.innerWidth <= 782;
}
function showMobileDetailPanel() {
if (isMobile()) {
$('.mxch-action-detail-panel').addClass('mobile-active');
$('body').addClass('mxch-mobile-panel-open');
}
}
function hideMobileDetailPanel() {
$('.mxch-action-detail-panel').removeClass('mobile-active');
$('body').removeClass('mxch-mobile-panel-open');
}
// Mobile back button handler
$(document).on('click', '.mxch-mobile-back-btn', function(e) {
e.preventDefault();
hideMobileDetailPanel();
resetEditorPanel();
$('.mxch-action-item').removeClass('active');
currentActionId = null;
});
// ==========================================================================
// Action List - Split Panel
// ==========================================================================
let currentPage = 1;
const perPage = 50;
let totalPages = 1;
let currentActionId = null;
let actionsLoaded = false;
let selectedActions = new Set();
let currentSortOrder = 'desc';
let currentFilter = '';
// ==========================================================================
// Phrase Tag Management
// ==========================================================================
let currentPhrases = []; // Array of {id, text, isLegacy}
function renderPhraseTags() {
const $container = $('#mxch-phrase-tags');
let html = '';
currentPhrases.forEach(function(phrase, index) {
const legacyClass = phrase.isLegacy ? ' mxch-phrase-legacy' : '';
const badge = phrase.isLegacy ? ' legacy' : '';
html += ''
+ escapeHtml(phrase.text) + badge
+ ' '
+ '';
});
$container.html(html);
}
function addPhraseFromInput() {
const $input = $('#mxch-phrase-input');
const text = $input.val().trim();
if (!text) return;
const formMode = $('#mxch-form-mode').val();
const intentId = $('#mxch-action-id').val();
if (formMode === 'edit' && intentId) {
// Edit mode: AJAX add immediately
const $btn = $('#mxch-add-phrase-btn');
$btn.prop('disabled', true).text('Adding...');
$.ajax({
url: mxchActionsData.ajaxUrl,
type: 'POST',
data: {
action: 'mxchat_add_phrase',
intent_id: intentId,
phrase: text,
security: mxchActionsData.addPhraseNonce
},
success: function(response) {
$btn.prop('disabled', false).text('Add');
if (response.success) {
currentPhrases.push({id: response.data.id, text: text, isLegacy: false});
renderPhraseTags();
$input.val('').focus();
} else {
alert(response.data || 'Failed to add phrase.');
}
},
error: function() {
$btn.prop('disabled', false).text('Add');
alert('Failed to add phrase. Please try again.');
}
});
} else {
// Add mode: just push locally
currentPhrases.push({id: null, text: text, isLegacy: false});
renderPhraseTags();
$input.val('').focus();
}
}
// Add phrase on Enter key
$(document).on('keydown', '#mxch-phrase-input', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
addPhraseFromInput();
}
});
// Add phrase on button click
$(document).on('click', '#mxch-add-phrase-btn', function() {
addPhraseFromInput();
});
// Remove phrase on X click
$(document).on('click', '.mxch-phrase-remove', function(e) {
e.stopPropagation();
const $pill = $(this).closest('.mxch-phrase-pill');
const index = parseInt($pill.data('index'));
const phrase = currentPhrases[index];
if (!phrase) return;
if (phrase.isLegacy) {
// Delete legacy phrases from main table
const intentId = $('#mxch-action-id').val();
if (!intentId) {
currentPhrases.splice(index, 1);
renderPhraseTags();
return;
}
if (!confirm('Remove all legacy grouped phrases? You can re-add them individually for better matching.')) return;
$.ajax({
url: mxchActionsData.ajaxUrl,
type: 'POST',
data: {
action: 'mxchat_delete_legacy_phrases',
intent_id: intentId,
security: mxchActionsData.deleteLegacyNonce
},
success: function(response) {
if (response.success) {
currentPhrases.splice(index, 1);
renderPhraseTags();
} else {
alert(response.data || 'Failed to remove legacy phrases.');
}
}
});
} else if (phrase.id) {
// Delete individual phrase via AJAX
$.ajax({
url: mxchActionsData.ajaxUrl,
type: 'POST',
data: {
action: 'mxchat_delete_phrase',
phrase_id: phrase.id,
security: mxchActionsData.deletePhraseNonce
},
success: function(response) {
if (response.success) {
currentPhrases.splice(index, 1);
renderPhraseTags();
} else {
alert(response.data || 'Failed to delete phrase.');
}
}
});
} else {
// Local-only phrase (add mode, not yet saved)
currentPhrases.splice(index, 1);
renderPhraseTags();
}
});
function fetchIndividualPhrases(intentId, callback) {
$.ajax({
url: mxchActionsData.ajaxUrl,
type: 'POST',
data: {
action: 'mxchat_get_phrases',
intent_id: intentId,
security: mxchActionsData.getPhrasesNonce
},
success: function(response) {
if (response.success) {
callback(response.data.phrases || []);
} else {
callback([]);
}
},
error: function() {
callback([]);
}
});
}
// Load action list on page load
loadActionList(1, '', '');
// Search functionality with debounce
let searchTimeout;
$('#mxch-search-actions').on('input', function() {
clearTimeout(searchTimeout);
const searchTerm = $(this).val().toLowerCase();
searchTimeout = setTimeout(function() {
currentPage = 1;
loadActionList(currentPage, searchTerm, currentFilter);
}, 300);
});
// Filter by type
$('#mxch-filter-actions').on('change', function() {
currentFilter = $(this).val();
currentPage = 1;
loadActionList(currentPage, $('#mxch-search-actions').val(), currentFilter);
});
// Refresh button
$('#mxch-refresh-actions').on('click', function() {
const $btn = $(this);
$btn.addClass('spinning');
loadActionList(currentPage, $('#mxch-search-actions').val(), currentFilter);
setTimeout(() => $btn.removeClass('spinning'), 500);
});
// ==========================================================================
// Bulk Selection & Actions
// ==========================================================================
// Select all checkbox
$('#mxch-select-all-actions').on('change', function() {
const isChecked = $(this).is(':checked');
$('.mxch-action-checkbox').prop('checked', isChecked);
if (isChecked) {
$('.mxch-action-item').each(function() {
selectedActions.add($(this).data('action-id'));
$(this).addClass('selected');
});
$('#mxch-action-list').addClass('selection-mode');
} else {
selectedActions.clear();
$('.mxch-action-item').removeClass('selected');
$('#mxch-action-list').removeClass('selection-mode');
}
updateSelectionUI();
});
// Update selection UI
function updateSelectionUI() {
const count = selectedActions.size;
const $countEl = $('#mxch-selected-action-count');
const $deleteBtn = $('#mxch-delete-selected-actions');
if (count > 0) {
$countEl.text(count + ' selected').addClass('has-selection');
$deleteBtn.prop('disabled', false);
$('#mxch-action-list').addClass('selection-mode');
} else {
$countEl.removeClass('has-selection');
$deleteBtn.prop('disabled', true);
$('#mxch-action-list').removeClass('selection-mode');
}
// Update select all checkbox state
const totalItems = $('.mxch-action-checkbox').length;
const checkedItems = $('.mxch-action-checkbox:checked').length;
$('#mxch-select-all-actions').prop('checked', totalItems > 0 && checkedItems === totalItems);
$('#mxch-select-all-actions').prop('indeterminate', checkedItems > 0 && checkedItems < totalItems);
}
// Delete selected button
$('#mxch-delete-selected-actions').on('click', function() {
const count = selectedActions.size;
if (count === 0) return;
if (!confirm(mxchActionsData.i18n.confirmBulkDelete)) {
return;
}
deleteMultipleActions(Array.from(selectedActions));
});
// Delete multiple actions
function deleteMultipleActions(actionIds) {
showLoading();
$.ajax({
url: mxchActionsData.ajaxUrl,
type: 'POST',
data: {
action: 'mxchat_bulk_delete_actions',
action_ids: actionIds,
security: mxchActionsData.deleteNonce
},
success: function(response) {
hideLoading();
if (response.success) {
// Clear selection
selectedActions.clear();
$('#mxch-select-all-actions').prop('checked', false);
updateSelectionUI();
// If current action was deleted, reset panel
if (actionIds.includes(currentActionId)) {
currentActionId = null;
resetEditorPanel();
}
// Reload list
loadActionList(currentPage, $('#mxch-search-actions').val(), currentFilter);
} else {
alert(response.data || mxchActionsData.i18n.error);
}
},
error: function() {
hideLoading();
alert(mxchActionsData.i18n.error);
}
});
}
// Load action list function
function loadActionList(page, searchTerm, filter) {
const $container = $('#mxch-action-list');
$container.html('