`);
$('body').append(this.overlay);
},
hideOverlay: function() {
if (this.overlay) {
this.overlay.remove();
this.overlay = null;
}
},
showButton: function(button, text = 'Loading...') {
const $btn = $(button);
$btn.addClass('loading').prop('disabled', true);
$btn.data('original-text', $btn.text());
if (text) {
$btn.attr('aria-label', text);
}
},
hideButton: function(button) {
const $btn = $(button);
$btn.removeClass('loading').prop('disabled', false);
const originalText = $btn.data('original-text');
if (originalText) {
$btn.text(originalText).removeData('original-text');
}
$btn.removeAttr('aria-label');
}
};
/**
* Enhanced AJAX Handler
*/
const HVACAjax = {
request: function(options) {
const defaults = {
type: 'POST',
dataType: 'json',
timeout: 30000,
showLoading: true,
loadingMessage: 'Processing...',
showToasts: true,
beforeSend: function() {
if (options.showLoading && options.button) {
HVACLoading.showButton(options.button, options.loadingMessage);
} else if (options.showLoading && options.showOverlay) {
HVACLoading.showOverlay(options.loadingMessage);
}
},
complete: function() {
if (options.showLoading && options.button) {
HVACLoading.hideButton(options.button);
} else if (options.showLoading && options.showOverlay) {
HVACLoading.hideOverlay();
}
},
success: function(response) {
if (options.showToasts) {
if (response.success) {
const message = response.data?.message || 'Operation completed successfully';
HVACToast.success(message);
} else {
const message = response.data?.message || 'Operation failed';
HVACToast.error(message);
}
}
},
error: function(xhr, status, error) {
console.error('AJAX Error:', {xhr, status, error});
if (options.showToasts) {
let message = 'An unexpected error occurred. Please try again.';
if (status === 'timeout') {
message = 'Request timed out. Please check your connection and try again.';
} else if (status === 'abort') {
message = 'Request was cancelled.';
} else if (xhr.status === 403) {
message = 'You do not have permission to perform this action.';
} else if (xhr.status === 404) {
message = 'The requested resource was not found.';
} else if (xhr.status >= 500) {
message = 'Server error. Please try again later.';
}
HVACToast.error(message, 'Connection Error');
}
}
};
const settings = $.extend({}, defaults, options);
return $.ajax(settings);
}
};
/**
* Form Validation Enhancement
*/
const HVACValidation = {
validateField: function(field, rules = []) {
const $field = $(field);
const value = $field.val().trim();
let isValid = true;
let errorMessage = '';
// Remove existing error states using compatibility fix
if (typeof window.HVACjQuery !== 'undefined') {
window.HVACjQuery.safeRemoveClass($field, 'error');
} else {
try {
$field.removeClass('error');
} catch (error) {
if ($field[0] && $field[0].classList) {
$field[0].classList.remove('error');
}
}
}
$field.siblings('.hvac-field-error').remove();
// Check rules
for (const rule of rules) {
if (rule.required && !value) {
isValid = false;
errorMessage = rule.message || 'This field is required';
break;
}
if (rule.minLength && value.length < rule.minLength) {
isValid = false;
errorMessage = rule.message || `Must be at least ${rule.minLength} characters`;
break;
}
if (rule.pattern && !rule.pattern.test(value)) {
isValid = false;
errorMessage = rule.message || 'Invalid format';
break;
}
if (rule.custom && typeof rule.custom === 'function') {
const result = rule.custom(value);
if (result !== true) {
isValid = false;
errorMessage = result || 'Invalid value';
break;
}
}
}
if (!isValid) {
$field.addClass('error');
$field.after(`
${errorMessage}
`);
}
return isValid;
}
};
/**
* Mobile Navigation Enhancement
*/
const HVACMobile = {
init: function() {
this.setupMobileNav();
this.setupTouchOptimizations();
},
setupMobileNav: function() {
// Create mobile navigation if not exists
$('.hvac-dashboard-nav').each(function() {
const $nav = $(this);
if ($nav.siblings('.hvac-mobile-nav-container').length === 0) {
const mobileNav = $(`
`);
// Copy nav items to mobile menu
const $mobileList = mobileNav.find('ul');
$nav.find('a').each(function() {
const $link = $(this).clone();
$mobileList.append($('
').append($link));
});
$nav.before(mobileNav);
// Add toggle functionality
mobileNav.find('.hvac-mobile-nav-toggle').on('click', function() {
const $toggle = $(this);
const $menu = $toggle.siblings('.hvac-mobile-nav');
const isOpen = $menu.hasClass('open');
$toggle.toggleClass('active').attr('aria-expanded', !isOpen);
$menu.toggleClass('open');
});
}
});
},
setupTouchOptimizations: function() {
// Add touch-friendly classes
$('button, .hvac-btn, input[type="submit"]').addClass('hvac-touch-target');
// Prevent double-tap zoom on buttons
$('button, .hvac-btn').on('touchend', function(e) {
e.preventDefault();
$(this).trigger('click');
});
}
};
/**
* Accessibility Enhancements
*/
const HVACAccessibility = {
init: function() {
this.setupKeyboardNavigation();
this.setupAriaLabels();
},
setupKeyboardNavigation: function() {
// Escape key to close modals/overlays
$(document).on('keydown', function(e) {
if (e.key === 'Escape') {
// Close any open mobile nav
$('.hvac-mobile-nav.open').removeClass('open');
$('.hvac-mobile-nav-toggle.active').removeClass('active').attr('aria-expanded', 'false');
// Hide loading overlay
HVACLoading.hideOverlay();
}
});
},
setupAriaLabels: function() {
// Add missing aria-labels to buttons without text
$('button:not([aria-label])').each(function() {
const $btn = $(this);
const text = $btn.text().trim();
if (!text) {
const title = $btn.attr('title');
if (title) {
$btn.attr('aria-label', title);
}
}
});
}
};
/**
* Replace all alert() calls with toast notifications
*/
function replaceAlerts() {
// Override the global alert function
window.hvacOriginalAlert = window.alert;
window.alert = function(message) {
// Determine type based on message content
const lowerMessage = message.toLowerCase();
if (lowerMessage.includes('success') || lowerMessage.includes('sent') || lowerMessage.includes('saved')) {
HVACToast.success(message);
} else if (lowerMessage.includes('error') || lowerMessage.includes('failed') || lowerMessage.includes('fail')) {
HVACToast.error(message);
} else {
HVACToast.info(message);
}
};
}
/**
* Initialize everything when document is ready
*/
$(document).ready(function() {
// Initialize all modules
HVACToast.init();
HVACMobile.init();
HVACAccessibility.init();
// Replace alert functions
replaceAlerts();
// Make modules globally available
window.HVACToast = HVACToast;
window.HVACLoading = HVACLoading;
window.HVACAjax = HVACAjax;
window.HVACValidation = HVACValidation;
// Auto-enhance existing forms
$('form').each(function() {
const $form = $(this);
// Add loading states to submit buttons
$form.on('submit', function() {
const $submitBtn = $form.find('input[type="submit"], button[type="submit"]').first();
if ($submitBtn.length) {
HVACLoading.showButton($submitBtn);
}
});
});
// Add responsive table wrapper
$('table').each(function() {
const $table = $(this);
if (!$table.parent().hasClass('hvac-table-responsive')) {
$table.wrap('');
}
});
console.log('HVAC UX Enhancements initialized');
});
})(jQuery);