/** * HVAC Community Events: UX Enhancements JavaScript * * Modern user experience enhancements including toast notifications, * loading states, and improved error handling. * * @version 1.0.0 */ (function($) { 'use strict'; /** * Toast Notification System */ const HVACToast = { container: null, init: function() { // Create toast container if it doesn't exist if (!this.container) { this.container = $('
'); $('body').append(this.container); } }, show: function(message, type = 'info', title = '', duration = 5000) { this.init(); const toastId = 'hvac-toast-' + Date.now(); const toast = $(`
${title ? `
${title}
` : ''}
${message}
`); // Add event listeners toast.find('.hvac-toast-close').on('click', () => this.hide(toastId)); // Add to container and show this.container.append(toast); // Trigger show animation setTimeout(() => toast.addClass('show'), 10); // Auto-hide after duration if (duration > 0) { setTimeout(() => this.hide(toastId), duration); } return toastId; }, hide: function(toastId) { const toast = $('#' + toastId); if (toast.length) { toast.addClass('hiding'); setTimeout(() => toast.remove(), 300); } }, success: function(message, title = 'Success', duration = 4000) { return this.show(message, 'success', title, duration); }, error: function(message, title = 'Error', duration = 7000) { return this.show(message, 'error', title, duration); }, warning: function(message, title = 'Warning', duration = 6000) { return this.show(message, 'warning', title, duration); }, info: function(message, title = '', duration = 5000) { return this.show(message, 'info', title, duration); } }; /** * Loading State Manager */ const HVACLoading = { overlay: null, showOverlay: function(message = 'Loading...') { this.hideOverlay(); // Remove existing overlay this.overlay = $(`
${message}
`); $('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);