UNPKG

besper-frontend-site-dev-main

Version:

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

354 lines (312 loc) 11.4 kB
/** * PAGE TEMPLATE - Bulletproof Loading Pattern * * Copy this template and replace: * - YourPageClass with your actual class name (e.g., UserManagementPage) * - your-page-content with your actual page content * - Initialize your specific page functionality * * This template solves "Page class not found" errors with: * - Immediate UI rendering * - Safe authentication handling * - Background data loading * - Bulletproof export mechanism */ class YourPageClass { constructor(options = {}) { this.options = { containerId: 'besper-site-content', environment: 'prod', ...options, }; this.initialized = false; // CRITICAL: Defer ALL complex operations to avoid blocking class export this.authService = null; this.isAuthenticated = false; // Initialize auth safely in next tick to ensure class export completes first if (typeof window !== 'undefined') { setTimeout(() => { this.initializeAuth(); }, 1); } } /** * Initialize authentication safely without blocking class export */ initializeAuth() { try { this.authService = this.getAuthService(); } catch (error) { console.warn('Auth initialization failed, using fallback:', 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; } return this.createFallbackAuthService(); } /** * Create safe 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; } }, }; } /** * Initialize the page - IMMEDIATE RENDERING * Shows UI instantly without waiting for authentication */ async initialize(_data = {}) { if (this.initialized) return; try { // IMMEDIATE: Show the UI without any loading delays this.renderImmediateUI(); // IMMEDIATE: Setup basic interactions this.setupInteractions(); this.initialized = true; // DEFERRED: Initialize authentication features in background this.initializeAuthenticationFeaturesInBackground(); } catch (error) { console.error('Error initializing page:', error); this.showError(error); } } /** * Render immediate UI structure - shows content instantly * CUSTOMIZE THIS METHOD for your page content */ renderImmediateUI() { const container = document.getElementById(this.options.containerId); if (!container) return; // Clear any existing loading indicators container.innerHTML = ''; // ADD YOUR PAGE CONTENT HERE - Replace with your actual UI container.innerHTML = ` <div style="background: #f8f9fa; color: #022d54; font-family: Arial, sans-serif;"> <div style="max-width: 1200px; margin: 0 auto; padding: 2rem 1rem;"> <!-- Header --> <div style="margin-bottom: 2rem;"> <h1 style="color: #022d54; font-weight: 300; font-size: 28px; margin-bottom: 0.5rem;">Your Page Title</h1> <p style="color: #6c757d; font-size: 14px; margin: 0;">Your page description</p> </div> <!-- Action Buttons --> <div style="display: flex; justify-content: flex-end; gap: 12px; margin-bottom: 2rem;"> <button class="primary-action-btn" style="padding: 10px 20px; border: none; border-radius: 6px; background: #022d54; color: white; cursor: pointer; font-size: 14px;"> ➕ Primary Action </button> </div> <!-- Main Content Area with Skeleton Loading --> <div style="background: white; border-radius: 8px; border: 1px solid #e0e0e0; overflow: hidden;"> <div style="padding: 1rem; border-bottom: 1px solid #e0e0e0;"> <h3 style="margin: 0; color: #022d54; font-size: 16px; font-weight: 500;">Content Loading...</h3> </div> <div style="padding: 2rem;"> <!-- Skeleton Loading Animation --> <div style="height: 20px; 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: 80%; 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: 100px; background: #e0e0e0; border-radius: 4px; animation: pulse 1.5s ease-in-out infinite;"></div> </div> </div> </div> </div> <!-- Skeleton Loading Animation --> <style> @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } } </style> `; } /** * Setup interactions - immediate setup without waiting * CUSTOMIZE THIS METHOD for your page interactions */ setupInteractions() { // Setup primary action button const primaryBtn = document.querySelector('.primary-action-btn'); if (primaryBtn) { primaryBtn.addEventListener('click', () => { console.log('Primary action clicked'); // TODO: Implement your primary action }); } // ADD YOUR OTHER INTERACTIONS HERE } /** * Initialize authentication features completely in background - NO UI BLOCKING */ async initializeAuthenticationFeaturesInBackground() { // Use requestIdleCallback for true background processing const initializeAuth = () => { try { // Ensure auth service is ready if (!this.authService) { this.initializeAuth(); } // Check authentication status without blocking if (this.authService) { this.isAuthenticated = this.authService.isUserAuthenticated(); if (this.isAuthenticated) { // Load your data in background this.loadDataInBackground(); } } } catch (error) { console.warn( 'Background authentication features initialization failed:', error ); // Continue silently - authentication is not critical for UI display } }; // Use requestIdleCallback for non-blocking background execution if (typeof requestIdleCallback !== 'undefined') { requestIdleCallback(initializeAuth, { timeout: 5000 }); } else { // Fallback for browsers without requestIdleCallback setTimeout(initializeAuth, 50); } } /** * Load data in background * CUSTOMIZE THIS METHOD for your data loading */ async loadDataInBackground() { try { console.log('Loading data in background...'); // TODO: Replace with your actual data loading // Example: // const data = await this.fetchYourData(); // this.updateUIWithData(data); // For now, just remove skeleton loading after delay setTimeout(() => { this.updateUIWithActualContent(); }, 1000); } catch (error) { console.error('Background data loading failed:', error); } } /** * Update UI with actual content (replace skeleton loading) * CUSTOMIZE THIS METHOD for your actual content */ updateUIWithActualContent() { // TODO: Replace skeleton with real content const contentArea = document.querySelector('[style*="background: white"]'); if (contentArea) { // Example of updating content - replace with your actual implementation contentArea.innerHTML = ` <div style="padding: 1rem; border-bottom: 1px solid #e0e0e0;"> <h3 style="margin: 0; color: #022d54; font-size: 16px; font-weight: 500;">Your Actual Content</h3> </div> <div style="padding: 2rem;"> <p>Your page content has loaded successfully!</p> <!-- Add your actual content structure here --> </div> `; } } /** * Show error message */ 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> `; } } } // BULLETPROOF EXPORT MECHANISM - Ensures class is always available (function () { 'use strict'; // Multiple export strategies to ensure maximum compatibility // Strategy 1: Direct window assignment (most reliable) if (typeof window !== 'undefined') { window.YourPageClass = YourPageClass; } // Strategy 2: Module exports for Node.js environments if (typeof module !== 'undefined' && module.exports) { module.exports = YourPageClass; } // Strategy 3: Global fallback if (typeof global !== 'undefined') { global.YourPageClass = YourPageClass; } // Strategy 4: AMD/RequireJS support if (typeof define === 'function' && define.amd) { define([], function () { return YourPageClass; }); } // Strategy 5: Self-executing verification setTimeout(function () { if (typeof window !== 'undefined' && !window.YourPageClass) { console.warn('YourPageClass export failed, retrying...'); window.YourPageClass = YourPageClass; } }, 10); })();