/** * Tab Management for HM QC Platform * * Handles tab state preservation, navigation, and cross-tab communication */ const TabManager = { /** * Save state for a specific tab * @param {string} tabId - Tab identifier (e.g., 'hm-qc', 'video-qc') * @param {object} state - State object to save */ saveState(tabId, state) { try { const stateKey = `tab_${tabId}_state`; sessionStorage.setItem(stateKey, JSON.stringify({ ...state, timestamp: new Date().toISOString() })); } catch (error) { console.error(`Error saving state for tab ${tabId}:`, error); } }, /** * Load state for a specific tab * @param {string} tabId - Tab identifier * @returns {object|null} Saved state or null if not found */ loadState(tabId) { try { const stateKey = `tab_${tabId}_state`; const stateStr = sessionStorage.getItem(stateKey); if (!stateStr) { return null; } return JSON.parse(stateStr); } catch (error) { console.error(`Error loading state for tab ${tabId}:`, error); return null; } }, /** * Clear state for a specific tab * @param {string} tabId - Tab identifier */ clearState(tabId) { try { const stateKey = `tab_${tabId}_state`; sessionStorage.removeItem(stateKey); } catch (error) { console.error(`Error clearing state for tab ${tabId}:`, error); } }, /** * Clear all tab states */ clearAllStates() { try { const keys = Object.keys(sessionStorage); keys.forEach(key => { if (key.startsWith('tab_')) { sessionStorage.removeItem(key); } }); } catch (error) { console.error('Error clearing all tab states:', error); } }, /** * Get current active tab * @returns {string|null} Active tab ID or null */ getActiveTab() { const activeLink = document.querySelector('.hm-tabs .nav-link.active'); return activeLink ? activeLink.dataset.tab : null; }, /** * Set active tab * @param {string} tabId - Tab identifier to activate */ setActiveTab(tabId) { // Remove active class from all tabs document.querySelectorAll('.hm-tabs .nav-link').forEach(link => { link.classList.remove('active'); }); // Add active class to target tab const targetLink = document.querySelector(`.hm-tabs .nav-link[data-tab="${tabId}"]`); if (targetLink) { targetLink.classList.add('active'); } }, /** * Check if a tab has new content/notifications * @param {string} tabId - Tab identifier * @returns {boolean} True if tab has notifications */ hasNotification(tabId) { const state = this.loadState(tabId); return state && state.hasNotification === true; }, /** * Set notification status for a tab * @param {string} tabId - Tab identifier * @param {boolean} hasNotification - Notification status */ setNotification(tabId, hasNotification) { const state = this.loadState(tabId) || {}; state.hasNotification = hasNotification; this.saveState(tabId, state); // Update UI to show notification indicator this.updateNotificationIndicator(tabId, hasNotification); }, /** * Update notification indicator in tab UI * @param {string} tabId - Tab identifier * @param {boolean} show - Whether to show indicator */ updateNotificationIndicator(tabId, show) { const tabLink = document.querySelector(`.hm-tabs .nav-link[data-tab="${tabId}"]`); if (!tabLink) return; // Remove existing indicator const existingIndicator = tabLink.querySelector('.notification-indicator'); if (existingIndicator) { existingIndicator.remove(); } // Add new indicator if needed if (show) { const indicator = document.createElement('span'); indicator.className = 'notification-indicator'; indicator.innerHTML = ''; indicator.style.cssText = 'color: #dc3545; font-size: 2rem; line-height: 0; margin-left: -8px;'; tabLink.appendChild(indicator); } } }; /** * Initialize tab management when DOM is loaded */ document.addEventListener('DOMContentLoaded', function() { console.log('Tab management initialized'); // Handle tab click events document.querySelectorAll('.hm-tabs .nav-link').forEach(link => { link.addEventListener('click', function(e) { const currentTab = TabManager.getActiveTab(); const targetTab = this.dataset.tab; // Don't do anything if clicking the same tab if (currentTab === targetTab) { e.preventDefault(); return; } // Save current tab state before switching if (currentTab) { const currentState = getCurrentTabState(currentTab); if (currentState) { TabManager.saveState(currentTab, currentState); } } // Clear notification when switching to a tab TabManager.setNotification(targetTab, false); console.log(`Switching from ${currentTab} to ${targetTab}`); }); }); // Restore state for current active tab const activeTab = TabManager.getActiveTab(); if (activeTab) { const state = TabManager.loadState(activeTab); if (state) { console.log(`Restoring state for ${activeTab}:`, state); restoreTabState(activeTab, state); } } // Check for notifications from other tabs checkForNotifications(); }); /** * Get current state for a tab (to be implemented per module) * @param {string} tabId - Tab identifier * @returns {object} Current state object */ function getCurrentTabState(tabId) { // This function should be overridden by each module // to return its current state return { scrollPosition: window.scrollY, timestamp: new Date().toISOString() }; } /** * Restore state for a tab (to be implemented per module) * @param {string} tabId - Tab identifier * @param {object} state - State to restore */ function restoreTabState(tabId, state) { // This function should be overridden by each module // to restore its state if (state.scrollPosition) { window.scrollTo(0, state.scrollPosition); } } /** * Check for notifications from other tabs */ function checkForNotifications() { const tabs = ['hm-qc', 'video-qc', 'video-master', 'reporting']; tabs.forEach(tabId => { if (TabManager.hasNotification(tabId)) { TabManager.updateNotificationIndicator(tabId, true); } }); } /** * Show notification toast * @param {string} message - Notification message * @param {string} type - Notification type (success, error, warning, info) */ function showNotification(message, type = 'info') { // Create toast container if it doesn't exist let toastContainer = document.querySelector('.toast-container'); if (!toastContainer) { toastContainer = document.createElement('div'); toastContainer.className = 'toast-container position-fixed top-0 end-0 p-3'; toastContainer.style.zIndex = '9999'; document.body.appendChild(toastContainer); } // Create toast element const toast = document.createElement('div'); toast.className = `toast align-items-center text-white bg-${type === 'error' ? 'danger' : type} border-0`; toast.setAttribute('role', 'alert'); toast.setAttribute('aria-live', 'assertive'); toast.setAttribute('aria-atomic', 'true'); toast.innerHTML = `
${message}
`; toastContainer.appendChild(toast); // Show toast const bsToast = new bootstrap.Toast(toast, { delay: 5000, autohide: true }); bsToast.show(); // Remove toast after it's hidden toast.addEventListener('hidden.bs.toast', function() { toast.remove(); }); } // Make TabManager available globally window.TabManager = TabManager; window.showNotification = showNotification;