upskill-event-manager/assets/js/hvac-ux-enhancements.js
Ben bb3441c0e6 feat: Complete TEC integration with mobile fixes and comprehensive testing
- Added mobile navigation fix CSS to resolve overlapping elements
- Created TEC integration pages (create, edit, my events)
- Implemented comprehensive Playwright E2E test suites
- Fixed mobile navigation conflicts with z-index management
- Added test runners with detailed reporting
- Achieved 70% test success rate (100% on core features)
- Page load performance optimized to 3.8 seconds
- Cross-browser compatibility verified

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-18 07:07:06 -03:00

419 lines
No EOL
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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 = $('<div class="hvac-toast-container"></div>');
$('body').append(this.container);
}
},
show: function(message, type = 'info', title = '', duration = 5000) {
this.init();
const toastId = 'hvac-toast-' + Date.now();
const toast = $(`
<div class="hvac-toast ${type}" id="${toastId}">
<div class="hvac-toast-icon"></div>
<div class="hvac-toast-content">
${title ? `<div class="hvac-toast-title">${title}</div>` : ''}
<div class="hvac-toast-message">${message}</div>
</div>
<button class="hvac-toast-close" type="button" aria-label="Close notification">×</button>
</div>
`);
// 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 = $(`
<div class="hvac-loading-overlay">
<div class="hvac-loading">
<div class="hvac-spinner large"></div>
<span>${message}</span>
</div>
</div>
`);
$('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(`<div class="hvac-field-error">${errorMessage}</div>`);
}
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 = $(`
<div class="hvac-mobile-nav-container">
<button class="hvac-mobile-nav-toggle" type="button" aria-expanded="false">
<span class="hvac-sr-only">Toggle navigation</span>
Navigation Menu
</button>
<nav class="hvac-mobile-nav">
<ul></ul>
</nav>
</div>
`);
// Copy nav items to mobile menu
const $mobileList = mobileNav.find('ul');
$nav.find('a').each(function() {
const $link = $(this).clone();
$mobileList.append($('<li>').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('<div class="hvac-table-responsive"></div>');
}
});
console.log('HVAC UX Enhancements initialized');
});
})(jQuery);