Code Quality Dashboard

Code Quality Dashboard

Track and visualize key code quality metrics for your projects.

Project Coverage Overview

Issues Breakdown

Project Health Overview

Projects Summary

Project Coverage Bugs Vulnerabilities Code Smells Duplication

${totalProjects}

`; document.getElementById('summary-card-coverage').innerHTML = `

Avg. Coverage

${avgCoverage}%

`; document.getElementById('summary-card-bugs').innerHTML = `

Total Bugs

${totalBugs}

`; document.getElementById('summary-card-vulnerabilities').innerHTML = `

Vulnerabilities

${totalVulnerabilities}

`; } function renderProjectSummaryTable() { const tableBody = document.getElementById('project-summary-table'); if (!tableBody) return; tableBody.innerHTML = projects.map(p => ` ${p.name} ${p.coverage}% ${p.bugs} ${p.vulnerabilities} ${p.smells} ${p.duplication}% `).join(''); } function renderCharts() { // Destroy existing charts before re-rendering to prevent issues if (charts.coverage) charts.coverage.destroy(); if (charts.issues) charts.issues.destroy(); if (charts.bubble) charts.bubble.destroy(); const projectNames = projects.map(p => p.name); const coverageData = projects.map(p => p.coverage); const totalBugs = projects.reduce((sum, p) => sum + p.bugs, 0); const totalVulnerabilities = projects.reduce((sum, p) => sum + p.vulnerabilities, 0); const totalSmells = projects.reduce((sum, p) => sum + p.smells, 0); // Coverage Chart (Bar) const coverageCtx = document.getElementById('coverageChart')?.getContext('2d'); if (coverageCtx) { charts.coverage = new Chart(coverageCtx, { type: 'bar', data: { labels: projectNames, datasets: [{ label: 'Code Coverage (%)', data: coverageData, backgroundColor: 'rgba(59, 130, 246, 0.6)', borderColor: 'rgba(59, 130, 246, 1)', borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, max: 100 } }, plugins: { legend: { display: false } } } }); } // Issues Chart (Doughnut) const issuesCtx = document.getElementById('issuesChart')?.getContext('2d'); if (issuesCtx) { charts.issues = new Chart(issuesCtx, { type: 'doughnut', data: { labels: ['Bugs', 'Vulnerabilities', 'Code Smells'], datasets: [{ label: 'Issues Breakdown', data: [totalBugs, totalVulnerabilities, totalSmells], backgroundColor: ['rgba(239, 68, 68, 0.7)', 'rgba(245, 158, 11, 0.7)', 'rgba(107, 114, 128, 0.7)'], borderColor: ['rgba(239, 68, 68, 1)', 'rgba(245, 158, 11, 1)', 'rgba(107, 114, 128, 1)'], borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' } } } }); } // Bubble Chart const bubbleCtx = document.getElementById('bubbleChart')?.getContext('2d'); if (bubbleCtx) { const bubbleDatasets = projects.map((p, index) => { const colors = [ 'rgba(59, 130, 246, 0.7)', 'rgba(239, 68, 68, 0.7)', 'rgba(16, 185, 129, 0.7)', 'rgba(245, 158, 11, 0.7)', 'rgba(139, 92, 246, 0.7)', 'rgba(236, 72, 153, 0.7)' ]; const color = colors[index % colors.length]; const totalIssues = p.bugs + p.vulnerabilities + p.smells; // Scale radius for better visualization const radius = Math.max(5, Math.sqrt(totalIssues) * 2.5); return { label: p.name, data: [{ x: p.coverage, y: p.duplication, r: radius }], backgroundColor: color, borderColor: color.replace('0.7', '1'), borderWidth: 1 }; }); charts.bubble = new Chart(bubbleCtx, { type: 'bubble', data: { datasets: bubbleDatasets }, options: { responsive: true, maintainAspectRatio: false, scales: { x: { title: { display: true, text: 'Code Coverage (%)' }, min: 0, max: 100 }, y: { title: { display: true, text: 'Code Duplication (%)' }, beginAtZero: true } }, plugins: { legend: { position: 'top' }, tooltip: { callbacks: { label: function(context) { const datasetLabel = context.dataset.label || ''; const projectIndex = context.datasetIndex; const project = projects[projectIndex]; const totalIssues = project.bugs + project.vulnerabilities + project.smells; return `${datasetLabel}: (Coverage: ${context.parsed.x}%, Duplication: ${context.parsed.y}%, Issues: ${totalIssues})`; } } } } } }); } } function renderConfigTable() { const tableBody = document.getElementById('config-table'); if (!tableBody) return; tableBody.innerHTML = projects.map((p, index) => ` `).join(''); } // --- EVENT HANDLING & ACTIONS --- window.switchTab = (tabName) => { activeTab = tabName; updateTabStyles(); updateNavButtons(); }; function updateTabStyles() { const dashboardTab = document.getElementById('tab-content-dashboard'); const configTab = document.getElementById('tab-content-config'); const dashboardBtn = document.getElementById('tab-btn-dashboard'); const configBtn = document.getElementById('tab-btn-config'); if (activeTab === 'dashboard') { dashboardTab.classList.remove('hidden'); configTab.classList.add('hidden'); dashboardBtn.classList.add('tab-active'); dashboardBtn.classList.remove('tab-inactive'); configBtn.classList.add('tab-inactive'); configBtn.classList.remove('tab-active'); } else { dashboardTab.classList.add('hidden'); configTab.classList.remove('hidden'); dashboardBtn.classList.add('tab-inactive'); dashboardBtn.classList.remove('tab-active'); configBtn.classList.add('tab-active'); configBtn.classList.remove('tab-inactive'); } } window.navigateTabs = (direction) => { if (direction === 'next' && activeTab === 'dashboard') { switchTab('config'); } else if (direction === 'prev' && activeTab === 'config') { switchTab('dashboard'); } }; function updateNavButtons() { const prevBtn = document.getElementById('prev-btn'); const nextBtn = document.getElementById('next-btn'); if (activeTab === 'dashboard') { prevBtn.disabled = true; prevBtn.classList.add('opacity-50', 'cursor-not-allowed'); nextBtn.disabled = false; nextBtn.classList.remove('opacity-50', 'cursor-not-allowed'); } else { prevBtn.disabled = false; prevBtn.classList.remove('opacity-50', 'cursor-not-allowed'); nextBtn.disabled = true; nextBtn.classList.add('opacity-50', 'cursor-not-allowed'); } } window.addProject = () => { const newId = projects.length > 0 ? Math.max(...projects.map(p => p.id)) + 1 : 1; projects.push({ id: newId, name: 'New Project', coverage: 0, bugs: 0, vulnerabilities: 0, smells: 0, duplication: 0 }); saveData(); updateUI(); }; window.updateProject = (index, field, value) => { if (projects[index]) { const parsedValue = (field === 'name') ? value : parseFloat(value); if (!isNaN(parsedValue) || field === 'name') { projects[index][field] = parsedValue; saveData(); renderDashboard(); // Re-render dashboard to reflect changes } } }; window.deleteProject = (index) => { projects.splice(index, 1); saveData(); updateUI(); }; window.downloadPDF = async () => { const { jsPDF } = window.jspdf; const dashboardContent = document.getElementById('dashboard-pdf-content'); const pdfButtonContainer = document.getElementById('pdf-button-container'); if (!dashboardContent || !pdfButtonContainer) return; pdfButtonContainer.style.display = 'none'; // Hide button for capture try { const canvas = await html2canvas(dashboardContent, { scale: 2, useCORS: true, logging: false, backgroundColor: '#f9fafb' }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'landscape', unit: 'px', format: [canvas.width, canvas.height] }); pdf.addImage(imgData, 'PNG', 0, 0, canvas.width, canvas.height); pdf.save('Code_Quality_Dashboard.pdf'); } catch (error) { console.error("Failed to generate PDF:", error); } finally { pdfButtonContainer.style.display = 'block'; // Show button again } }; // --- START THE APP --- init(); });
Scroll to Top