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
JavaScript
/**
* 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>
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;
}
})();