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