// MQ Admin Dashboard JavaScript
class MQAdminDashboard {
constructor() {
this.wsConnection = null;
this.isConnected = false;
this.charts = {};
this.refreshInterval = null;
this.currentTab = 'overview';
this.data = {
metrics: {},
queues: [],
consumers: [],
pools: [],
broker: {},
healthChecks: []
};
this.init();
}
init() {
this.setupEventListeners();
this.initializeCharts();
this.connectWebSocket();
this.startRefreshInterval();
this.loadInitialData();
}
setupEventListeners() {
// Tab navigation
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
this.switchTab(e.target.dataset.tab);
});
});
// Refresh button
document.getElementById('refreshBtn').addEventListener('click', () => {
this.refreshData();
});
// Modal handlers
this.setupModalHandlers();
// Broker controls
this.setupBrokerControls();
// Form handlers
this.setupFormHandlers();
}
setupModalHandlers() {
// Consumer modal
const consumerModal = document.getElementById('consumerModal');
const cancelConsumerBtn = document.getElementById('cancelConsumerConfig');
cancelConsumerBtn.addEventListener('click', () => {
consumerModal.classList.add('hidden');
});
// Pool modal
const poolModal = document.getElementById('poolModal');
const cancelPoolBtn = document.getElementById('cancelPoolConfig');
cancelPoolBtn.addEventListener('click', () => {
poolModal.classList.add('hidden');
});
// Close modals on backdrop click
[consumerModal, poolModal].forEach(modal => {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.classList.add('hidden');
}
});
});
}
setupBrokerControls() {
document.getElementById('restartBroker').addEventListener('click', () => {
this.confirmAction('restart broker', () => this.restartBroker());
});
document.getElementById('stopBroker').addEventListener('click', () => {
this.confirmAction('stop broker', () => this.stopBroker());
});
document.getElementById('flushQueues').addEventListener('click', () => {
this.confirmAction('flush all queues', () => this.flushQueues());
});
// Tasks refresh button
document.getElementById('refreshTasks').addEventListener('click', () => {
this.fetchTasks();
});
}
setupFormHandlers() {
// Consumer form
document.getElementById('consumerForm').addEventListener('submit', (e) => {
e.preventDefault();
this.updateConsumerConfig();
});
// Pool form
document.getElementById('poolForm').addEventListener('submit', (e) => {
e.preventDefault();
this.updatePoolConfig();
});
}
switchTab(tabName) {
// Update active tab button
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active');
});
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
// Show corresponding content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
content.classList.add('hidden');
});
document.getElementById(tabName).classList.remove('hidden');
document.getElementById(tabName).classList.add('active');
this.currentTab = tabName;
this.loadTabData(tabName);
}
loadTabData(tabName) {
switch (tabName) {
case 'overview':
this.loadOverviewData();
break;
case 'broker':
this.loadBrokerData();
break;
case 'queues':
this.loadQueuesData();
break;
case 'consumers':
this.loadConsumersData();
break;
case 'pools':
this.loadPoolsData();
break;
case 'tasks':
this.loadTasksData();
break;
case 'monitoring':
this.loadMonitoringData();
break;
}
}
connectWebSocket() {
try {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws`;
// Use the Socket class instead of raw WebSocket
this.wsConnection = new Socket(wsUrl, 'admin-user');
this.wsConnection.connect().then(connected => {
console.log('WebSocket connected');
this.wsConnection.onConnect(() => {
this.updateConnectionStatus(true);
this.showToast('Connected to MQ Admin', 'success');
console.log('WebSocket connected');
});
this.wsConnection.on('update', (data) => {
try {
// Data is already parsed by the Socket class
this.handleWebSocketMessage(data);
} catch (error) {
console.error('Failed to handle WebSocket message:', error);
}
});
this.wsConnection.onDisconnect(() => {
this.updateConnectionStatus(false);
this.showToast('Disconnected from MQ Admin', 'warning');
console.log('WebSocket disconnected');
// Socket class handles reconnection automatically
});
});
} catch (error) {
console.error('Failed to connect WebSocket:', error);
this.updateConnectionStatus(false);
}
}
handleWebSocketMessage(data) {
console.log('WebSocket message received:', data);
switch (data.type) {
case 'metrics':
this.updateMetrics(data.data);
break;
case 'queues':
this.updateQueues(data.data);
break;
case 'consumers':
this.updateConsumers(data.data);
break;
case 'pools':
this.updatePools(data.data);
break;
case 'broker':
this.updateBroker(data.data);
break;
case 'task_update':
this.handleTaskUpdate(data.data);
break;
case 'broker_restart':
case 'broker_stop':
case 'broker_pause':
case 'broker_resume':
this.showToast(data.data.message || 'Broker operation completed', 'info');
// Refresh broker info
this.fetchBrokerInfo();
break;
case 'consumer_pause':
case 'consumer_resume':
case 'consumer_stop':
this.showToast(data.data.message || 'Consumer operation completed', 'info');
// Refresh consumer info
this.fetchConsumers();
break;
case 'pool_pause':
case 'pool_resume':
case 'pool_stop':
this.showToast(data.data.message || 'Pool operation completed', 'info');
// Refresh pool info
this.fetchPools();
break;
case 'queue_purge':
case 'queues_flush':
this.showToast(data.data.message || 'Queue operation completed', 'info');
// Refresh queue info
this.fetchQueues();
break;
default:
console.log('Unknown WebSocket message type:', data.type);
}
}
handleTaskUpdate(taskData) {
// Add to activity feed
const activity = {
type: 'task',
message: `Task ${taskData.task_id} ${taskData.status} in queue ${taskData.queue}`,
timestamp: new Date(taskData.updated_at),
status: taskData.status === 'completed' ? 'success' : taskData.status === 'failed' ? 'error' : 'info'
};
this.addActivity(activity);
// Update metrics if on overview tab
if (this.currentTab === 'overview') {
this.fetchMetrics();
}
}
updateConnectionStatus(connected) {
this.isConnected = connected;
const indicator = document.getElementById('connectionIndicator');
const status = document.getElementById('connectionStatus');
if (connected) {
indicator.className = 'w-3 h-3 bg-green-500 rounded-full';
status.textContent = 'Connected';
} else {
indicator.className = 'w-3 h-3 bg-red-500 rounded-full';
status.textContent = 'Disconnected';
}
}
initializeCharts() {
// Throughput Chart
const throughputCtx = document.getElementById('throughputChart').getContext('2d');
this.charts.throughput = new Chart(throughputCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Messages/sec',
data: [],
borderColor: 'rgb(59, 130, 246)',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: false, // Disable animations to prevent loops
scales: {
y: {
beginAtZero: true
}
},
plugins: {
legend: {
display: false
}
}
}
});
// Queue Depth Chart
const queueDepthCtx = document.getElementById('queueDepthChart').getContext('2d');
this.charts.queueDepth = new Chart(queueDepthCtx, {
type: 'bar',
data: {
labels: [],
datasets: [{
label: 'Queue Depth',
data: [],
backgroundColor: 'rgba(16, 185, 129, 0.8)',
borderColor: 'rgb(16, 185, 129)',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true
}
},
plugins: {
legend: {
display: false
}
}
}
});
// System Performance Chart
const systemCtx = document.getElementById('systemChart').getContext('2d');
this.charts.system = new Chart(systemCtx, {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'CPU %',
data: [],
borderColor: 'rgb(239, 68, 68)',
backgroundColor: 'rgba(239, 68, 68, 0.1)',
tension: 0.4
},
{
label: 'Memory %',
data: [],
borderColor: 'rgb(245, 158, 11)',
backgroundColor: 'rgba(245, 158, 11, 0.1)',
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100
}
}
}
});
// Error Rate Chart
const errorCtx = document.getElementById('errorChart').getContext('2d');
this.charts.error = new Chart(errorCtx, {
type: 'doughnut',
data: {
labels: ['Success', 'Failed'],
datasets: [{
data: [0, 0],
backgroundColor: [
'rgba(16, 185, 129, 0.8)',
'rgba(239, 68, 68, 0.8)'
],
borderWidth: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
}
async loadInitialData() {
try {
await Promise.all([
this.fetchMetrics(),
this.fetchQueues(),
this.fetchConsumers(),
this.fetchPools(),
this.fetchBrokerInfo(),
this.fetchHealthChecks()
]);
} catch (error) {
console.error('Failed to load initial data:', error);
this.showToast('Failed to load initial data', 'error');
}
}
async fetchMetrics() {
try {
const response = await fetch('/api/admin/metrics');
const metrics = await response.json();
this.updateMetrics(metrics);
} catch (error) {
console.error('Failed to fetch metrics:', error);
}
}
async fetchQueues() {
try {
const response = await fetch('/api/admin/queues');
const queues = await response.json();
this.updateQueues(queues);
} catch (error) {
console.error('Failed to fetch queues:', error);
}
}
async fetchConsumers() {
try {
const response = await fetch('/api/admin/consumers');
const consumers = await response.json();
this.updateConsumers(consumers);
} catch (error) {
console.error('Failed to fetch consumers:', error);
}
}
async fetchPools() {
try {
const response = await fetch('/api/admin/pools');
const pools = await response.json();
this.updatePools(pools);
} catch (error) {
console.error('Failed to fetch pools:', error);
}
}
async fetchBrokerInfo() {
try {
const response = await fetch('/api/admin/broker');
const broker = await response.json();
this.updateBroker(broker);
} catch (error) {
console.error('Failed to fetch broker info:', error);
}
}
async fetchHealthChecks() {
try {
const response = await fetch('/api/admin/health');
const healthChecks = await response.json();
this.updateHealthChecks(healthChecks);
} catch (error) {
console.error('Failed to fetch health checks:', error);
}
}
async fetchTasks() {
try {
const response = await fetch('/api/admin/tasks');
const data = await response.json();
this.updateTasks(data.tasks || []);
} catch (error) {
console.error('Failed to fetch tasks:', error);
}
}
updateTasks(tasks) {
this.data.tasks = tasks;
// Update task count cards
const activeTasks = tasks.filter(task => task.status === 'queued' || task.status === 'processing').length;
const completedTasks = tasks.filter(task => task.status === 'completed').length;
const failedTasks = tasks.filter(task => task.status === 'failed').length;
const queuedTasks = tasks.filter(task => task.status === 'queued').length;
document.getElementById('activeTasks').textContent = activeTasks;
document.getElementById('completedTasks').textContent = completedTasks;
document.getElementById('failedTasks').textContent = failedTasks;
document.getElementById('queuedTasks').textContent = queuedTasks;
// Update tasks table
this.renderTasksTable(tasks);
}
renderTasksTable(tasks) {
const tbody = document.getElementById('tasksTableBody');
tbody.innerHTML = '';
if (!tasks || tasks.length === 0) {
tbody.innerHTML = `
No tasks found
|
`;
return;
}
tasks.forEach(task => {
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50';
const statusClass = this.getStatusClass(task.status);
const createdAt = this.formatTime(task.created_at);
const payload = typeof task.payload === 'string' ? task.payload : JSON.stringify(task.payload);
const truncatedPayload = payload.length > 50 ? payload.substring(0, 50) + '...' : payload;
row.innerHTML = `
${task.id}
|
${task.queue}
|
${task.status}
|
${task.retry_count || 0}
|
${createdAt}
|
${truncatedPayload}
|
`;
tbody.appendChild(row);
});
}
updateMetrics(metrics) {
this.data.metrics = metrics;
// Update overview cards
document.getElementById('totalMessages').textContent = this.formatNumber(metrics.total_messages || 0);
document.getElementById('activeConsumers').textContent = metrics.active_consumers || 0;
document.getElementById('activeQueues').textContent = metrics.active_queues || 0;
document.getElementById('failedMessages').textContent = this.formatNumber(metrics.failed_messages || 0);
// Update charts
this.updateThroughputChart(metrics.throughput_history || []);
this.updateErrorChart(metrics.success_count || 0, metrics.error_count || 0);
}
updateQueues(queues) {
this.data.queues = queues;
this.renderQueuesTable(queues);
this.updateQueueDepthChart(queues);
}
updateConsumers(consumers) {
this.data.consumers = consumers;
this.renderConsumersTable(consumers);
}
updatePools(pools) {
this.data.pools = pools;
this.renderPoolsTable(pools);
}
updateBroker(broker) {
this.data.broker = broker;
this.renderBrokerInfo(broker);
}
updateHealthChecks(healthChecks) {
this.data.healthChecks = healthChecks;
this.renderHealthChecks(healthChecks);
}
updateThroughputChart(throughputHistory) {
const chart = this.charts.throughput;
if (!chart || !throughputHistory) return;
try {
const now = new Date();
// Keep last 20 data points
chart.data.labels = throughputHistory.map((_, index) => {
const time = new Date(now.getTime() - (throughputHistory.length - index - 1) * 5000);
return time.toLocaleTimeString();
});
chart.data.datasets[0].data = throughputHistory;
chart.update('none');
} catch (error) {
console.error('Error updating throughput chart:', error);
}
}
updateQueueDepthChart(queues) {
const chart = this.charts.queueDepth;
if (!chart || !queues) return;
try {
chart.data.labels = queues.map(q => q.name);
chart.data.datasets[0].data = queues.map(q => q.depth || 0);
chart.update('none');
} catch (error) {
console.error('Error updating queue depth chart:', error);
}
}
updateErrorChart(successCount, errorCount) {
const chart = this.charts.error;
if (!chart) return;
try {
chart.data.datasets[0].data = [successCount, errorCount];
chart.update('none');
} catch (error) {
console.error('Error updating error chart:', error);
}
}
renderQueuesTable(queues) {
const tbody = document.getElementById('queuesTable');
tbody.innerHTML = '';
queues.forEach(queue => {
const row = document.createElement('tr');
row.className = 'table-row';
row.innerHTML = `
${queue.name} |
${queue.depth || 0} |
${queue.consumers || 0} |
${queue.rate || 0}/sec |
|
`;
tbody.appendChild(row);
});
}
renderConsumersTable(consumers) {
const tbody = document.getElementById('consumersTable');
tbody.innerHTML = '';
consumers.forEach(consumer => {
const row = document.createElement('tr');
row.className = 'table-row';
row.innerHTML = `
${consumer.id} |
${consumer.queue} |
${consumer.status}
|
${consumer.processed || 0} |
${consumer.errors || 0} |
|
`;
tbody.appendChild(row);
});
}
renderPoolsTable(pools) {
const tbody = document.getElementById('poolsTable');
tbody.innerHTML = '';
pools.forEach(pool => {
const row = document.createElement('tr');
row.className = 'table-row';
row.innerHTML = `
${pool.id} |
${pool.workers || 0} |
${pool.queue_size || 0} |
${pool.active_tasks || 0} |
${pool.status}
|
|
`;
tbody.appendChild(row);
});
}
renderBrokerInfo(broker) {
document.getElementById('brokerStatus').textContent = broker.status || 'Unknown';
document.getElementById('brokerStatus').className = `px-2 py-1 text-xs rounded-full ${this.getStatusClass(broker.status)}`;
document.getElementById('brokerAddress').textContent = broker.address || 'N/A';
document.getElementById('brokerUptime').textContent = this.formatDuration(broker.uptime || 0);
document.getElementById('brokerConnections').textContent = broker.connections || 0;
// Render broker configuration
this.renderBrokerConfig(broker.config || {});
}
renderBrokerConfig(config) {
const container = document.getElementById('brokerConfig');
container.innerHTML = '';
Object.entries(config).forEach(([key, value]) => {
const div = document.createElement('div');
div.className = 'flex justify-between items-center p-3 bg-gray-50 rounded';
div.innerHTML = `
${this.formatConfigKey(key)}:
${this.formatConfigValue(value)}
`;
container.appendChild(div);
});
}
renderHealthChecks(healthChecks) {
const container = document.getElementById('healthChecks');
container.innerHTML = '';
healthChecks.forEach(check => {
const div = document.createElement('div');
div.className = `health-check ${check.status}`;
div.innerHTML = `
${this.getHealthIcon(check.status)}
${check.name}
${check.message}
${this.formatDuration(check.duration)}
`;
container.appendChild(div);
});
}
addActivity(activity) {
const feed = document.getElementById('activityFeed');
const item = document.createElement('li');
item.className = 'activity-item';
item.innerHTML = `
${this.getActivityIcon(activity.type)}
${activity.title}
${activity.description}
${this.formatTime(activity.timestamp)}
`;
feed.insertBefore(item, feed.firstChild);
// Keep only last 50 items
while (feed.children.length > 50) {
feed.removeChild(feed.lastChild);
}
}
// Action methods
async pauseConsumer(consumerId) {
try {
const consumer = this.data.consumers.find(c => c.id === consumerId);
const action = consumer?.status === 'paused' ? 'resume' : 'pause';
const response = await fetch(`/api/admin/consumers/${consumerId}/${action}`, {
method: 'POST'
});
if (response.ok) {
this.showToast(`Consumer ${action}d successfully`, 'success');
this.fetchConsumers();
} else {
throw new Error(`Failed to ${action} consumer`);
}
} catch (error) {
this.showToast(error.message, 'error');
}
}
async stopConsumer(consumerId) {
this.confirmAction('stop this consumer', async () => {
try {
const response = await fetch(`/api/admin/consumers/${consumerId}/stop`, {
method: 'POST'
});
if (response.ok) {
this.showToast('Consumer stopped successfully', 'success');
this.fetchConsumers();
} else {
throw new Error('Failed to stop consumer');
}
} catch (error) {
this.showToast(error.message, 'error');
}
});
}
configureConsumer(consumerId) {
const consumer = this.data.consumers.find(c => c.id === consumerId);
if (!consumer) return;
document.getElementById('consumerIdField').value = consumer.id;
document.getElementById('maxConcurrentTasks').value = consumer.max_concurrent_tasks || 10;
document.getElementById('taskTimeout').value = consumer.task_timeout || 30;
document.getElementById('maxRetries').value = consumer.max_retries || 3;
document.getElementById('consumerModal').classList.remove('hidden');
}
async updateConsumerConfig() {
try {
const consumerId = document.getElementById('consumerIdField').value;
const config = {
max_concurrent_tasks: parseInt(document.getElementById('maxConcurrentTasks').value),
task_timeout: parseInt(document.getElementById('taskTimeout').value),
max_retries: parseInt(document.getElementById('maxRetries').value)
};
const response = await fetch(`/api/admin/consumers/${consumerId}/config`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(config)
});
if (response.ok) {
this.showToast('Consumer configuration updated', 'success');
document.getElementById('consumerModal').classList.add('hidden');
this.fetchConsumers();
} else {
throw new Error('Failed to update consumer configuration');
}
} catch (error) {
this.showToast(error.message, 'error');
}
}
async pausePool(poolId) {
try {
const pool = this.data.pools.find(p => p.id === poolId);
const action = pool?.status === 'paused' ? 'resume' : 'pause';
const response = await fetch(`/api/admin/pools/${poolId}/${action}`, {
method: 'POST'
});
if (response.ok) {
this.showToast(`Pool ${action}d successfully`, 'success');
this.fetchPools();
} else {
throw new Error(`Failed to ${action} pool`);
}
} catch (error) {
this.showToast(error.message, 'error');
}
}
async stopPool(poolId) {
this.confirmAction('stop this pool', async () => {
try {
const response = await fetch(`/api/admin/pools/${poolId}/stop`, {
method: 'POST'
});
if (response.ok) {
this.showToast('Pool stopped successfully', 'success');
this.fetchPools();
} else {
throw new Error('Failed to stop pool');
}
} catch (error) {
this.showToast(error.message, 'error');
}
});
}
configurePool(poolId) {
const pool = this.data.pools.find(p => p.id === poolId);
if (!pool) return;
document.getElementById('poolIdField').value = pool.id;
document.getElementById('numWorkers').value = pool.workers || 4;
document.getElementById('queueSize').value = pool.queue_size || 100;
document.getElementById('maxMemoryLoad').value = pool.max_memory_load || 5000000;
document.getElementById('poolModal').classList.remove('hidden');
}
async updatePoolConfig() {
try {
const poolId = document.getElementById('poolIdField').value;
const config = {
workers: parseInt(document.getElementById('numWorkers').value),
queue_size: parseInt(document.getElementById('queueSize').value),
max_memory_load: parseInt(document.getElementById('maxMemoryLoad').value)
};
const response = await fetch(`/api/admin/pools/${poolId}/config`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(config)
});
if (response.ok) {
this.showToast('Pool configuration updated', 'success');
document.getElementById('poolModal').classList.add('hidden');
this.fetchPools();
} else {
throw new Error('Failed to update pool configuration');
}
} catch (error) {
this.showToast(error.message, 'error');
}
}
async purgeQueue(queueName) {
this.confirmAction(`purge queue "${queueName}"`, async () => {
try {
const response = await fetch(`/api/admin/queues/${queueName}/purge`, {
method: 'POST'
});
if (response.ok) {
this.showToast('Queue purged successfully', 'success');
this.fetchQueues();
} else {
throw new Error('Failed to purge queue');
}
} catch (error) {
this.showToast(error.message, 'error');
}
});
}
async restartBroker() {
try {
const response = await fetch('/api/admin/broker/restart', {
method: 'POST'
});
if (response.ok) {
this.showToast('Broker restart initiated', 'success');
this.fetchBrokerInfo();
} else {
throw new Error('Failed to restart broker');
}
} catch (error) {
this.showToast(error.message, 'error');
}
}
async stopBroker() {
try {
const response = await fetch('/api/admin/broker/stop', {
method: 'POST'
});
if (response.ok) {
this.showToast('Broker stop initiated', 'warning');
this.fetchBrokerInfo();
} else {
throw new Error('Failed to stop broker');
}
} catch (error) {
this.showToast(error.message, 'error');
}
}
async flushQueues() {
try {
const response = await fetch('/api/admin/queues/flush', {
method: 'POST'
});
if (response.ok) {
this.showToast('All queues flushed', 'success');
this.fetchQueues();
} else {
throw new Error('Failed to flush queues');
}
} catch (error) {
this.showToast(error.message, 'error');
}
}
// Utility methods
confirmAction(action, callback) {
if (confirm(`Are you sure you want to ${action}?`)) {
callback();
}
}
showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.innerHTML = `
${message}
`;
document.body.appendChild(toast);
// Show toast
setTimeout(() => toast.classList.add('show'), 100);
// Auto-remove after 5 seconds
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => toast.remove(), 300);
}, 5000);
}
formatNumber(num) {
if (num >= 1000000) {
return (num / 1000000).toFixed(1) + 'M';
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K';
}
return num.toString();
}
formatDuration(ms) {
if (ms < 1000) return `${ms}ms`;
if (ms < 60000) return `${Math.floor(ms / 1000)}s`;
if (ms < 3600000) return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
const hours = Math.floor(ms / 3600000);
const minutes = Math.floor((ms % 3600000) / 60000);
return `${hours}h ${minutes}m`;
}
formatTime(timestamp) {
return new Date(timestamp).toLocaleTimeString();
}
formatConfigKey(key) {
return key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
}
formatConfigValue(value) {
if (typeof value === 'boolean') return value ? 'Yes' : 'No';
if (typeof value === 'object') return JSON.stringify(value);
return value.toString();
}
getStatusClass(status) {
switch (status?.toLowerCase()) {
case 'running':
case 'active':
case 'healthy':
return 'status-running';
case 'paused':
case 'warning':
return 'status-paused';
case 'stopped':
case 'error':
case 'failed':
return 'status-stopped';
default:
return 'status-paused';
}
}
getHealthIcon(status) {
switch (status) {
case 'healthy':
return '✓';
case 'warning':
return '⚠';
case 'error':
return '✗';
default:
return '?';
}
}
getActivityIcon(type) {
switch (type) {
case 'success':
return '✓';
case 'warning':
return '⚠';
case 'error':
return '✗';
case 'info':
default:
return 'i';
}
}
refreshData() {
this.loadInitialData();
this.showToast('Data refreshed', 'success');
}
startRefreshInterval() {
// Clear any existing interval first
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
this.refreshInterval = null;
}
// Refresh data every 15 seconds (reduced frequency to prevent overload)
this.refreshInterval = setInterval(() => {
if (this.isConnected && this.currentTab) {
try {
this.loadTabData(this.currentTab);
} catch (error) {
console.error('Error during refresh:', error);
}
}
}, 15000);
}
loadOverviewData() {
this.fetchMetrics();
}
loadBrokerData() {
this.fetchBrokerInfo();
}
loadQueuesData() {
this.fetchQueues();
}
loadConsumersData() {
this.fetchConsumers();
}
loadPoolsData() {
this.fetchPools();
}
loadTasksData() {
this.fetchTasks();
}
loadMonitoringData() {
this.fetchHealthChecks();
this.fetchMetrics();
}
}
// Initialize dashboard when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
window.adminDashboard = new MQAdminDashboard();
});