UNPKG

besper-frontend-site-dev-main

Version:

Professional B-esper Frontend Site - Site-wide integration toolkit for full website bot deployment

275 lines (245 loc) 8.05 kB
/** * Standard Page Base Class - Bulletproof loading pattern for all pages * * This solves the persistent "Page class not found" errors by providing: * 1. Immediate UI rendering with skeleton loading * 2. Safe authentication handling without blocking class export * 3. Standardized pattern for all pages * 4. Bulletproof export mechanism */ class StandardPageBase { constructor(options = {}) { this.options = { containerId: 'besper-site-content', environment: 'prod', ...options, }; this.initialized = false; this.authService = null; this.isAuthenticated = false; // Defer all complex initialization to avoid blocking class export this.deferredInitialization(); } /** * Deferred initialization - runs after class is exported to window */ deferredInitialization() { if (typeof requestIdleCallback !== 'undefined') { requestIdleCallback( () => { this.initializeAuthService(); }, { timeout: 1000 } ); } else { setTimeout(() => { this.initializeAuthService(); }, 10); } } /** * Initialize auth service safely without blocking */ initializeAuthService() { try { this.authService = this.getAuthService(); } catch (error) { console.warn('Auth service initialization failed:', error); this.authService = this.createFallbackAuthService(); } } /** * Get authentication service from global scope or create simple fallback */ getAuthService() { // Try to access global token auth service if available if (typeof window !== 'undefined' && window.tokenAuthService) { return window.tokenAuthService; } // Fallback implementation using global window.auth return this.createFallbackAuthService(); } /** * Create fallback auth service */ createFallbackAuthService() { return { isUserAuthenticated: () => { try { return ( typeof window !== 'undefined' && window.auth && typeof window.auth.getToken === 'function' && !!window.auth.getToken() ); } catch (error) { return false; } }, getToken: () => { try { if ( typeof window !== 'undefined' && window.auth && typeof window.auth.getToken === 'function' ) { return window.auth.getToken(); } } catch (error) { console.warn('Token access failed:', error); } return null; }, getUserPermission: key => { try { if (typeof window === 'undefined') return null; const mappings = { contactId: window.contact_id || window.user_contactid, userName: window.user_name, name: window.user_name, userEmail: window.user_email, email: window.user_email, workspaceId: window.workspace_id, accountId: window.account_id, subscriptionId: window.subscription_id, }; return mappings[key] || null; } catch (error) { console.warn('User permission access failed:', error); return null; } }, }; } /** * Standard initialize method - IMMEDIATE RENDERING pattern * @param {Object} data - Optional initialization data */ async initialize(data = {}) { if (this.initialized) return; try { // IMMEDIATE: Show the UI without any loading delays this.renderImmediateUI(); // IMMEDIATE: Setup basic interactions this.setupBasicInteractions(); this.initialized = true; // DEFERRED: Initialize advanced features in background this.initializeAdvancedFeaturesInBackground(data); } catch (error) { console.error(`Error initializing page:`, error); this.showError(error); } } /** * Render immediate UI - must be overridden by child classes */ renderImmediateUI() { const container = document.getElementById(this.options.containerId); if (!container) return; // Clear any existing loading indicators container.innerHTML = ''; // Show skeleton loading by default container.innerHTML = ` <div style="background: #f8f9fa; padding: 2rem;"> <div style="max-width: 1200px; margin: 0 auto;"> <div style="background: white; padding: 2rem; border-radius: 8px; border: 1px solid #e0e0e0;"> <div style="height: 40px; background: #e0e0e0; border-radius: 4px; margin-bottom: 1rem; animation: pulse 1.5s ease-in-out infinite;"></div> <div style="height: 20px; background: #e0e0e0; border-radius: 4px; margin-bottom: 1rem; width: 60%; animation: pulse 1.5s ease-in-out infinite;"></div> <div style="height: 300px; background: #e0e0e0; border-radius: 4px; animation: pulse 1.5s ease-in-out infinite;"></div> </div> </div> </div> <style> @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } } </style> `; } /** * Setup basic interactions - override in child classes */ setupBasicInteractions() { // Override in child classes } /** * Initialize advanced features in background - NO UI BLOCKING */ async initializeAdvancedFeaturesInBackground(data) { // Use requestIdleCallback for true background processing const initializeAdvanced = async () => { try { // Wait for auth service to be ready if (!this.authService) { this.initializeAuthService(); } // Check authentication status without blocking if (this.authService) { this.isAuthenticated = this.authService.isUserAuthenticated(); } // Load data and update UI await this.loadDataInBackground(data); } catch (error) { console.warn( 'Background advanced features initialization failed:', error ); // Continue silently - advanced features are not critical } }; // Use requestIdleCallback for non-blocking background execution if (typeof requestIdleCallback !== 'undefined') { requestIdleCallback(initializeAdvanced, { timeout: 5000 }); } else { // Fallback for browsers without requestIdleCallback setTimeout(initializeAdvanced, 100); } } /** * Load data in background - override in child classes */ async loadDataInBackground(data) { // Override in child classes console.log('Loading data in background...', data); } /** * Show error message with consistent styling */ showError(error) { const container = document.getElementById(this.options.containerId); if (container) { container.innerHTML = ` <div style="background: #f8f9fa; display: flex; align-items: center; justify-content: center; padding: 4rem 1rem;"> <div style="text-align: center; padding: 2rem; background: white; border-radius: 8px; border: 1px solid #e0e0e0; max-width: 500px;"> <h3 style="color: #d32f2f; margin-bottom: 1rem;">Error Loading Page</h3> <p style="color: #6c757d; margin-bottom: 1.5rem;">${error.message || error}</p> <button onclick="location.reload()" style="padding: 10px 20px; background: #022d54; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px;"> Try Again </button> </div> </div> `; } } /** * Cleanup resources */ destroy() { this.initialized = false; } } // Bulletproof export mechanism (function () { if (typeof module !== 'undefined' && module.exports) { module.exports = StandardPageBase; } if (typeof window !== 'undefined') { window.StandardPageBase = StandardPageBase; } // Global fallback if (typeof global !== 'undefined') { global.StandardPageBase = StandardPageBase; } })();