|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TabManager { |
|
|
constructor() { |
|
|
this.currentTab = 'market'; |
|
|
this.tabs = {}; |
|
|
this.onChangeCallbacks = []; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
init() { |
|
|
|
|
|
this.registerTab('market', 'π', 'Market', this.loadMarketTab.bind(this)); |
|
|
this.registerTab('api-monitor', 'π‘', 'API Monitor', this.loadAPIMonitorTab.bind(this)); |
|
|
this.registerTab('advanced', 'β‘', 'Advanced', this.loadAdvancedTab.bind(this)); |
|
|
this.registerTab('admin', 'βοΈ', 'Admin', this.loadAdminTab.bind(this)); |
|
|
this.registerTab('huggingface', 'π€', 'HuggingFace', this.loadHuggingFaceTab.bind(this)); |
|
|
this.registerTab('pools', 'π', 'Pools', this.loadPoolsTab.bind(this)); |
|
|
this.registerTab('providers', 'π§©', 'Providers', this.loadProvidersTab.bind(this)); |
|
|
this.registerTab('logs', 'π', 'Logs', this.loadLogsTab.bind(this)); |
|
|
this.registerTab('reports', 'π', 'Reports', this.loadReportsTab.bind(this)); |
|
|
|
|
|
|
|
|
this.setupEventListeners(); |
|
|
|
|
|
|
|
|
const hash = window.location.hash.slice(1); |
|
|
const initialTab = hash && this.tabs[hash] ? hash : 'market'; |
|
|
this.switchTab(initialTab); |
|
|
|
|
|
|
|
|
window.addEventListener('popstate', () => { |
|
|
const tabId = window.location.hash.slice(1) || 'market'; |
|
|
this.switchTab(tabId, false); |
|
|
}); |
|
|
|
|
|
console.log('[TabManager] Initialized with', Object.keys(this.tabs).length, 'tabs'); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
registerTab(id, icon, label, loadFn) { |
|
|
this.tabs[id] = { |
|
|
id, |
|
|
icon, |
|
|
label, |
|
|
loadFn, |
|
|
loaded: false, |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setupEventListeners() { |
|
|
|
|
|
document.querySelectorAll('.nav-tab-btn').forEach(btn => { |
|
|
btn.addEventListener('click', (e) => { |
|
|
e.preventDefault(); |
|
|
const tabId = btn.dataset.tab; |
|
|
if (tabId && this.tabs[tabId]) { |
|
|
this.switchTab(tabId); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
btn.addEventListener('keydown', (e) => { |
|
|
if (e.key === 'Enter' || e.key === ' ') { |
|
|
e.preventDefault(); |
|
|
const tabId = btn.dataset.tab; |
|
|
if (tabId && this.tabs[tabId]) { |
|
|
this.switchTab(tabId); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.mobile-nav-tab-btn').forEach(btn => { |
|
|
btn.addEventListener('click', (e) => { |
|
|
e.preventDefault(); |
|
|
const tabId = btn.dataset.tab; |
|
|
if (tabId && this.tabs[tabId]) { |
|
|
this.switchTab(tabId); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switchTab(tabId, updateHistory = true) { |
|
|
if (!this.tabs[tabId]) { |
|
|
console.warn(`[TabManager] Tab ${tabId} not found`); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
if (window.featureFlagsManager && this.isTabDisabled(tabId)) { |
|
|
this.showFeatureDisabledMessage(tabId); |
|
|
return; |
|
|
} |
|
|
|
|
|
console.log(`[TabManager] Switching to tab: ${tabId}`); |
|
|
|
|
|
|
|
|
document.querySelectorAll('[data-tab]').forEach(btn => { |
|
|
if (btn.dataset.tab === tabId) { |
|
|
btn.classList.add('active'); |
|
|
btn.setAttribute('aria-selected', 'true'); |
|
|
} else { |
|
|
btn.classList.remove('active'); |
|
|
btn.setAttribute('aria-selected', 'false'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.tab-content').forEach(content => { |
|
|
content.classList.remove('active'); |
|
|
content.setAttribute('aria-hidden', 'true'); |
|
|
}); |
|
|
|
|
|
|
|
|
const tabContent = document.getElementById(`${tabId}-tab`); |
|
|
if (tabContent) { |
|
|
tabContent.classList.add('active'); |
|
|
tabContent.setAttribute('aria-hidden', 'false'); |
|
|
} |
|
|
|
|
|
|
|
|
const tab = this.tabs[tabId]; |
|
|
if (!tab.loaded && tab.loadFn) { |
|
|
tab.loadFn(); |
|
|
tab.loaded = true; |
|
|
} |
|
|
|
|
|
|
|
|
if (updateHistory) { |
|
|
window.location.hash = tabId; |
|
|
} |
|
|
|
|
|
|
|
|
this.currentTab = tabId; |
|
|
|
|
|
|
|
|
this.notifyChange(tabId); |
|
|
|
|
|
|
|
|
this.announceTabChange(tab.label); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
isTabDisabled(tabId) { |
|
|
if (!window.featureFlagsManager) return false; |
|
|
|
|
|
const flagMap = { |
|
|
'market': 'enableMarketOverview', |
|
|
'huggingface': 'enableHFIntegration', |
|
|
'pools': 'enablePoolManagement', |
|
|
'advanced': 'enableAdvancedCharts', |
|
|
}; |
|
|
|
|
|
const flagName = flagMap[tabId]; |
|
|
if (flagName) { |
|
|
return !window.featureFlagsManager.isEnabled(flagName); |
|
|
} |
|
|
|
|
|
return false; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
showFeatureDisabledMessage(tabId) { |
|
|
const tab = this.tabs[tabId]; |
|
|
alert(`The "${tab.label}" feature is currently disabled. Enable it in Admin > Feature Flags.`); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
announceTabChange(label) { |
|
|
const liveRegion = document.getElementById('sr-live-region'); |
|
|
if (liveRegion) { |
|
|
liveRegion.textContent = `Switched to ${label} tab`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onChange(callback) { |
|
|
this.onChangeCallbacks.push(callback); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
notifyChange(tabId) { |
|
|
this.onChangeCallbacks.forEach(callback => { |
|
|
try { |
|
|
callback(tabId); |
|
|
} catch (error) { |
|
|
console.error('[TabManager] Error in change callback:', error); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async loadMarketTab() { |
|
|
console.log('[TabManager] Loading Market tab'); |
|
|
try { |
|
|
const marketData = await window.apiClient.getMarket(); |
|
|
this.renderMarketData(marketData); |
|
|
} catch (error) { |
|
|
console.error('[TabManager] Error loading market data:', error); |
|
|
this.showError('market-tab', 'Failed to load market data'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadAPIMonitorTab() { |
|
|
console.log('[TabManager] Loading API Monitor tab'); |
|
|
try { |
|
|
const providers = await window.apiClient.getProviders(); |
|
|
this.renderAPIMonitor(providers); |
|
|
} catch (error) { |
|
|
console.error('[TabManager] Error loading API monitor:', error); |
|
|
this.showError('api-monitor-tab', 'Failed to load API monitor data'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadAdvancedTab() { |
|
|
console.log('[TabManager] Loading Advanced tab'); |
|
|
try { |
|
|
const stats = await window.apiClient.getStats(); |
|
|
this.renderAdvanced(stats); |
|
|
} catch (error) { |
|
|
console.error('[TabManager] Error loading advanced data:', error); |
|
|
this.showError('advanced-tab', 'Failed to load advanced data'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadAdminTab() { |
|
|
console.log('[TabManager] Loading Admin tab'); |
|
|
try { |
|
|
const flags = await window.apiClient.getFeatureFlags(); |
|
|
this.renderAdmin(flags); |
|
|
} catch (error) { |
|
|
console.error('[TabManager] Error loading admin data:', error); |
|
|
this.showError('admin-tab', 'Failed to load admin data'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadHuggingFaceTab() { |
|
|
console.log('[TabManager] Loading HuggingFace tab'); |
|
|
try { |
|
|
const hfHealth = await window.apiClient.getHFHealth(); |
|
|
this.renderHuggingFace(hfHealth); |
|
|
} catch (error) { |
|
|
console.error('[TabManager] Error loading HuggingFace data:', error); |
|
|
this.showError('huggingface-tab', 'Failed to load HuggingFace data'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadPoolsTab() { |
|
|
console.log('[TabManager] Loading Pools tab'); |
|
|
try { |
|
|
const pools = await window.apiClient.getPools(); |
|
|
this.renderPools(pools); |
|
|
} catch (error) { |
|
|
console.error('[TabManager] Error loading pools data:', error); |
|
|
this.showError('pools-tab', 'Failed to load pools data'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadProvidersTab() { |
|
|
console.log('[TabManager] Loading Providers tab'); |
|
|
try { |
|
|
const providers = await window.apiClient.getProviders(); |
|
|
this.renderProviders(providers); |
|
|
} catch (error) { |
|
|
console.error('[TabManager] Error loading providers data:', error); |
|
|
this.showError('providers-tab', 'Failed to load providers data'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadLogsTab() { |
|
|
console.log('[TabManager] Loading Logs tab'); |
|
|
try { |
|
|
const logs = await window.apiClient.getRecentLogs(); |
|
|
this.renderLogs(logs); |
|
|
} catch (error) { |
|
|
console.error('[TabManager] Error loading logs:', error); |
|
|
this.showError('logs-tab', 'Failed to load logs'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadReportsTab() { |
|
|
console.log('[TabManager] Loading Reports tab'); |
|
|
try { |
|
|
const discoveryReport = await window.apiClient.getDiscoveryReport(); |
|
|
const modelsReport = await window.apiClient.getModelsReport(); |
|
|
this.renderReports({ discoveryReport, modelsReport }); |
|
|
} catch (error) { |
|
|
console.error('[TabManager] Error loading reports:', error); |
|
|
this.showError('reports-tab', 'Failed to load reports'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
renderMarketData(data) { |
|
|
if (window.dashboardApp && window.dashboardApp.renderMarketTab) { |
|
|
window.dashboardApp.renderMarketTab(data); |
|
|
} |
|
|
} |
|
|
|
|
|
renderAPIMonitor(data) { |
|
|
if (window.dashboardApp && window.dashboardApp.renderAPIMonitorTab) { |
|
|
window.dashboardApp.renderAPIMonitorTab(data); |
|
|
} |
|
|
} |
|
|
|
|
|
renderAdvanced(data) { |
|
|
if (window.dashboardApp && window.dashboardApp.renderAdvancedTab) { |
|
|
window.dashboardApp.renderAdvancedTab(data); |
|
|
} |
|
|
} |
|
|
|
|
|
renderAdmin(data) { |
|
|
if (window.dashboardApp && window.dashboardApp.renderAdminTab) { |
|
|
window.dashboardApp.renderAdminTab(data); |
|
|
} |
|
|
} |
|
|
|
|
|
renderHuggingFace(data) { |
|
|
if (window.dashboardApp && window.dashboardApp.renderHuggingFaceTab) { |
|
|
window.dashboardApp.renderHuggingFaceTab(data); |
|
|
} |
|
|
} |
|
|
|
|
|
renderPools(data) { |
|
|
if (window.dashboardApp && window.dashboardApp.renderPoolsTab) { |
|
|
window.dashboardApp.renderPoolsTab(data); |
|
|
} |
|
|
} |
|
|
|
|
|
renderProviders(data) { |
|
|
if (window.dashboardApp && window.dashboardApp.renderProvidersTab) { |
|
|
window.dashboardApp.renderProvidersTab(data); |
|
|
} |
|
|
} |
|
|
|
|
|
renderLogs(data) { |
|
|
if (window.dashboardApp && window.dashboardApp.renderLogsTab) { |
|
|
window.dashboardApp.renderLogsTab(data); |
|
|
} |
|
|
} |
|
|
|
|
|
renderReports(data) { |
|
|
if (window.dashboardApp && window.dashboardApp.renderReportsTab) { |
|
|
window.dashboardApp.renderReportsTab(data); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
showError(tabId, message) { |
|
|
const tabElement = document.getElementById(tabId); |
|
|
if (tabElement) { |
|
|
const contentArea = tabElement.querySelector('.tab-body') || tabElement; |
|
|
contentArea.innerHTML = ` |
|
|
<div class="alert alert-error"> |
|
|
<strong>β Error:</strong> ${message} |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
window.tabManager = new TabManager(); |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
window.tabManager.init(); |
|
|
}); |
|
|
|
|
|
console.log('[TabManager] Module loaded'); |
|
|
|