(function($) { const { ssa, post, ssa_membership_product_settings, translations, interval_types, constants } = ssaMeprMembershipOptions; var selectedAppointmentTypes = {} // Get included appointment types if( ssa_membership_product_settings?.[ constants.product_settings_str ] && Object.keys(ssa_membership_product_settings[ constants.product_settings_str ]).length !== 0 ) { selectedAppointmentTypes = ssa_membership_product_settings[ constants.product_settings_str ]; } // Cache frequently accessed DOM elements: const $includeApptsChechbox = $('#ssa-mepr-include-appointments'); const $appointmentsBlock = $('#ssa-mepr-appointments-block'); const $helperText = $('#ssa-memberpress-helperText'); const $actionBtn = $('#ssa-mepr-action-btn'); const $tableContent = $('#ssa-mepr-appointment-type-table-content'); const $dropdown = $('#ssa_apptTypesDropdown'); const $offerNote = $('#ssa-mepr-note-offers'); const $setupNote = $('#ssa-mepr-note-not-setup'); const $tableContainer = $('#ssa-mepr-appointment-type-table'); // Mepr fields const $meprProductForm = $('#mepr-products-form'); const $meprProductBillingTypeSelect = $('#mepr-product-billing-type'); const $meprProductTrialCheckbox = $('#_mepr_product_trial'); // Interval types const intervalTypesValues = Object.keys(interval_types); // Add eventListener for the initial checkbox $includeApptsChechbox.on('change', function() { // Toggle the entire block $appointmentsBlock.prop('aria-hidden', !this.checked).toggle(this.checked); // Toggle the helper text $helperText.prop('aria-hidden', this.checked).toggle(!this.checked); }); // Add eventListener on the `Add` appointment type button $actionBtn.on('click', function($event) { $event.preventDefault(); addAppointmentType(); }); // Add eventListener on the remove `X` buttons $tableContent.on('click', '.ssa-mepr-remove-appointment-types', function($event, el) { $event.preventDefault(); removeAppointmentType(this); }); // Add eventListener on intervals $tableContent.on('change', '.ssa-mepr-interval' , function() { var interval = $(this).val(); var id = $(this).data('id'); selectedAppointmentTypes[id]['per_interval'] = parseInt(interval); }); // Add eventListener on 'Interval Type' changes $tableContent.on('change', '.ssa-mepr-interval-type' , function() { var intervalType = $(this).val(); var id = $(this).data('id'); // Ensure that the appointment type exists in selectedAppointmentTypes if (selectedAppointmentTypes[id]) { selectedAppointmentTypes[id][ constants.interval_type_str ] = intervalType; } }); // Add eventListener on the booking limit per cycle checkbox $tableContent.on('change', '.ssa-booking-limit-checkbox' , function() { var id = $(this).data('id'); var isChecked = $(this).is(':checked'); selectedAppointmentTypes[id]['limit_booking_per_cycle'] = $(this).is(':checked'); // Get the corresponding booking limit input var $bookingLimitInput = $(`#ssa-booking-limit-input-${id}`); if (isChecked) { // Enable the input field $bookingLimitInput.prop('disabled', false); $bookingLimitInput.prop('required', true); // Update max_num_of_appointments_per_cycle with current input value selectedAppointmentTypes[id]['max_num_of_appointments_per_cycle'] = parseInt($bookingLimitInput.val()); } else { // Disable the input field $bookingLimitInput.prop('disabled', true); $bookingLimitInput.prop('required', false); } }); // Add eventListener on allow booking during trials $tableContent.on('change', '.ssa-booking-trial-period-checkbox' , function() { var id = $(this).data('id'); var isChecked = $(this).is(':checked'); selectedAppointmentTypes[id]['allow_booking_during_trials'] = $(this).is(':checked'); // Get the corresponding limit input var $bookingLimitInput = $(`#ssa-booking-trial-period-input-${id}`); if (isChecked) { // Enable the input field $bookingLimitInput.prop('disabled', false); $bookingLimitInput.prop('required', true); // Update max_num_of_appointments_during_trials with current input value selectedAppointmentTypes[id]['max_num_of_appointments_during_trials'] = parseInt($bookingLimitInput.val()); } else { // Disable the input field $bookingLimitInput.prop('disabled', true); $bookingLimitInput.prop('required', false); } }); // Add eventListener on the booking limit per cycle input field $tableContent.on('change', '.ssa-booking-limit-input' , function() { var limit = $(this).val(); var id = $(this).data('id'); selectedAppointmentTypes[id]['max_num_of_appointments_per_cycle'] = parseInt(limit); }); // Add eventListener on the booking limit during trials input field $tableContent.on('change', '.ssa-booking-trial-period-input' , function() { var limit = $(this).val(); var id = $(this).data('id'); selectedAppointmentTypes[id]['max_num_of_appointments_during_trials'] = parseInt(limit); }); // Mepr: Add eventListener on the billing type; could be either recurring or single var billingType = $meprProductBillingTypeSelect.val(); var trialCheckbox = $meprProductTrialCheckbox?.is(':checked'); $meprProductForm.on('change', '#mepr-product-billing-type' , function() { if(!billingType || billingType !== $(this).val()) { billingType = $(this).val(); render(); } }); // Mepr: Add eventListener on the trial checkbox could be either checked or unchecked $meprProductForm.on('change', '#_mepr_product_trial' , function() { let isChecked = $(this).is(':checked'); if(!trialCheckbox || trialCheckbox !== isChecked) { trialCheckbox = isChecked; render(); } }); function isRecurringProduct() { return billingType && billingType === 'recurring'; } function isTrialChecked() { return trialCheckbox && trialCheckbox === true; } function shouldDisplayTrial() { return isRecurringProduct() && isTrialChecked(); } function removeAppointmentType(el){ var id = $(el).data('id'); var $option = $(`#ssa_apptTypesDropdown option[value="${id}"]`); // Enable the option in the dropdown menu $option.prop('disabled', false).prop('aria-disabled', false); // Set the removed option as selected in dropdown $dropdown.val($option.val()); selectedAppointmentTypes[id]['active'] = 0; render(); } function addAppointmentType() { // Get selected appointment type id var appointment_type_id = $dropdown.val(); // Get selected appointment type title var title = $('#ssa_apptTypesDropdown option:selected').data('title'); // Disable from the dropdown menu & set the first enabled option as selected in dropdown $(`#ssa_apptTypesDropdown option[value="${appointment_type_id}"]`).prop('disabled', true).prop('aria-disabled', true); var $firstEnabledOption = $('#ssa_apptTypesDropdown option:first'); while ($firstEnabledOption.prop('disabled')) { $firstEnabledOption = $firstEnabledOption.next(); } $dropdown.val($firstEnabledOption.val()); // check if the appointment type was added before and is set to inactive if (Object.prototype.hasOwnProperty.call(selectedAppointmentTypes, appointment_type_id )) { selectedAppointmentTypes[appointment_type_id]['active'] = 1; } else { // This appointment has not been added before Object.assign(selectedAppointmentTypes, { [appointment_type_id]: { title, active: 1, [constants.per_interval_str]: 1, [constants.interval_type_str]: intervalTypesValues[0], [constants.limit_per_cycle_str]: false, [constants.max_per_cycle_str]: 1, [constants.allow_trial_str]: false, [constants.max_per_trial_str]: 1, } }); } render(); } function render() { let allIncludedCount; let activeTypesCount; if(Object.keys(selectedAppointmentTypes)) { allIncludedCount = Object.keys(selectedAppointmentTypes).length } if(allIncludedCount) { let appointmentTypesConfig = Object.values(selectedAppointmentTypes); activeTypesCount = appointmentTypesConfig.filter((item) => !!item.active).length; } if(activeTypesCount === 0){ // Empty selected appointment types case $offerNote.hide(); $setupNote.show(); // Hide foxy $tableContainer.hide(); return; } // If we just added make sure to display the entire block if(activeTypesCount > 0){ $tableContainer.show(); // Change the subtitle that says no appointment are set up yet $offerNote.show(); $setupNote.hide(); } // Empty the table content before render $tableContent.empty(); let index = 0; for (const id in selectedAppointmentTypes) { if(!selectedAppointmentTypes[id]['active']) { continue; } index++; addAppointmentTypeTableBlock(index, id, selectedAppointmentTypes[id]); } } function addAppointmentTypeTableBlock (index, id, settings) { let myElm = document.createElement("div"); myElm.style.display = "flex"; myElm.classList.add("ssa-mepr-flex", "ssa-mepr-appointmnet-details"); var intervalTypesOptions = intervalTypesValues.map(function(type){ return ``; }).join(""); myElm.innerHTML = `
${index}
${translations['appointment_type']} ${settings['title']}
${pointer('number_of_interval_'+id, translations.tooltips.intervals.title, translations.tooltips.intervals.text)}
${pointer('limit_booking_per_cycle_' + id, translations.tooltips.limit_per_cycle.title, translations.tooltips.limit_per_cycle.text)}
${pointer('allow_booking_during_trials_' + id, translations.tooltips.trial_booking.title, translations.tooltips.trial_booking.text)}
`; $tableContent.append(myElm) } function pointer(id, title, text) { return ` ${title} ${text} `; } // Collect data before saving function collectData(){ var isMembershipEnabled = $includeApptsChechbox.prop('checked'); return { isMembershipEnabled, productSettings: selectedAppointmentTypes } } // Function to check if a value is a positive integer function isValidPositiveInteger(value) { return Number.isInteger(value) && value > 0; } function validateProductSettings(collectedData) { var productSettings = collectedData.productSettings; // Loop over each key in productSettings for (var key in productSettings) { var settings = productSettings[key]; // Check if the values are valid positive integers if ( (!isValidPositiveInteger(settings.max_num_of_appointments_during_trials) && settings.allow_booking_during_trials) || (!isValidPositiveInteger(settings.max_num_of_appointments_per_cycle) && settings.limit_booking_per_cycle) || !isValidPositiveInteger(settings.per_interval) ) { return false; } } return true; } // Saving the data on submit: $('form').on( "submit", function(e) { var form = $(this); // Double check the form includes ssa-mepr inputs const appointmentsInputField = form.find('#ssa-mepr-include-appointments'); if (appointmentsInputField.length === 0) { return; } var form_id = form.attr('id'); if (form_id !== 'post') { return; } var post_type = $('#post_type').val(); var post_id = $('#post_ID').val(); if (post_type !== 'memberpressproduct' || post_id.toString() !== post['ID'].toString() ) { return; } var collectedData = collectData(); if (!validateProductSettings(collectedData)) { alert(translations.inputFieldsErrorMsg); e.preventDefault(); // Prevent form submission return; } $.ajax({ url: ssa.api.root + '/memberpress/' + post['ID'], method: 'POST', dataType: 'json', contentType: 'application/json', data: JSON.stringify(collectedData), beforeSend: (xhr) => { xhr.setRequestHeader('X-WP-Nonce', ssa.api.nonce) } }) .done(() => { }) .fail((error) => { console.log(error) }) }); // On load render(); })(jQuery);