$(document).ready(function() { /** * Global AJAX Form Handler * Intercepts forms with class .ajax-form and handles them via AJAX. */ $(document).on('submit', 'form.ajax-form', function(e) { e.preventDefault(); const $form = $(this); const $submitBtn = $form.find('button[type="submit"]'); const originalBtnHtml = $submitBtn.html(); const actionUrl = $form.attr('action'); const method = $form.attr('method') || 'POST'; // CKEditor 5 Sync $form.find('textarea').each(function() { if (this.ckeditorInstance) { this.ckeditorInstance.updateSourceElement(); } }); // 1. Loading state $submitBtn.prop('disabled', true).html(' ' + ( $submitBtn.data('loading-text') || 'Saving...' )); // 2. Perform Request const formData = new FormData(this); // Allow page-specific code to modify formData before sending // (e.g. appending FilePond files) $form.trigger('ajaxForm:beforeSend', [formData]); $.ajax({ url: actionUrl, method: method, data: formData, processData: false, contentType: false, dataType: 'json', headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }, success: function(response) { // Notif (Allow skipping global alert) if ($form.data('alert') !== false) { const swalInstance = window.StandardSwal || Swal; swalInstance.fire({ icon: 'success', title: 'Success!', text: response.message || 'Data saved successfully.', timer: 1500, showConfirmButton: false }); } // 3. UI Updates // Close active modal if exists const $modal = $form.closest('.modal'); if ($modal.length) { const modalInstance = bootstrap.Modal.getInstance($modal[0]); if (modalInstance) { modalInstance.hide(); } else { // Fallback jQuery hide if instance not found $($modal[0]).modal('hide'); } // Robust Cleanup: Ensure backdrop is removed and scrolling is restored // This fixes the common Bootstrap bug where backdrop stays stuck setTimeout(() => { $('.modal-backdrop').remove(); $('body').removeClass('modal-open').css({ 'overflow': '', 'padding-right': '' }); }, 400); } // Refresh DataTable if window.reloadDataTable is defined if (typeof window.reloadDataTable === 'function') { window.reloadDataTable(); } // Reset form if it's not an update form (optional based on data-reset attribute) if ($form.data('reset') !== false && !$form.find('input[name="_method"][value="PUT"]').length) { $form[0].reset(); // Reset Choices.js if present $form.find('select').each(function() { if (this.choices) this.choices.setChoiceByValue(''); }); } // Trigger custom event for specific page logic $form.trigger('ajaxForm:success', [response]); }, error: function(xhr) { let errorMsg = 'Something went wrong.'; if (xhr.status === 422) { const errors = xhr.responseJSON.errors; errorMsg = ''; } else if (xhr.responseJSON && xhr.responseJSON.message) { errorMsg = xhr.responseJSON.message; } if ($form.data('alert') !== false) { const swalInstance = window.StandardSwal || Swal; swalInstance.fire({ icon: 'error', title: 'Validation Error', html: errorMsg, confirmButtonText: 'Understood' }); } $form.trigger('ajaxForm:error', [xhr]); }, complete: function() { // Restore button $submitBtn.prop('disabled', false).html(originalBtnHtml); } }); }); });