`;
const existingModal = document.getElementById(`${type}Modal`);
if (existingModal) {
existingModal.remove();
}
document.body.insertAdjacentHTML("beforeend", modalHTML);
// Add animation classes
requestAnimationFrame(() => {
const modal = document.getElementById(`${type}Modal`);
const content = modal.querySelector(".modal-content");
content.style.animation = "swal2-show 0.3s";
});
};
window.closeModal = function(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
const content = modal.querySelector(".modal-content");
content.style.animation = "swal2-hide 0.15s forwards";
const backdrop = document.querySelector(".modal-backdrop");
if (backdrop) {
backdrop.style.opacity = "0";
backdrop.style.transition = "opacity 0.15s";
}
setTimeout(() => {
modal.remove();
if (backdrop) backdrop.remove();
document.body.classList.remove("modal-open");
}, 150);
}
};
// Initialize form
document.addEventListener('DOMContentLoaded', function() {
showStep(1);
// File upload handlers
document.querySelectorAll('input[type="file"]').forEach(input => {
input.addEventListener('change', function() {
const uploadArea = this.closest('.file-upload-area');
const textElement = uploadArea.querySelector('.file-upload-text');
if (this.files.length > 0) {
textElement.textContent = this.files[0].name;
uploadArea.style.background = '#e8f5e8'; // Light green for success
uploadArea.style.borderColor = 'var(--success-color)';
displayFieldError(this, ''); // Clear error on successful file selection
} else {
textElement.textContent = scholarshipAjax.options.file_upload_text;
uploadArea.style.background = 'var(--accent-bg)';
uploadArea.style.borderColor = 'var(--primary-color)';
}
});
});
// Form submission
document.getElementById('scholarship-form').addEventListener('submit', handleFormSubmit);
// Initialize payment method selection
document.querySelectorAll('.payment-option').forEach(card => {
card.addEventListener('click', function() {
// Update radio button state
const radio = this.querySelector('input[type="radio"]');
if (radio) {
// radio.checked = true;
// Manually trigger change event if needed for other listeners, though selectPaymentMethod handles UI
// var event = new Event('change');
// radio.dispatchEvent(event);
}
selectPaymentMethod(this.dataset.method);
// Clear radio button error when an option is selected
const radioGroup = document.querySelector(`input[name="${radio.name}"]`);
if (radioGroup) {
displayFieldError(radioGroup, '');
}
});
});
// Set initial payment method state
const initialCheckedRadio = document.querySelector('input[name="payment_method"]:checked');
if (initialCheckedRadio) {
selectPaymentMethod(initialCheckedRadio.value);
} else {
// If no radio is checked by default (e.g. due to HTML structure), check the first one.
const firstRadio = document.querySelector('input[name="payment_method"]');
if (firstRadio) {
firstRadio.checked = true;
selectPaymentMethod(firstRadio.value);
}
}
});
function showStep(step) {
// Hide all steps
document.querySelectorAll('.step-content').forEach(content => {
content.classList.remove('active');
});
// Show current step
const currentStepEl = document.getElementById('step-' + step);
if (currentStepEl) {
currentStepEl.classList.add('active');
}
// Update progress bar
for (let i = 1; i <= totalSteps; i++) {
const circle = document.getElementById('step-circle-' + i).parentElement;
if (circle) {
circle.classList.remove('active', 'completed');
if (i < step) {
circle.classList.add('completed');
} else if (i === step) {
circle.classList.add('active');
}
}
}
currentStep = step;
window.scrollTo(0, 0); // Scroll to top on step change
}
function nextStep() {
if (validateCurrentStep() && currentStep < totalSteps) {
showStep(currentStep + 1);
}
}
function previousStep() {
if (currentStep > 1) {
showStep(currentStep - 1);
}
}
function validateCurrentStep() {
const currentStepElement = document.getElementById('step-' + currentStep);
if (!currentStepElement) return false;
let isValid = true;
const stepFields = scholarshipAjax.fields[currentStep];
let firstInvalidField = null;
let errorMessages = [];
for (const fieldName in stepFields) {
const config = stepFields[fieldName];
if (config.type === 'payment_details') {
continue;
}
if (config.required) {
const fieldElement = document.getElementById(fieldName);
if (!fieldElement && config.type !== 'radio') {
console.warn(`Required field element not found for: ${fieldName}`);
continue;
}
// Reset previous error states
if (fieldElement) {
displayFieldError(fieldElement, '');
if (fieldElement.type !== 'file') {
fieldElement.style.borderColor = '';
} else {
const fileArea = fieldElement.closest('.file-upload-area');
if (fileArea) fileArea.style.borderColor = '';
}
}
let fieldIsValid = true;
let errorMessage = '';
if (config.type === 'radio') {
const radioGroupName = fieldName;
const radioGroup = currentStepElement.querySelectorAll(`input[name="${radioGroupName}"]`);
const isRadioChecked = Array.from(radioGroup).some(radio => radio.checked);
if (!isRadioChecked) {
fieldIsValid = false;
errorMessage = scholarshipAjax.options.please_select_option;
const paymentOptionsContainer = currentStepElement.querySelector('.payment-options');
if (paymentOptionsContainer) paymentOptionsContainer.classList.add('input-error-glow');
} else {
const paymentOptionsContainer = currentStepElement.querySelector('.payment-options');
if (paymentOptionsContainer) paymentOptionsContainer.classList.remove('input-error-glow');
}
} else if (config.type === 'file') {
if (fieldElement.files.length === 0) {
fieldIsValid = false;
errorMessage = scholarshipAjax.options.this_file_is_required;
const fileArea = fieldElement.closest('.file-upload-area');
if (fileArea) fileArea.style.borderColor = 'var(--error-color)';
}
} else if (config.type === 'url') {
if (!fieldElement.value.trim() || !isValidUrl(fieldElement.value.trim())) {
fieldIsValid = false;
errorMessage = scholarshipAjax.options.please_enter_valid_url;
fieldElement.style.borderColor = 'var(--error-color)';
}
} else if (!fieldElement.value.trim()) {
fieldIsValid = false;
errorMessage = scholarshipAjax.options.this_field_is_required;
fieldElement.style.borderColor = 'var(--error-color)';
}
if (!fieldIsValid) {
isValid = false;
const targetElement = (config.type === 'radio' && radioGroup.length > 0) ? radioGroup[0] : fieldElement;
displayFieldError(targetElement, errorMessage);
errorMessages.push(config.label + ': ' + errorMessage);
if (!firstInvalidField) {
firstInvalidField = targetElement;
}
}
}
}
if (!isValid) {
showModal('error', errorMessages.join('
'));
if (firstInvalidField) {
firstInvalidField.scrollIntoView({ behavior: 'smooth', block: 'center' });
if (typeof firstInvalidField.focus === 'function' && firstInvalidField.type !== 'hidden') {
firstInvalidField.focus();
}
}
}
return isValid;
}
function isValidUrl(str) {
if (!str?.trim()) return false;
const s = str.trim();
const patterns = [
s,
`https://${s}`,
`http://${s}`,
s.includes('www.') ? null : `https://www.${s}`,
s.includes('www.') ? null : `http://www.${s}`
];
return patterns.some(p => p && isUrl(p));
}
const isUrl = s => {
try {
const u = new URL(s);
return u.hostname.includes('.');
} catch {
return false;
}
};
// Bonus: Get normalized URL
const normalizeUrl = str => {
if (!str?.trim()) return null;
const s = str.trim();
for (const p of [s, `https://${s}`, `https://www.${s}`]) {
if (isUrl(p)) return new URL(p).href;
}
return null;
};
function selectPaymentMethod(methodId) {
// Update visual selection of payment option cards
document.querySelectorAll('.payment-option').forEach(optionCard => {
if (optionCard.dataset.method === methodId) {
optionCard.classList.add('selected');
} else {
optionCard.classList.remove('selected');
}
});
// Ensure the correct radio button is checked (it might already be if click originated from card)
const radioToCheck = document.querySelector('input[name="payment_method"][value="' + methodId + '"]');
if (radioToCheck) {
radioToCheck.checked = true;
}
// Show/hide corresponding payment details sections
document.querySelectorAll('.payment-details').forEach(detailsSection => {
detailsSection.classList.add('d-none');
detailsSection.classList.remove('d-block');
});
const activeDetailsSection = document.getElementById(methodId + '_details-container');
if (activeDetailsSection) {
activeDetailsSection.classList.remove('d-none');
activeDetailsSection.classList.add('d-block');
}
}
function handleFormSubmit(e) {
e.preventDefault();
if (!validateCurrentStep()) {
return;
}
const form = document.getElementById('scholarship-form');
const formData = new FormData(form);
formData.append('action', 'submit_scholarship_application_form');
formData.append('scholarship_nonce', document.getElementById('scholarship_nonce').value); // Ensure nonce is included
// Show loading state
const submitBtn = form.querySelector('button[type="submit"]');
const originalText = submitBtn.textContent;
submitBtn.innerHTML = '
' + scholarshipAjax.options.submitting_text;
submitBtn.disabled = true;
fetch('https://eduhub21.com/wp-admin/admin-ajax.php', {
method: 'POST',
body: formData
// Headers not needed for FormData, browser sets multipart/form-data
})
.then(response => response.json())
.then(data => {
if (data.success) {
showModal('success', data.data.message || scholarshipAjax.options.success_message_title);
form.reset();
// Reset file input visual states
document.querySelectorAll('input[type="file"]').forEach(input => {
const uploadArea = input.closest('.file-upload-area');
const textElement = uploadArea.querySelector('.file-upload-text');
textElement.textContent = scholarshipAjax.options.file_upload_text;
uploadArea.style.background = 'var(--accent-bg)';
uploadArea.style.borderColor = 'var(--primary-color)';
});
// Reset payment method to the first option and hide details
const firstPaymentRadio = document.querySelector('input[name="payment_method"]');
if (firstPaymentRadio) {
selectPaymentMethod(firstPaymentRadio.value);
}
showStep(1); // Go back to the first step
} else {
showModal('error', data.data.message || 'An error occurred. Please try again.');
}
})
.catch(error => {
console.error('Error:', error);
showModal('error', scholarshipAjax.options.error_message + '. ' + scholarshipAjax.options.check_console_for_details);
})
.finally(() => {
submitBtn.textContent = originalText;
submitBtn.disabled = false;
});
}