Skip to content
201 changes: 127 additions & 74 deletions assets/src/js/admin-dashboard/segments/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -715,104 +715,157 @@ document.addEventListener('DOMContentLoaded', function () {
}

/**
* Toggle turn-off confirmation modals.
* Option change and turn-off confirmation modals.
*
* Intercepts the change event on tutor-form-toggle-input elements that are
* configured in the localized `tutorTurnoffConfirm` map (keyed by field key).
* When such a toggle is turned OFF, the handler reverts the toggle, shows a
* confirm modal, and only proceeds with the turn-off if the user confirms.
* Intercepts user interactions on options configured in the localized
* `tutorOptionConfirmations` map (keyed by field key).
*
* The config can include a usage-check AJAX action to decide whether the
* modal needs to be shown at all.
* Supports:
* - 'turnoff': When a toggle switch is turned OFF.
* - 'change': When a field value (e.g. select dropdown) changes.
*
* The map is localized by Tutor Pro; Free only provides this generic,
* configuration-driven mechanism.
*
* @since 4.1.0
*/
const turnOffConfirmations = window.tutorTurnoffConfirm || {};
Object.entries(turnOffConfirmations).forEach(([fieldKey, config]) => {
document.querySelectorAll(`#field_${fieldKey} .tutor-form-toggle-input`).forEach((checkbox) => {
checkbox.addEventListener('change', function (e) {
if (this.checked) {
return;
}

const message = config.message;
const title = config.title;
const cancelText = config.cancel;
const confirmText = config.confirm;
const usageAjaxAction = config.usage_check_action;
if (!message) {
return;
}
const optionConfirmations = window.tutorOptionConfirmations || {};
Object.entries(optionConfirmations).forEach(([fieldKey, config]) => {
const message = config.message;
const title = config.title;
const cancelText = config.cancel;
const confirmText = config.confirm;
const usageAjaxAction = config.usage_check_action;
const confirmationType = config.type || 'turnoff';

if (!message) {
return;
}

const hiddenInput = this.previousElementSibling;
const syncToggleVisibility = () => {
const $toggle = $(this);
if ($toggle.data('toggle-fields')) {
showHideToggleChildren($toggle);
}
if ($toggle.data('toggle-blocks')) {
showHideToggleBlock($toggle);
if (confirmationType === 'turnoff') {
document.querySelectorAll(`#field_${fieldKey} .tutor-form-toggle-input`).forEach((checkbox) => {
checkbox.addEventListener('change', function (e) {
if (this.checked) {
return;
}
};

const revertToggle = () => {
this.checked = true;
if (hiddenInput) {
hiddenInput.value = 'on';
}
syncToggleVisibility();
};
const hiddenInput = this.previousElementSibling;
const syncToggleVisibility = () => {
const $toggle = $(this);
if ($toggle.data('toggle-fields')) {
showHideToggleChildren($toggle);
}
if ($toggle.data('toggle-blocks')) {
showHideToggleBlock($toggle);
}
};

const proceedWithTurnoff = () => {
this.checked = false;
if (hiddenInput) {
hiddenInput.value = 'off';
}
syncToggleVisibility();
};
const revertToggle = () => {
this.checked = true;
if (hiddenInput) {
hiddenInput.value = 'on';
}
syncToggleVisibility();
};

if (!usageAjaxAction) {
revertToggle();
tutorConfirmTurnoffModal(message, title, cancelText, confirmText).then((confirmed) => {
if (confirmed) {
proceedWithTurnoff();
const proceedWithTurnoff = () => {
this.checked = false;
if (hiddenInput) {
hiddenInput.value = 'off';
}
});
return;
}
syncToggleVisibility();
};

const confirmAndTurnoff = () => {
revertToggle();
tutorConfirmOptionModal(message, title, cancelText, confirmText).then((confirmed) => {
if (confirmed) {
proceedWithTurnoff();
}
});
};

const formData = new FormData();
formData.append('action', usageAjaxAction);
formData.append(_tutorobject.nonce_key, _tutorobject._tutor_nonce);
if (!usageAjaxAction) {
confirmAndTurnoff();
return;
}

fetch(_tutorobject.ajaxurl, { method: 'POST', body: formData })
.then((response) => response.json())
.then((result) => {
const hasCustomized = result?.data?.has_customized;
if (hasCustomized) {
const formData = new FormData();
formData.append('action', usageAjaxAction);
formData.append(_tutorobject.nonce_key, _tutorobject._tutor_nonce);

fetch(_tutorobject.ajaxurl, { method: 'POST', body: formData })
.then((response) => response.json())
.then((result) => {
const hasCustomized = result?.data?.has_customized;
if (hasCustomized) {
confirmAndTurnoff();
} else {
proceedWithTurnoff();
}
})
.catch(() => {
revertToggle();
tutorConfirmTurnoffModal(message, title, cancelText, confirmText).then((confirmed) => {
if (confirmed) {
proceedWithTurnoff();
});
});
});
} else if (confirmationType === 'change') {
document.querySelectorAll(`#field_${fieldKey} select`).forEach((selectElement) => {
let previousValue = selectElement.value;

selectElement.addEventListener('change', function () {
const newValue = this.value;
if (newValue === previousValue) {
return;
}

const saveBtn = document.getElementById('save_tutor_option');
const wasSaveDisabled = saveBtn ? saveBtn.disabled : false;

const revertSelect = () => {
this.value = previousValue;

const customSelect = this.nextElementSibling;
if (customSelect && customSelect.classList.contains('tutor-js-form-select')) {
const selectLabel = customSelect.querySelector('.tutor-form-select-label');
const selectedOption = Array.from(this.options).find((opt) => opt.value === previousValue);
if (selectLabel && selectedOption) {
selectLabel.innerText = selectedOption.text;
selectLabel.dataset.value = previousValue;
}
const optionsWrap = customSelect.querySelector('.tutor-form-select-options');
if (optionsWrap) {
optionsWrap.querySelector('.is-active')?.classList.remove('is-active');
const prevItem = optionsWrap.querySelector(`[data-key="${previousValue}"]`);
if (prevItem) {
prevItem.classList.add('is-active');
}
});
}
}

if (saveBtn) {
saveBtn.disabled = wasSaveDisabled;
}
};

tutorConfirmOptionModal(message, title, cancelText, confirmText).then((confirmed) => {
if (confirmed) {
previousValue = newValue;
if (saveBtn) {
saveBtn.disabled = false;
}
} else {
proceedWithTurnoff();
revertSelect();
}
})
.catch(() => {
revertToggle();
});
});
});
});
}
});
});

/**
* Show a confirmation modal for toggle turn-off.
* Show a confirmation modal for option changes or turn-offs.
*
* @since 4.1.0
*
Expand All @@ -822,7 +875,7 @@ document.addEventListener('DOMContentLoaded', function () {
* @param {string} [confirmText] Optional confirm button label.
* @return {Promise<boolean>} Resolves true if confirmed, false if cancelled.
*/
function tutorConfirmTurnoffModal(message, title, cancelText, confirmText) {
function tutorConfirmOptionModal(message, title, cancelText, confirmText) {
const { __ } = wp.i18n;

return new Promise((resolve) => {
Expand All @@ -839,7 +892,7 @@ function tutorConfirmTurnoffModal(message, title, cancelText, confirmText) {
};

popup = new window.tutor_popup(window.jQuery, '').popup({
title: title || __('Turn off setting?', 'tutor'),
title: title || __('Confirm setting?', 'tutor'),
description: message,
buttons: {
cancel: {
Expand Down
Loading
Loading