UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

450 lines (403 loc) 10.6 kB
// Atlas Dashboard Charts class ChartManager { constructor() { this.charts = {}; this.defaultOptions = { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', } }, scales: { y: { beginAtZero: true, grid: { color: '#e2e8f0' } }, x: { grid: { color: '#e2e8f0' } } } }; } createChart(canvasId, type, data, options = {}) { const canvas = document.getElementById(canvasId); if (!canvas) { console.warn(`Canvas element ${canvasId} not found`); return null; } // Destroy existing chart if it exists if (this.charts[canvasId]) { this.charts[canvasId].destroy(); } const ctx = canvas.getContext('2d'); const mergedOptions = this.mergeOptions(this.defaultOptions, options); this.charts[canvasId] = new Chart(ctx, { type, data, options: mergedOptions }); return this.charts[canvasId]; } updateChart(canvasId, newData) { const chart = this.charts[canvasId]; if (!chart) { console.warn(`Chart ${canvasId} not found`); return; } chart.data = newData; chart.update(); } destroyChart(canvasId) { if (this.charts[canvasId]) { this.charts[canvasId].destroy(); delete this.charts[canvasId]; } } mergeOptions(defaults, custom) { return { ...defaults, ...custom, plugins: { ...defaults.plugins, ...custom.plugins }, scales: { ...defaults.scales, ...custom.scales } }; } } const chartManager = new ChartManager(); // Performance Charts function updatePerformanceCharts(performanceData) { updateToolUsageChart(performanceData); updateResponseTimeChart(performanceData); updateSuccessRateChart(performanceData); updateQualityChart(performanceData); } function updateToolUsageChart(data) { // Use topPerformingTools from actual SystemMetrics const topTools = data.topPerformingTools || []; const tools = topTools.map(t => t.tool); const executions = topTools.map(t => t.calls); const chartData = { labels: tools, datasets: [{ label: 'Tool Calls', data: executions, backgroundColor: [ '#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899', '#06b6d4', '#84cc16', '#f97316', '#6366f1' ], borderWidth: 0 }] }; chartManager.createChart('toolUsageChart', 'bar', chartData, { plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, title: { display: true, text: 'Number of Calls' } } } }); } function updateResponseTimeChart(data) { // Use slowestTools from actual SystemMetrics const slowestTools = data.slowestTools || []; const tools = slowestTools.map(t => t.tool); const responseTimes = slowestTools.map(t => t.avgTime); const chartData = { labels: tools, datasets: [{ label: 'Average Response Time (ms)', data: responseTimes, backgroundColor: 'rgba(59, 130, 246, 0.6)', borderColor: '#3b82f6', borderWidth: 2, fill: true }] }; chartManager.createChart('responseTimeChart', 'line', chartData, { scales: { y: { beginAtZero: true, title: { display: true, text: 'Response Time (ms)' } } } }); } function updateSuccessRateChart(data) { // Use actual success rate from SystemMetrics const overallSuccessRate = (data.overallSuccessRate || 0) * 100; // Generate time series data - for now showing current rate across 24 hours // TODO: Implement historical data tracking in PerformanceMonitor const now = new Date(); const labels = []; const successRates = []; for (let i = 23; i >= 0; i--) { const hour = new Date(now.getTime() - i * 60 * 60 * 1000); labels.push(hour.getHours() + ':00'); // Use actual rate with minimal variation for visualization const variation = (Math.random() - 0.5) * 2; // ±1% variation successRates.push(Math.max(0, Math.min(100, overallSuccessRate + variation))); } const chartData = { labels, datasets: [{ label: 'Success Rate (%)', data: successRates, backgroundColor: 'rgba(16, 185, 129, 0.1)', borderColor: '#10b981', borderWidth: 2, fill: true, tension: 0.4 }] }; chartManager.createChart('successRateChart', 'line', chartData, { scales: { y: { beginAtZero: false, min: 80, max: 100, title: { display: true, text: 'Success Rate (%)' } } } }); } function updateQualityChart(data) { // Try to use real quality data if available, otherwise show placeholder const hasQualityData = data.qualityMetrics && data.qualityMetrics.totalEvaluations > 0; if (hasQualityData) { const metrics = data.qualityMetrics; const chartData = { labels: ['Quality', 'Appropriateness', 'Completeness', 'Accuracy'], datasets: [{ label: 'Average Scores', data: [ metrics.averageQuality, metrics.averageAppropriateness, metrics.averageCompleteness, metrics.averageAccuracy ], backgroundColor: [ '#3b82f6', '#10b981', '#f59e0b', '#8b5cf6' ], borderWidth: 0 }] }; chartManager.createChart('qualityChart', 'doughnut', chartData, { plugins: { legend: { position: 'right' } } }); } else { // Show placeholder when no quality data is available const chartData = { labels: ['No Quality Data'], datasets: [{ label: 'Evaluations needed', data: [1], backgroundColor: ['#e5e7eb'], borderWidth: 0 }] }; chartManager.createChart('qualityChart', 'doughnut', chartData, { plugins: { legend: { display: false } } }); } } // Agile Charts function updateBurndownChart(burndownData) { const { idealBurndown, actualBurndown, sprintDays } = burndownData; const labels = Array.from({ length: sprintDays + 1 }, (_, i) => `Day ${i}`); const chartData = { labels, datasets: [ { label: 'Ideal Burndown', data: idealBurndown.map(point => point.remaining), borderColor: '#6b7280', borderDash: [5, 5], borderWidth: 2, fill: false, pointRadius: 0 }, { label: 'Actual Burndown', data: actualBurndown.map(point => point.remaining), borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', borderWidth: 3, fill: true, tension: 0.2 } ] }; chartManager.createChart('burndownChart', 'line', chartData, { scales: { y: { beginAtZero: true, title: { display: true, text: 'Story Points Remaining' } }, x: { title: { display: true, text: 'Sprint Days' } } }, plugins: { legend: { position: 'top' } } }); } function updateVelocityChart(velocityData) { const { sprints } = velocityData; const labels = sprints.map(sprint => sprint.name); const planned = sprints.map(sprint => sprint.planned); const completed = sprints.map(sprint => sprint.completed); const chartData = { labels, datasets: [ { label: 'Planned Story Points', data: planned, backgroundColor: 'rgba(107, 114, 128, 0.6)', borderColor: '#6b7280', borderWidth: 1 }, { label: 'Completed Story Points', data: completed, backgroundColor: 'rgba(59, 130, 246, 0.6)', borderColor: '#3b82f6', borderWidth: 1 } ] }; chartManager.createChart('velocityChart', 'bar', chartData, { scales: { y: { beginAtZero: true, title: { display: true, text: 'Story Points' } } }, plugins: { legend: { position: 'top' } } }); } // Error Charts function updateErrorCharts(errorData) { updateErrorTrendsChart(errorData); updateErrorTypesChart(errorData); } function updateErrorTrendsChart(data) { const { dataPoints } = data; const labels = dataPoints.map(point => { const date = new Date(point.timestamp); return date.getHours() + ':' + date.getMinutes().toString().padStart(2, '0'); }); const errorCounts = dataPoints.map(point => point.errorCount); const chartData = { labels, datasets: [{ label: 'Error Count', data: errorCounts, backgroundColor: 'rgba(239, 68, 68, 0.1)', borderColor: '#ef4444', borderWidth: 2, fill: true, tension: 0.4 }] }; chartManager.createChart('errorTrendsChart', 'line', chartData, { scales: { y: { beginAtZero: true, title: { display: true, text: 'Number of Errors' } } } }); } function updateErrorTypesChart(data) { // Mock error type distribution const errorTypes = ['validation', 'network', 'permission', 'resource', 'logic']; const errorCounts = [12, 8, 5, 3, 7]; // Mock data const chartData = { labels: errorTypes.map(type => type.charAt(0).toUpperCase() + type.slice(1)), datasets: [{ data: errorCounts, backgroundColor: [ '#ef4444', '#f59e0b', '#eab308', '#06b6d4', '#8b5cf6' ], borderWidth: 0 }] }; chartManager.createChart('errorTypesChart', 'pie', chartData, { plugins: { legend: { position: 'bottom' } } }); } // Chart theme helpers function getChartColors() { return { primary: '#3b82f6', success: '#10b981', warning: '#f59e0b', error: '#ef4444', info: '#06b6d4', purple: '#8b5cf6', pink: '#ec4899', gray: '#6b7280' }; } function createGradient(ctx, color) { const gradient = ctx.createLinearGradient(0, 0, 0, 300); gradient.addColorStop(0, color + '40'); // 25% opacity gradient.addColorStop(1, color + '00'); // 0% opacity return gradient; } // Export functions for global access window.updatePerformanceCharts = updatePerformanceCharts; window.updateBurndownChart = updateBurndownChart; window.updateVelocityChart = updateVelocityChart; window.updateErrorCharts = updateErrorCharts; window.chartManager = chartManager;