/**
* Dashboard Application Controller
* Crypto Monitor HF - Enterprise Edition
*/
class DashboardApp {
constructor() {
this.initialized = false;
this.charts = {};
this.refreshIntervals = {};
}
/**
* Initialize dashboard
*/
async init() {
if (this.initialized) return;
console.log('[Dashboard] Initializing...');
// Wait for dependencies
await this.waitForDependencies();
// Set up global error handler
this.setupErrorHandler();
// Set up refresh intervals
this.setupRefreshIntervals();
this.initialized = true;
console.log('[Dashboard] Initialized successfully');
}
/**
* Wait for required dependencies to load
*/
async waitForDependencies() {
const maxWait = 5000;
const startTime = Date.now();
while (!window.apiClient || !window.tabManager || !window.themeManager) {
if (Date.now() - startTime > maxWait) {
throw new Error('Timeout waiting for dependencies');
}
await new Promise(resolve => setTimeout(resolve, 100));
}
}
/**
* Set up global error handler
*/
setupErrorHandler() {
window.addEventListener('error', (event) => {
console.error('[Dashboard] Global error:', event.error);
});
window.addEventListener('unhandledrejection', (event) => {
console.error('[Dashboard] Unhandled rejection:', event.reason);
});
}
/**
* Set up automatic refresh intervals
*/
setupRefreshIntervals() {
// Refresh market data every 60 seconds
this.refreshIntervals.market = setInterval(() => {
if (window.tabManager.currentTab === 'market') {
window.tabManager.loadMarketTab();
}
}, 60000);
// Refresh API monitor every 30 seconds
this.refreshIntervals.apiMonitor = setInterval(() => {
if (window.tabManager.currentTab === 'api-monitor') {
window.tabManager.loadAPIMonitorTab();
}
}, 30000);
}
/**
* Clear all refresh intervals
*/
clearRefreshIntervals() {
Object.values(this.refreshIntervals).forEach(interval => {
clearInterval(interval);
});
this.refreshIntervals = {};
}
// ===== Tab Rendering Methods =====
/**
* Render Market tab
*/
renderMarketTab(data) {
const container = document.querySelector('#market-tab .tab-body');
if (!container) return;
try {
let html = '
';
// Market stats
if (data.market_cap_usd) {
html += this.createStatCard('💰', 'Market Cap', this.formatCurrency(data.market_cap_usd), 'primary');
}
if (data.total_volume_usd) {
html += this.createStatCard('📊', '24h Volume', this.formatCurrency(data.total_volume_usd), 'purple');
}
if (data.btc_dominance) {
html += this.createStatCard('₿', 'BTC Dominance', `${data.btc_dominance.toFixed(2)}%`, 'yellow');
}
if (data.active_cryptocurrencies) {
html += this.createStatCard('🪙', 'Active Coins', data.active_cryptocurrencies.toLocaleString(), 'green');
}
html += '
';
// Trending coins if available
if (data.trending && data.trending.length > 0) {
html += '';
html += this.renderTrendingCoins(data.trending);
html += '
';
}
container.innerHTML = html;
} catch (error) {
console.error('[Dashboard] Error rendering market tab:', error);
this.showError(container, 'Failed to render market data');
}
}
/**
* Render API Monitor tab
*/
renderAPIMonitorTab(data) {
const container = document.querySelector('#api-monitor-tab .tab-body');
if (!container) return;
try {
const providers = data.providers || data || [];
let html = '';
if (providers.length === 0) {
html += this.createEmptyState('No providers configured', 'Add providers in the Providers tab');
} else {
html += '
';
html += 'Provider Status Category Health Route Actions ';
html += ' ';
providers.forEach(provider => {
const status = provider.status || 'unknown';
const health = provider.health_status || provider.health || 'unknown';
const route = provider.last_route || provider.route || 'direct';
const category = provider.category || 'general';
html += '';
html += `${provider.name || provider.id} `;
html += `${this.createStatusBadge(status)} `;
html += `${category} `;
html += `${this.createHealthIndicator(health)} `;
html += `${this.createRouteBadge(route, provider.proxy_enabled)} `;
html += `Check `;
html += ' ';
});
html += '
';
}
html += '
';
container.innerHTML = html;
} catch (error) {
console.error('[Dashboard] Error rendering API monitor tab:', error);
this.showError(container, 'Failed to render API monitor data');
}
}
/**
* Render Providers tab
*/
renderProvidersTab(data) {
const container = document.querySelector('#providers-tab .tab-body');
if (!container) return;
try {
const providers = data.providers || data || [];
let html = '';
if (providers.length === 0) {
html += this.createEmptyState('No providers found', 'Configure providers to monitor APIs');
} else {
providers.forEach(provider => {
html += this.createProviderCard(provider);
});
}
html += '
';
container.innerHTML = html;
} catch (error) {
console.error('[Dashboard] Error rendering providers tab:', error);
this.showError(container, 'Failed to render providers');
}
}
/**
* Render Pools tab
*/
renderPoolsTab(data) {
const container = document.querySelector('#pools-tab .tab-body');
if (!container) return;
try {
const pools = data.pools || data || [];
let html = '+ Create Pool
';
html += '';
if (pools.length === 0) {
html += this.createEmptyState('No pools configured', 'Create a pool to manage provider groups');
} else {
pools.forEach(pool => {
html += this.createPoolCard(pool);
});
}
html += '
';
container.innerHTML = html;
} catch (error) {
console.error('[Dashboard] Error rendering pools tab:', error);
this.showError(container, 'Failed to render pools');
}
}
/**
* Render Logs tab
*/
renderLogsTab(data) {
const container = document.querySelector('#logs-tab .tab-body');
if (!container) return;
try {
const logs = data.logs || data || [];
let html = '';
if (logs.length === 0) {
html += this.createEmptyState('No logs available', 'Logs will appear here as the system runs');
} else {
html += '
';
logs.forEach(log => {
const level = log.level || 'info';
const timestamp = log.timestamp ? new Date(log.timestamp).toLocaleString() : '';
const message = log.message || '';
html += `
`;
html += `${timestamp} `;
html += `${level.toUpperCase()} `;
html += `${this.escapeHtml(message)} `;
html += `
`;
});
html += '
';
}
html += '
';
container.innerHTML = html;
} catch (error) {
console.error('[Dashboard] Error rendering logs tab:', error);
this.showError(container, 'Failed to render logs');
}
}
/**
* Render HuggingFace tab
*/
renderHuggingFaceTab(data) {
const container = document.querySelector('#huggingface-tab .tab-body');
if (!container) return;
try {
let html = '';
if (data.status === 'available' || data.available) {
html += '
✅ HuggingFace API is available
';
html += `
Models loaded: ${data.models_count || 0}
`;
html += '
Run Sentiment Analysis ';
} else {
html += '
⚠️ HuggingFace API is not available
';
if (data.error) {
html += `
${this.escapeHtml(data.error)}
`;
}
}
html += '
';
container.innerHTML = html;
} catch (error) {
console.error('[Dashboard] Error rendering HuggingFace tab:', error);
this.showError(container, 'Failed to render HuggingFace data');
}
}
/**
* Render Reports tab
*/
renderReportsTab(data) {
const container = document.querySelector('#reports-tab .tab-body');
if (!container) return;
try {
let html = '';
// Discovery Report
if (data.discoveryReport) {
html += this.renderDiscoveryReport(data.discoveryReport);
}
// Models Report
if (data.modelsReport) {
html += this.renderModelsReport(data.modelsReport);
}
container.innerHTML = html || this.createEmptyState('No reports available', 'Reports will appear here when data is available');
} catch (error) {
console.error('[Dashboard] Error rendering reports tab:', error);
this.showError(container, 'Failed to render reports');
}
}
/**
* Render Admin tab
*/
renderAdminTab(data) {
const container = document.querySelector('#admin-tab .tab-body');
if (!container) return;
try {
let html = '';
container.innerHTML = html;
// Render feature flags using the existing manager
if (window.featureFlagsManager) {
window.featureFlagsManager.renderUI('feature-flags-container');
}
} catch (error) {
console.error('[Dashboard] Error rendering admin tab:', error);
this.showError(container, 'Failed to render admin panel');
}
}
/**
* Render Advanced tab
*/
renderAdvancedTab(data) {
const container = document.querySelector('#advanced-tab .tab-body');
if (!container) return;
try {
let html = '';
html += '
' + JSON.stringify(data, null, 2) + ' ';
html += '
';
container.innerHTML = html;
} catch (error) {
console.error('[Dashboard] Error rendering advanced tab:', error);
this.showError(container, 'Failed to render advanced data');
}
}
// ===== Helper Methods =====
createStatCard(icon, label, value, variant = 'primary') {
return `
${icon}
${value}
${label}
`;
}
createStatusBadge(status) {
const statusMap = {
'online': 'success',
'offline': 'danger',
'degraded': 'warning',
'unknown': 'secondary'
};
const badgeClass = statusMap[status] || 'secondary';
return `${status} `;
}
createHealthIndicator(health) {
const healthMap = {
'healthy': { icon: '✅', class: 'provider-health-online' },
'degraded': { icon: '⚠️', class: 'provider-health-degraded' },
'unhealthy': { icon: '❌', class: 'provider-health-offline' },
'unknown': { icon: '❓', class: '' }
};
const indicator = healthMap[health] || healthMap.unknown;
return `${indicator.icon} ${health} `;
}
createRouteBadge(route, proxyEnabled) {
if (proxyEnabled || route === 'proxy') {
return '🔀 Proxy ';
}
return 'Direct ';
}
createProviderCard(provider) {
const status = provider.status || 'unknown';
const health = provider.health_status || provider.health || 'unknown';
return `
Category: ${provider.category || 'N/A'}
Health: ${this.createHealthIndicator(health)}
Endpoint: ${provider.endpoint || provider.url || 'N/A'}
`;
}
createPoolCard(pool) {
const members = pool.members || [];
return `
Strategy: ${pool.strategy || 'round-robin'}
Members: ${members.join(', ') || 'None'}
Rotate
`;
}
createEmptyState(title, description) {
return `
📭
${title}
${description}
`;
}
renderTrendingCoins(coins) {
let html = '';
coins.slice(0, 5).forEach((coin, index) => {
html += `
${index + 1} ${coin.name || coin.symbol}
`;
});
html += '
';
return html;
}
renderDiscoveryReport(report) {
return `
Enabled: ${report.enabled ? '✅ Yes' : '❌ No'}
Last Run: ${report.last_run ? new Date(report.last_run.started_at).toLocaleString() : 'Never'}
`;
}
renderModelsReport(report) {
return `
Total Models: ${report.total_models || 0}
Available: ${report.available || 0}
Errors: ${report.errors || 0}
`;
}
showError(container, message) {
container.innerHTML = `❌ ${message}
`;
}
formatCurrency(value) {
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', notation: 'compact' }).format(value);
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
getLogLevelClass(level) {
const map = { error: 'danger', warning: 'warning', info: 'primary', debug: 'secondary' };
return map[level] || 'secondary';
}
// ===== Action Handlers =====
async checkProviderHealth(providerId) {
try {
const result = await window.apiClient.checkProviderHealth(providerId);
alert(`Provider health check result: ${JSON.stringify(result)}`);
} catch (error) {
alert(`Failed to check provider health: ${error.message}`);
}
}
async clearLogs() {
if (confirm('Clear all logs?')) {
try {
await window.apiClient.clearLogs();
window.tabManager.loadLogsTab();
} catch (error) {
alert(`Failed to clear logs: ${error.message}`);
}
}
}
async runSentiment() {
try {
const result = await window.apiClient.runHFSentiment({ text: 'Bitcoin is going to the moon!' });
alert(`Sentiment result: ${JSON.stringify(result)}`);
} catch (error) {
alert(`Failed to run sentiment: ${error.message}`);
}
}
async rotatePool(poolId) {
try {
await window.apiClient.rotatePool(poolId);
window.tabManager.loadPoolsTab();
} catch (error) {
alert(`Failed to rotate pool: ${error.message}`);
}
}
createPool() {
alert('Create pool functionality - to be implemented with a modal form');
}
/**
* Cleanup
*/
destroy() {
this.clearRefreshIntervals();
Object.values(this.charts).forEach(chart => {
if (chart && chart.destroy) chart.destroy();
});
this.charts = {};
}
}
// Create global instance
window.dashboardApp = new DashboardApp();
// Auto-initialize
document.addEventListener('DOMContentLoaded', () => {
window.dashboardApp.init();
});
// Cleanup on unload
window.addEventListener('beforeunload', () => {
window.dashboardApp.destroy();
});
console.log('[Dashboard] Module loaded');