Files

128 lines
5.0 KiB
JavaScript

$(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('<span class="spinner-border spinner-border-sm me-2"></span> ' + ( $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 = '<ul class="text-start mb-0">';
Object.values(errors).flat().forEach(err => {
errorMsg += `<li>${err}</li>`;
});
errorMsg += '</ul>';
} 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);
}
});
});
});