Code Quality Dashboard

Code Quality Dashboard

Track and visualize key code quality metrics for your projects.

Project Coverage Overview

Issues Breakdown

Projects Summary

Project Coverage Bugs Vulnerabilities Code Smells Duplication

${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() { 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); // Destroy existing charts before re-rendering if (charts.coverage) charts.coverage.destroy(); if (charts.issues) charts.issues.destroy(); // 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' } } } }); } } 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(); // Only need to re-render dashboard } } }; 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; // Temporarily hide the download button so it's not in the PDF pdfButtonContainer.style.display = 'none'; try { const canvas = await html2canvas(dashboardContent, { scale: 2, // Higher scale for better quality useCORS: true, logging: false, backgroundColor: '#f9fafb' // Match body background }); 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); // In a real app, show a user-friendly error message here } finally { // Show the button again pdfButtonContainer.style.display = 'block'; } }; function attachEventListeners() { // Event listeners for tabs and nav buttons are handled by inline onclick // This is sufficient for this self-contained tool as per spec. } // --- START THE APP --- init(); });
Scroll to Top