radiantrh/Bootstrap_new/js/ux-manager.js
2025-12-20 21:07:26 +00:00

511 lines
16 KiB
JavaScript

// UX Manager - Gestionnaire d'expérience utilisateur modulaire
class UXManager {
constructor() {
this.navigation = new NavigationManager();
this.contextPanel = new ContextPanelManager();
this.notifications = new NotificationManager();
this.accessibility = new AccessibilityManager();
this.performance = new PerformanceManager();
this.language = new LanguageManager();
this.init();
}
init() {
console.log('[UX Manager] Initialisation...');
this.navigation.init();
this.contextPanel.init();
this.notifications.init();
this.accessibility.init();
this.performance.init();
this.language.init();
// Vérifier la session
this.checkSession();
// Initialiser le Service Worker
this.initServiceWorker();
}
toggleContextPanel() {
this.contextPanel.toggle();
}
toggleSidebar() {
const sidebar = document.getElementById('sidebar');
sidebar.classList.toggle('show');
}
checkSession() {
const dureeSession = parseInt(document.getElementById('dureeSession').value) || 30;
const dureeMinutes = dureeSession * 60 * 1000;
setInterval(() => {
const derniereAction = sessionStorage.getItem('derniere_action');
const maintenant = Date.now();
if (derniereAction && (maintenant - derniereAction > dureeMinutes)) {
this.showSessionWarning();
}
}, 60000); // Vérifier toutes les minutes
}
showSessionWarning() {
const isAnglophone = window.appConfig.isAnglophone;
const message = isAnglophone
? 'Your session will expire soon. Do you want to extend it?'
: 'Votre session va bientôt expirer. Souhaitez-vous la prolonger?';
Swal.fire({
title: isAnglophone ? 'Session Warning' : 'Avertissement de session',
text: message,
icon: 'warning',
showCancelButton: true,
confirmButtonText: isAnglophone ? 'Extend' : 'Prolonger',
cancelButtonText: isAnglophone ? 'Logout' : 'Déconnexion'
}).then((result) => {
if (result.isConfirmed) {
sessionStorage.setItem('derniere_action', Date.now());
} else {
window.location.href = window.appConfig.racineWeb + 'Connexion/deconnecter';
}
});
}
initServiceWorker() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
console.log('[UX Manager] Service Worker prêt:', registration.scope);
});
}
}
}
// Navigation Manager - Gestion des menus
class NavigationManager {
constructor() {
this.currentOpenMenu = null;
}
init() {
this.setupMenuBehavior();
this.setupKeyboardNavigation();
this.setupActiveMenu();
}
setupMenuBehavior() {
// Désactiver le comportement Bootstrap par défaut
document.querySelectorAll('.nav-link[data-bs-toggle="collapse"]').forEach(link => {
link.addEventListener('click', (e) => {
const targetId = link.getAttribute('href').substring(1);
this.toggleMenu(targetId, e);
});
});
// Fermer le menu en cliquant ailleurs
document.addEventListener('click', (e) => {
if (!e.target.closest('.app-sidebar')) {
this.closeAllMenus();
}
});
}
toggleMenu(menuId, event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
const menu = document.getElementById(menuId);
const link = document.querySelector(`[href="#${menuId}"]`);
if (!menu || !link) return;
// Si ce menu est déjà ouvert, le fermer
if (this.currentOpenMenu === menuId) {
this.closeMenu(menuId);
this.currentOpenMenu = null;
} else {
// Fermer le menu actuellement ouvert
if (this.currentOpenMenu) {
this.closeMenu(this.currentOpenMenu);
}
// Ouvrir le nouveau menu
this.openMenu(menuId);
this.currentOpenMenu = menuId;
}
}
openMenu(menuId) {
const menu = document.getElementById(menuId);
const link = document.querySelector(`[href="#${menuId}"]`);
if (menu && link) {
menu.classList.add('show');
link.classList.add('active');
link.setAttribute('aria-expanded', 'true');
link.querySelector('.nav-arrow').style.transform = 'rotate(90deg)';
}
}
closeMenu(menuId) {
const menu = document.getElementById(menuId);
const link = document.querySelector(`[href="#${menuId}"]`);
if (menu && link) {
menu.classList.remove('show');
link.classList.remove('active');
link.setAttribute('aria-expanded', 'false');
link.querySelector('.nav-arrow').style.transform = 'rotate(0deg)';
}
}
closeAllMenus() {
document.querySelectorAll('.nav-submenu.show').forEach(menu => {
const menuId = menu.id;
this.closeMenu(menuId);
});
this.currentOpenMenu = null;
}
setupKeyboardNavigation() {
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.closeAllMenus();
}
// Navigation par flèches
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
const focused = document.activeElement;
if (focused.classList.contains('nav-link')) {
e.preventDefault();
this.navigateMenu(focused, e.key);
}
}
});
}
navigateMenu(currentElement, direction) {
const allLinks = Array.from(document.querySelectorAll('.nav-link'));
const currentIndex = allLinks.indexOf(currentElement);
if (direction === 'ArrowDown' && currentIndex < allLinks.length - 1) {
allLinks[currentIndex + 1].focus();
} else if (direction === 'ArrowUp' && currentIndex > 0) {
allLinks[currentIndex - 1].focus();
}
}
setupActiveMenu() {
// Ouvrir automatiquement le menu actif au chargement
const activeParentId = window.appConfig.activeParentId;
if (activeParentId !== null && activeParentId !== '') {
const menuId = `submenu${activeParentId}`;
setTimeout(() => {
this.openMenu(menuId);
this.currentOpenMenu = menuId;
}, 100);
}
}
}
// Context Panel Manager - Gestion du panneau de contexte
class ContextPanelManager {
constructor() {
this.panel = document.getElementById('contextPanel');
this.toggleButton = document.querySelector('.context-toggle');
this.proximityArea = document.querySelector('.proximity-hover-area');
this.isOpen = false;
}
init() {
this.setupProximityDetection();
this.setupKeyboardControls();
}
setupProximityDetection() {
if (!this.proximityArea) return;
this.proximityArea.addEventListener('mouseenter', () => {
this.toggleButton.style.opacity = '1';
this.toggleButton.style.transform = 'scale(1.1)';
});
this.proximityArea.addEventListener('mouseleave', () => {
if (!this.isOpen) {
this.toggleButton.style.opacity = '0.2';
this.toggleButton.style.transform = 'scale(1)';
}
});
// Initial opacity
this.toggleButton.style.opacity = '0.2';
}
setupKeyboardControls() {
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.shiftKey && e.key === 'C') {
e.preventDefault();
this.toggle();
}
if (e.key === 'Escape' && this.isOpen) {
this.close();
}
});
}
toggle() {
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
open() {
this.panel.classList.add('open');
this.toggleButton.setAttribute('aria-expanded', 'true');
this.toggleButton.style.opacity = '1';
this.isOpen = true;
// Focus sur le bouton de fermeture
setTimeout(() => {
this.panel.querySelector('.context-close').focus();
}, 300);
}
close() {
this.panel.classList.remove('open');
this.toggleButton.setAttribute('aria-expanded', 'false');
this.toggleButton.style.opacity = '0.2';
this.isOpen = false;
// Retourner le focus au bouton toggle
this.toggleButton.focus();
}
}
// Notification Manager
class NotificationManager {
constructor() {
this.countElement = document.getElementById('notificationCount');
this.unreadCount = 0;
}
init() {
this.loadNotifications();
this.setupPolling();
}
loadNotifications() {
// Simuler le chargement des notifications
this.updateCount(3); // Exemple: 3 notifications non lues
}
updateCount(count) {
this.unreadCount = count;
if (this.countElement) {
this.countElement.textContent = count;
this.countElement.style.display = count > 0 ? 'flex' : 'none';
// Mettre à jour le titre de la page
const baseTitle = document.title.replace(/^\(\d+\)\s*/, '');
document.title = count > 0 ? `(${count}) ${baseTitle}` : baseTitle;
}
}
setupPolling() {
// Vérifier les nouvelles notifications toutes les 30 secondes
setInterval(() => {
// À implémenter: appel AJAX pour vérifier les nouvelles notifications
// this.checkNewNotifications();
}, 30000);
}
showMessagesModal() {
const modal = new bootstrap.Modal(document.getElementById('messagesModal'));
modal.show();
// Charger les messages
this.loadMessages();
}
loadMessages() {
const container = document.getElementById('div_messagerie');
if (!container) return;
// Simuler le chargement des messages
container.innerHTML = `
<div class="list-group">
<div class="list-group-item">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Nouvelle prescription</h6>
<small>Il y a 5 minutes</small>
</div>
<p class="mb-1">Une nouvelle prescription a été créée pour le bénéficiaire.</p>
</div>
<div class="list-group-item">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Dérogation approuvée</h6>
<small>Il y a 2 heures</small>
</div>
<p class="mb-1">Votre demande de dérogation a été approuvée.</p>
</div>
</div>
`;
}
}
// Accessibility Manager
class AccessibilityManager {
init() {
this.setupFocusManagement();
this.setupAriaLiveRegions();
this.detectReducedMotion();
}
setupFocusManagement() {
// Gérer le focus pour les modales
document.addEventListener('shown.bs.modal', (e) => {
const modal = e.target;
const focusable = modal.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (focusable) focusable.focus();
});
}
setupAriaLiveRegions() {
// Créer une région ARIA live pour les notifications
const liveRegion = document.createElement('div');
liveRegion.setAttribute('aria-live', 'polite');
liveRegion.setAttribute('aria-atomic', 'true');
liveRegion.className = 'visually-hidden';
document.body.appendChild(liveRegion);
}
detectReducedMotion() {
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
if (mediaQuery.matches) {
document.documentElement.classList.add('reduced-motion');
}
}
}
// Performance Manager
class PerformanceManager {
init() {
this.monitorPerformance();
this.setupLazyLoading();
this.setupIntersectionObserver();
}
monitorPerformance() {
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
console.log('[Performance] LCP:', entry.startTime);
}
}
});
observer.observe({ entryTypes: ['largest-contentful-paint', 'first-input'] });
}
}
setupLazyLoading() {
// Lazy loading pour les images
if ('loading' in HTMLImageElement.prototype) {
document.querySelectorAll('img[loading="lazy"]').forEach(img => {
img.addEventListener('load', () => {
img.classList.add('loaded');
});
});
}
}
setupIntersectionObserver() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('in-view');
}
});
}, {
threshold: 0.1
});
document.querySelectorAll('.content-card').forEach(card => {
observer.observe(card);
});
}
}
// Language Manager
class LanguageManager {
changeLanguage() {
const currentLang = window.appConfig.isAnglophone ? 'en_US' : 'fr_FR';
const newLang = currentLang === 'en_US' ? 'fr_FR' : 'en_US';
// Demander confirmation
const message = window.appConfig.isAnglophone
? 'Switch to French?'
: 'Passer en Anglais?';
Swal.fire({
title: window.appConfig.isAnglophone ? 'Change Language' : 'Changer de langue',
text: message,
icon: 'question',
showCancelButton: true,
confirmButtonText: window.appConfig.isAnglophone ? 'Switch' : 'Changer',
cancelButtonText: window.appConfig.isAnglophone ? 'Cancel' : 'Annuler'
}).then((result) => {
if (result.isConfirmed) {
// Rediriger vers la page de connexion avec le nouveau paramètre de langue
window.location.href = window.appConfig.racineWeb +
'Connexion/index?langue=' + newLang;
}
});
}
init() {
// Initialiser les textes selon la langue
this.updateLanguageTexts();
}
updateLanguageTexts() {
// À implémenter: mise à jour des textes dynamiques
}
}
// Initialisation de l'application
document.addEventListener('DOMContentLoaded', () => {
window.appUX = new UXManager();
console.log('[App] Portail RH Inter Santé initialisé');
// Déclencher les animations après le chargement
setTimeout(() => {
document.body.classList.add('loaded');
}, 100);
});
// Fonctions globales
function appNotifications() {
return window.appUX?.notifications;
}
function appNavigation() {
return window.appUX?.navigation;
}
function appLanguage() {
return window.appUX?.language;
}
// Export pour les scripts externes
window.uxManager = {
toggleContextPanel: () => window.appUX?.toggleContextPanel(),
toggleSidebar: () => window.appUX?.toggleSidebar(),
showNotifications: () => window.appUX?.notifications.showMessagesModal()
};