UNPKG

besper-frontend-site-dev-main

Version:

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

1,196 lines (1,064 loc) 35.1 kB
/** * Manage User Page Script - Bulletproof loading pattern * Manage user accounts and permissions */ class ManageUserPage { constructor(options = {}) { this.options = { containerId: 'besper-site-content', environment: 'prod', ...options, }; this.initialized = false; // BULLETPROOF: Defer all complex operations to avoid blocking class export this.authService = null; this.operatorsService = 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(); this.operatorsService = this.getOperatorsService(); } catch (error) { console.warn('Auth initialization failed, using fallback:', error); this.authService = this.createFallbackAuthService(); this.operatorsService = this.createFallbackOperatorsService(); } } /** * 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(); } /** * Get operators service from global scope or create simple fallback */ getOperatorsService() { // Try to access global page operators service if available if (typeof window !== 'undefined' && window.pageOperatorsService) { return window.pageOperatorsService; } return this.createFallbackOperatorsService(); } /** * 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; } }, }; } /** * Create safe fallback operators service */ createFallbackOperatorsService() { return { callOperator: async () => { console.warn( 'Operators service not available, returning empty response' ); return { success: false, message: 'Service not available' }; }, }; } /** * 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 manage user page:', error); this.showError(error); } } /** * Render immediate UI structure - shows content instantly */ renderImmediateUI() { const container = document.getElementById(this.options.containerId); if (!container) return; // Clear any existing loading indicators container.innerHTML = ''; // Show immediate manage user interface 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;">Manage User</h1> <p style="color: #6c757d; font-size: 14px; margin: 0;">Manage user accounts and permissions</p> </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;">Loading Content...</h3> </div> <div id="page-content-area" 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 */ setupInteractions() { // TODO: Add page-specific interactions here console.log('Manage User interactions ready'); } /** * Initialize authentication features completely in background - NO UI BLOCKING */ async initializeAuthenticationFeaturesInBackground() { // Use requestIdleCallback for true background processing const initializeAuth = () => { try { // Ensure services are ready if (!this.authService) { this.initializeAuth(); } // Check authentication status without blocking if (this.authService) { this.isAuthenticated = this.authService.isUserAuthenticated(); if (this.isAuthenticated) { // Load data in background this.loadDataInBackground(); } else { this.showUnauthenticatedView(); } } } 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 */ async loadDataInBackground() { try { console.log('Loading manage user data in background...'); // TODO: Replace with actual data loading // Example: // const data = await this.fetchData(); // 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) * Full-featured user management interface based on new_usermanagement.html template */ updateUIWithActualContent() { const contentArea = document.getElementById('page-content-area'); if (contentArea) { // Get user's preferred language const language = this.getCurrentLanguage(); const translations = this.getTranslations(language); contentArea.innerHTML = this.generateUserManagementHTML(translations); // Initialize user management functionality this.initializeUserManagementFeatures(translations); } } /** * Get current language (German or English) */ getCurrentLanguage() { // Check URL parameter first const urlParams = new URLSearchParams(window.location.search); const langParam = urlParams.get('lang'); if (langParam && ['en', 'de'].includes(langParam)) { return langParam; } // Check localStorage const storedLang = localStorage.getItem('bsp_language'); if (storedLang && ['en', 'de'].includes(storedLang)) { return storedLang; } // Check browser language const browserLang = navigator.language.toLowerCase(); if (browserLang.startsWith('de')) { return 'de'; } // Default to English return 'en'; } /** * Get translations for the specified language */ getTranslations(language) { const translations = { en: { title: 'User Management', subtitle: 'Manage users, permissions, and access controls', totalUsers: 'Total Users', activeUsers: 'Active Users', pendingInvites: 'Pending Invites', adminUsers: 'Admin Users', usersThisMonth: 'users this month', currentlyActive: 'currently active', awaitingResponse: 'awaiting response', fullAccess: 'full access', allUsers: 'All Users', searchUsers: 'Search users...', name: 'Name', email: 'Email', role: 'Role', lastActive: 'Last Active', status: 'Status', actions: 'Actions', noUsersFound: 'No users found', inviteUser: 'Invite User', export: 'Export', view: 'View', edit: 'Edit', remove: 'Remove', active: 'Active', inactive: 'Inactive', pending: 'Pending', admin: 'Admin', member: 'Member', viewer: 'Viewer', owner: 'Owner', }, de: { title: 'Benutzerverwaltung', subtitle: 'Benutzer, Berechtigungen und Zugriffskontrollen verwalten', totalUsers: 'Gesamte Benutzer', activeUsers: 'Aktive Benutzer', pendingInvites: 'Ausstehende Einladungen', adminUsers: 'Admin-Benutzer', usersThisMonth: 'Benutzer diesen Monat', currentlyActive: 'derzeit aktiv', awaitingResponse: 'warten auf Antwort', fullAccess: 'vollzugriff', allUsers: 'Alle Benutzer', searchUsers: 'Benutzer suchen...', name: 'Name', email: 'E-Mail', role: 'Rolle', lastActive: 'Zuletzt aktiv', status: 'Status', actions: 'Aktionen', noUsersFound: 'Keine Benutzer gefunden', inviteUser: 'Benutzer einladen', export: 'Exportieren', view: 'Anzeigen', edit: 'Bearbeiten', remove: 'Entfernen', active: 'Aktiv', inactive: 'Inaktiv', pending: 'Ausstehend', admin: 'Administrator', member: 'Mitglied', viewer: 'Betrachter', owner: 'Eigentümer', }, }; return translations[language] || translations.en; } /** * Generate the complete user management HTML interface */ generateUserManagementHTML(translations) { return ` <div class="user-management-container"> <!-- Header --> <div class="user-header"> <div class="header-left"> <h1>${translations.title}</h1> <div class="subtitle">${translations.subtitle}</div> </div> <div class="header-actions"> <select class="language-selector" onchange="window.manageUserPage.changeLanguage(this.value)"> <option value="en" ${this.getCurrentLanguage() === 'en' ? 'selected' : ''}>English</option> <option value="de" ${this.getCurrentLanguage() === 'de' ? 'selected' : ''}>Deutsch</option> </select> <button class="btn btn-secondary" onclick="window.manageUserPage.exportData()"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/> </svg> ${translations.export} </button> <button class="btn btn-primary" onclick="window.manageUserPage.showInviteUser()"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M16 21v-2a4 4 0 00-4-4H6a4 4 0 00-4 4v2M9 7a4 4 0 108 0 4 4 0 00-8 0M20 8v6M23 11h-6"/> </svg> ${translations.inviteUser} </button> </div> </div> <!-- Stats Cards --> <div class="stats-grid"> <div class="stat-card"> <div class="stat-label">${translations.totalUsers}</div> <div class="stat-value" id="totalUsers">-</div> <div class="stat-subtext">${translations.usersThisMonth}</div> </div> <div class="stat-card"> <div class="stat-label">${translations.activeUsers}</div> <div class="stat-value" id="activeUsers">-</div> <div class="stat-subtext">${translations.currentlyActive}</div> </div> <div class="stat-card"> <div class="stat-label">${translations.pendingInvites}</div> <div class="stat-value" id="pendingInvites">-</div> <div class="stat-subtext">${translations.awaitingResponse}</div> </div> <div class="stat-card"> <div class="stat-label">${translations.adminUsers}</div> <div class="stat-value" id="adminUsers">-</div> <div class="stat-subtext">${translations.fullAccess}</div> </div> </div> <!-- Users Table --> <div class="table-container"> <div class="table-header"> <div class="table-title">${translations.allUsers}</div> <input type="text" class="search-box" placeholder="${translations.searchUsers}" id="searchInput" onkeyup="window.manageUserPage.filterUsers()"> </div> <table> <thead> <tr> <th>${translations.name}</th> <th>${translations.email}</th> <th class="center">${translations.role}</th> <th class="center">${translations.lastActive}</th> <th class="center">${translations.status}</th> <th class="center">${translations.actions}</th> </tr> </thead> <tbody id="usersTableBody"> <!-- Table rows will be populated here --> </tbody> </table> <div class="empty-state" id="emptyState" style="display: none;"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M16 21v-2a4 4 0 00-4-4H6a4 4 0 00-4 4v2M9 7a4 4 0 108 0 4 4 0 00-8 0"/> </svg> <h3>${translations.noUsersFound}</h3> <p>Use the invite button to add new users to your workspace</p> </div> </div> ${this.generateUserManagementCSS()} </div> `; } /** * Generate CSS styles for the user management interface */ generateUserManagementCSS() { return ` <style> .user-management-container { max-width: 1400px; margin: 0 auto; padding: 32px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; background: #f8f9fa; color: #022d54; line-height: 1.6; } .user-header { margin-bottom: 32px; display: flex; justify-content: space-between; align-items: center; } .user-header h1 { font-size: 28px; font-weight: 300; color: #022d54; margin-bottom: 4px; letter-spacing: -0.5px; margin: 0; } .subtitle { color: #6c757d; font-size: 14px; } .header-actions { display: flex; gap: 12px; align-items: center; } .language-selector { padding: 8px 12px; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 14px; background: white; } .btn { padding: 10px 20px; border: none; border-radius: 6px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.2s; display: inline-flex; align-items: center; gap: 8px; } .btn-primary { background: #022d54; color: white; } .btn-primary:hover { background: #011831; } .btn-secondary { background: white; color: #022d54; border: 1px solid #e0e0e0; } .btn-secondary:hover { background: #f8f9fa; border-color: #6c757d; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 32px; } .stat-card { background: white; padding: 20px; border-radius: 8px; border: 1px solid #e0e0e0; transition: all 0.2s ease; } .stat-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.08); transform: translateY(-2px); } .stat-label { font-size: 12px; text-transform: uppercase; color: #6c757d; letter-spacing: 0.5px; margin-bottom: 8px; } .stat-value { font-size: 28px; font-weight: 600; color: #022d54; line-height: 1; } .stat-subtext { font-size: 12px; color: #6c757d; margin-top: 8px; } .table-container { background: white; border-radius: 8px; border: 1px solid #e0e0e0; overflow: hidden; margin-bottom: 32px; } .table-header { padding: 20px 24px; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; align-items: center; } .table-title { font-size: 18px; font-weight: 500; color: #022d54; } .search-box { padding: 8px 12px; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 14px; width: 250px; } .search-box:focus { outline: none; border-color: #022d54; } table { width: 100%; border-collapse: collapse; } thead { background: #fafbfc; } th { font-size: 12px; text-transform: uppercase; color: #6c757d; letter-spacing: 0.5px; font-weight: 600; padding: 12px 24px; text-align: left; border-bottom: 2px solid #e0e0e0; } th.center { text-align: center; } td { padding: 16px 24px; border-bottom: 1px solid #f0f0f0; font-size: 14px; } td.center { text-align: center; } tbody tr { transition: background 0.2s; cursor: pointer; } tbody tr:hover { background: #f8f9fa; } .user-name { font-weight: 500; color: #022d54; display: flex; align-items: center; gap: 12px; } .user-avatar { width: 32px; height: 32px; border-radius: 50%; background: linear-gradient(135deg, #022d54 0%, #033e6b 100%); display: flex; align-items: center; justify-content: center; color: white; font-size: 14px; font-weight: 600; } .user-email { color: #6c757d; } .role-badge { display: inline-block; padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: 500; } .role-admin { background: #e3f2fd; color: #1976d2; } .role-owner { background: #f3e5f5; color: #7b1fa2; } .role-member { background: #e8f5e8; color: #2e7d32; } .role-viewer { background: #fff3e0; color: #f57c00; } .status-badge { display: inline-block; padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: 500; } .status-active { background: #e8f5e8; color: #2e7d32; } .status-inactive { background: #f3f4f6; color: #6b7280; } .status-pending { background: #fef3c7; color: #92400e; } .actions-cell { display: flex; gap: 8px; justify-content: center; } .action-btn { width: 32px; height: 32px; border: 1px solid #e0e0e0; background: white; border-radius: 4px; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; justify-content: center; padding: 0; } .action-btn svg { width: 16px; height: 16px; stroke: #6c757d; fill: none; stroke-width: 2; } .action-btn:hover { background: #f8f9fa; border-color: #6c757d; } .action-btn.remove:hover { background: #fee2e2; border-color: #dc2626; } .action-btn.remove:hover svg { stroke: #dc2626; } .empty-state { padding: 60px 24px; text-align: center; color: #6c757d; } .empty-state svg { width: 64px; height: 64px; margin-bottom: 16px; stroke: #cbd5e0; } .empty-state h3 { font-size: 18px; font-weight: 500; margin-bottom: 8px; color: #495057; } .empty-state p { font-size: 14px; margin-bottom: 20px; } @media (max-width: 768px) { .user-management-container { padding: 16px; } .user-header { flex-direction: column; align-items: flex-start; gap: 16px; } .stats-grid { grid-template-columns: 1fr; } .search-box { width: 100%; } .user-name { flex-direction: column; align-items: flex-start; gap: 4px; } } </style> `; } /** * Initialize user management features */ initializeUserManagementFeatures(translations) { this.translations = translations; this.userData = { users: [], stats: { totalUsers: 0, activeUsers: 0, pendingInvites: 0, adminUsers: 0, }, }; // Load user data from APIM APIs this.loadUserDataFromAPIM(); } /** * Load user data from APIM administrative APIs */ async loadUserDataFromAPIM() { try { console.log( '[UserManagement] Loading user data from APIM administrative API...' ); // Get authentication token const token = this.authService?.getToken(); if (!token) { console.warn( '[UserManagement] No authentication token available, using sample data' ); this.loadSampleUserData(); return; } // TODO: Replace with actual APIM administrative API calls // const userData = await this.callAPIMApi('/admin/users', token); // const statsData = await this.callAPIMApi('/admin/users/stats', token); // For now, simulate API call with sample data setTimeout(() => { this.loadSampleUserData(); }, 1000); } catch (error) { console.error('[UserManagement] Failed to load user data:', error); this.loadSampleUserData(); } } /** * Load sample user data for demonstration */ loadSampleUserData() { this.userData = { stats: { totalUsers: 12, activeUsers: 8, pendingInvites: 3, adminUsers: 2, }, users: [ { id: 1, name: 'John Smith', email: 'john.smith@company.com', role: 'admin', lastActive: '2024-01-15', status: 'active', avatar: 'JS', }, { id: 2, name: 'Sarah Johnson', email: 'sarah.j@company.com', role: 'member', lastActive: '2024-01-14', status: 'active', avatar: 'SJ', }, { id: 3, name: 'Mike Wilson', email: 'mike.wilson@company.com', role: 'viewer', lastActive: '2024-01-10', status: 'inactive', avatar: 'MW', }, { id: 4, name: 'Lisa Brown', email: 'lisa.brown@company.com', role: 'member', lastActive: null, status: 'pending', avatar: 'LB', }, ], }; this.updateStatsDisplay(); this.renderUsers(); } /** * Update statistics display */ updateStatsDisplay() { const stats = this.userData.stats; const totalUsersEl = document.getElementById('totalUsers'); const activeUsersEl = document.getElementById('activeUsers'); const pendingInvitesEl = document.getElementById('pendingInvites'); const adminUsersEl = document.getElementById('adminUsers'); if (totalUsersEl) totalUsersEl.textContent = stats.totalUsers; if (activeUsersEl) activeUsersEl.textContent = stats.activeUsers; if (pendingInvitesEl) pendingInvitesEl.textContent = stats.pendingInvites; if (adminUsersEl) adminUsersEl.textContent = stats.adminUsers; } /** * Render users table */ renderUsers(filteredUsers = null) { const tbody = document.getElementById('usersTableBody'); const emptyState = document.getElementById('emptyState'); const dataToRender = filteredUsers || this.userData.users; if (!tbody) return; if (dataToRender.length === 0) { tbody.style.display = 'none'; if (emptyState) emptyState.style.display = 'block'; return; } tbody.style.display = ''; if (emptyState) emptyState.style.display = 'none'; tbody.innerHTML = dataToRender .map( user => ` <tr onclick="window.manageUserPage.viewUser(${user.id})"> <td> <div class="user-name"> <div class="user-avatar">${user.avatar}</div> ${user.name} </div> </td> <td> <div class="user-email">${user.email}</div> </td> <td class="center"> <span class="role-badge role-${user.role}">${this.translations[user.role] || user.role}</span> </td> <td class="center">${user.lastActive || '-'}</td> <td class="center"> <span class="status-badge status-${user.status}">${this.translations[user.status] || user.status}</span> </td> <td class="center"> <div class="actions-cell"> <button class="action-btn" onclick="event.stopPropagation(); window.manageUserPage.viewUser(${user.id})" title="${this.translations.view}"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/> <path d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/> </svg> </button> <button class="action-btn" onclick="event.stopPropagation(); window.manageUserPage.editUser(${user.id})" title="${this.translations.edit}"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/> <path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/> </svg> </button> <button class="action-btn remove" onclick="event.stopPropagation(); window.manageUserPage.removeUser(${user.id})" title="${this.translations.remove}"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/> </svg> </button> </div> </td> </tr> ` ) .join(''); } /** * Filter users based on search input */ filterUsers() { const searchTerm = document.getElementById('searchInput')?.value.toLowerCase() || ''; const filtered = this.userData.users.filter( user => user.name.toLowerCase().includes(searchTerm) || user.email.toLowerCase().includes(searchTerm) || user.role.toLowerCase().includes(searchTerm) || user.status.toLowerCase().includes(searchTerm) ); this.renderUsers(filtered); } /** * Change language */ changeLanguage(language) { localStorage.setItem('bsp_language', language); this.updateUIWithActualContent(); // Re-render with new language } /** * Export user data */ exportData() { const csvContent = 'data:text/csv;charset=utf-8,' + 'Name,Email,Role,Last Active,Status\n' + this.userData.users .map( u => `"${u.name}","${u.email}","${u.role}","${u.lastActive || 'Never'}","${u.status}"` ) .join('\n'); const encodedUri = encodeURI(csvContent); const link = document.createElement('a'); link.setAttribute('href', encodedUri); link.setAttribute('download', 'users_export.csv'); document.body.appendChild(link); link.click(); document.body.removeChild(link); } /** * Show invite user form */ showInviteUser() { console.log('Show invite user form'); // TODO: Implement invite user functionality alert('Invite user functionality - integrate with APIM administrative API'); } /** * View user details */ viewUser(userId) { const user = this.userData.users.find(u => u.id === userId); if (user) { console.log('View user:', user); // TODO: Implement user details view alert(`Viewing ${user.name} - ${user.email}`); } } /** * Edit user */ editUser(userId) { const user = this.userData.users.find(u => u.id === userId); if (user) { console.log('Edit user:', user); // TODO: Implement user editing functionality alert(`Edit ${user.name} - Role: ${user.role}`); } } /** * Remove user */ removeUser(userId) { const user = this.userData.users.find(u => u.id === userId); if (user && confirm(`Are you sure you want to remove ${user.name}?`)) { console.log('Remove user:', user); // TODO: Implement user removal via APIM administrative API alert(`Remove ${user.name} - integrate with APIM administrative API`); } } /** * Show unauthenticated view */ showUnauthenticatedView() { const contentArea = document.getElementById('page-content-area'); if (contentArea) { contentArea.innerHTML = ` <div style="text-align: center; padding: 2rem;"> <h2 style="color: #022d54;">Authentication Required</h2> <p style="color: #6c757d;">Please log in to access manage user.</p> </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;"> <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 Manage User</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.ManageUserPage = ManageUserPage; // Also make instance globally accessible for method calls window.manageUserPage = new ManageUserPage(); } // Strategy 2: Module exports for Node.js environments if (typeof module !== 'undefined' && module.exports) { module.exports = ManageUserPage; } // Strategy 3: Global fallback if (typeof global !== 'undefined') { global.ManageUserPage = ManageUserPage; } // Strategy 4: AMD/RequireJS support if (typeof define === 'function' && define.amd) { define([], function () { return ManageUserPage; }); } // Strategy 5: Self-executing verification setTimeout(function () { if (typeof window !== 'undefined' && !window.ManageUserPage) { console.warn('ManageUserPage export failed, retrying...'); window.ManageUserPage = ManageUserPage; } }, 10); })();