Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>CodeMetrics | Source Code Analysis Tool</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
<style> | |
.file-drop-area { | |
border: 2px dashed #3b82f6; | |
border-radius: 0.5rem; | |
transition: all 0.3s ease; | |
} | |
.file-drop-area:hover { | |
background-color: rgba(59, 130, 246, 0.1); | |
} | |
.file-drop-area.active { | |
border-color: #10b981; | |
background-color: rgba(16, 185, 129, 0.1); | |
} | |
.metric-card { | |
transition: transform 0.3s ease, box-shadow 0.3s ease; | |
} | |
.metric-card:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); | |
} | |
.code-block { | |
font-family: 'Courier New', Courier, monospace; | |
background-color: #1e293b; | |
color: #f8fafc; | |
border-radius: 0.375rem; | |
overflow-x: auto; | |
} | |
.line-number { | |
color: #64748b; | |
user-select: none; | |
padding-right: 1rem; | |
} | |
.issue-marker { | |
background-color: rgba(239, 68, 68, 0.2); | |
border-left: 3px solid #ef4444; | |
} | |
.nav-link { | |
position: relative; | |
} | |
.nav-link::after { | |
content: ''; | |
position: absolute; | |
bottom: -2px; | |
left: 0; | |
width: 0; | |
height: 2px; | |
background-color: #3b82f6; | |
transition: width 0.3s ease; | |
} | |
.nav-link:hover::after { | |
width: 100%; | |
} | |
.nav-link.active::after { | |
width: 100%; | |
} | |
</style> | |
</head> | |
<body class="bg-gray-50 min-h-screen"> | |
<div class="flex flex-col min-h-screen"> | |
<!-- Header --> | |
<header class="bg-white shadow-sm"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4"> | |
<div class="flex justify-between items-center"> | |
<div class="flex items-center space-x-4"> | |
<div class="bg-blue-500 text-white p-2 rounded-lg"> | |
<i class="fas fa-code text-xl"></i> | |
</div> | |
<h1 class="text-2xl font-bold text-gray-800">CodeMetrics</h1> | |
</div> | |
<nav class="hidden md:flex space-x-8"> | |
<a href="#" class="nav-link active text-blue-500 font-medium">Dashboard</a> | |
<a href="#" class="nav-link text-gray-600 hover:text-blue-500 font-medium">Projects</a> | |
<a href="#" class="nav-link text-gray-600 hover:text-blue-500 font-medium">Issues</a> | |
<a href="#" class="nav-link text-gray-600 hover:text-blue-500 font-medium">Rules</a> | |
<a href="#" class="nav-link text-gray-600 hover:text-blue-500 font-medium">Quality Gates</a> | |
</nav> | |
<div class="flex items-center space-x-4"> | |
<button class="p-2 text-gray-500 hover:text-blue-500"> | |
<i class="fas fa-bell"></i> | |
</button> | |
<div class="w-8 h-8 rounded-full bg-blue-500 text-white flex items-center justify-center"> | |
<span>AD</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</header> | |
<!-- Main Content --> | |
<main class="flex-grow max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> | |
<div class="mb-8"> | |
<h2 class="text-2xl font-bold text-gray-800 mb-2">Analyze Your Code</h2> | |
<p class="text-gray-600">Upload your source code files or connect to a repository to analyze code quality metrics.</p> | |
</div> | |
<!-- File Upload Section --> | |
<div class="bg-white rounded-xl shadow-sm p-6 mb-8"> | |
<div id="fileDropArea" class="file-drop-area p-12 text-center cursor-pointer"> | |
<div class="flex flex-col items-center justify-center space-y-4"> | |
<div class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center text-blue-500"> | |
<i class="fas fa-cloud-upload-alt text-3xl"></i> | |
</div> | |
<h3 class="text-lg font-medium text-gray-800">Drag & drop your files here</h3> | |
<p class="text-gray-500">or click to browse files</p> | |
<input type="file" id="fileInput" class="hidden" multiple accept=".js,.java,.py,.php,.html,.css,.ts,.go,.rb,.cs"> | |
<button id="browseBtn" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition"> | |
Browse Files | |
</button> | |
</div> | |
</div> | |
<div id="fileList" class="mt-4 hidden"> | |
<h4 class="font-medium text-gray-700 mb-2">Selected Files:</h4> | |
<ul id="selectedFiles" class="space-y-2"></ul> | |
</div> | |
<div class="mt-6 flex justify-end"> | |
<button id="analyzeBtn" class="px-6 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 transition disabled:opacity-50 disabled:cursor-not-allowed" disabled> | |
<i class="fas fa-play mr-2"></i> Analyze Code | |
</button> | |
</div> | |
</div> | |
<!-- Analysis Results (Initially Hidden) --> | |
<div id="analysisResults" class="hidden"> | |
<!-- Project Overview --> | |
<div class="bg-white rounded-xl shadow-sm p-6 mb-8"> | |
<div class="flex justify-between items-center mb-6"> | |
<h3 class="text-xl font-bold text-gray-800">Project Analysis Summary</h3> | |
<div class="flex space-x-4"> | |
<button class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"> | |
<i class="fas fa-download mr-2"></i> Export Report | |
</button> | |
<button class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"> | |
<i class="fas fa-share-alt mr-2"></i> Share | |
</button> | |
</div> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8"> | |
<!-- Lines of Code --> | |
<div class="metric-card bg-white border border-gray-200 rounded-lg p-6 shadow-sm"> | |
<div class="flex items-center justify-between"> | |
<div> | |
<p class="text-sm font-medium text-gray-500">Lines of Code</p> | |
<h3 class="text-2xl font-bold text-gray-800 mt-1" id="locValue">0</h3> | |
</div> | |
<div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center text-blue-500"> | |
<i class="fas fa-code"></i> | |
</div> | |
</div> | |
<div class="mt-4"> | |
<div class="h-2 bg-gray-200 rounded-full overflow-hidden"> | |
<div class="h-full bg-blue-500 rounded-full" style="width: 70%"></div> | |
</div> | |
</div> | |
</div> | |
<!-- Complexity --> | |
<div class="metric-card bg-white border border-gray-200 rounded-lg p-6 shadow-sm"> | |
<div class="flex items-center justify-between"> | |
<div> | |
<p class="text-sm font-medium text-gray-500">Complexity</p> | |
<h3 class="text-2xl font-bold text-gray-800 mt-1" id="complexityValue">0</h3> | |
</div> | |
<div class="w-12 h-12 bg-purple-100 rounded-full flex items-center justify-center text-purple-500"> | |
<i class="fas fa-project-diagram"></i> | |
</div> | |
</div> | |
<div class="mt-4"> | |
<div class="h-2 bg-gray-200 rounded-full overflow-hidden"> | |
<div class="h-full bg-purple-500 rounded-full" style="width: 45%"></div> | |
</div> | |
</div> | |
</div> | |
<!-- Issues --> | |
<div class="metric-card bg-white border border-gray-200 rounded-lg p-6 shadow-sm"> | |
<div class="flex items-center justify-between"> | |
<div> | |
<p class="text-sm font-medium text-gray-500">Issues</p> | |
<h3 class="text-2xl font-bold text-gray-800 mt-1" id="issuesValue">0</h3> | |
</div> | |
<div class="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center text-red-500"> | |
<i class="fas fa-exclamation-triangle"></i> | |
</div> | |
</div> | |
<div class="mt-4"> | |
<div class="h-2 bg-gray-200 rounded-full overflow-hidden"> | |
<div class="h-full bg-red-500 rounded-full" style="width: 30%"></div> | |
</div> | |
</div> | |
</div> | |
<!-- Code Smells --> | |
<div class="metric-card bg-white border border-gray-200 rounded-lg p-6 shadow-sm"> | |
<div class="flex items-center justify-between"> | |
<div> | |
<p class="text-sm font-medium text-gray-500">Code Smells</p> | |
<h3 class="text-2xl font-bold text-gray-800 mt-1" id="smellsValue">0</h3> | |
</div> | |
<div class="w-12 h-12 bg-yellow-100 rounded-full flex items-center justify-center text-yellow-500"> | |
<i class="fas fa-bug"></i> | |
</div> | |
</div> | |
<div class="mt-4"> | |
<div class="h-2 bg-gray-200 rounded-full overflow-hidden"> | |
<div class="h-full bg-yellow-500 rounded-full" style="width: 60%"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Charts --> | |
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8"> | |
<div class="bg-white border border-gray-200 rounded-lg p-6 shadow-sm"> | |
<h4 class="font-medium text-gray-700 mb-4">Code Metrics Distribution</h4> | |
<canvas id="metricsChart" height="250"></canvas> | |
</div> | |
<div class="bg-white border border-gray-200 rounded-lg p-6 shadow-sm"> | |
<h4 class="font-medium text-gray-700 mb-4">Issue Severity</h4> | |
<canvas id="issuesChart" height="250"></canvas> | |
</div> | |
</div> | |
</div> | |
<!-- File Analysis Details --> | |
<div class="bg-white rounded-xl shadow-sm p-6 mb-8"> | |
<h3 class="text-xl font-bold text-gray-800 mb-6">File Analysis Details</h3> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full divide-y divide-gray-200"> | |
<thead class="bg-gray-50"> | |
<tr> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">File</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">LOC</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Complexity</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Issues</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Smells</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Coverage</th> | |
</tr> | |
</thead> | |
<tbody id="fileAnalysisTable" class="bg-white divide-y divide-gray-200"> | |
<!-- Files will be added here dynamically --> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<!-- Code Viewer with Issues --> | |
<div class="bg-white rounded-xl shadow-sm p-6"> | |
<h3 class="text-xl font-bold text-gray-800 mb-6">Code Review</h3> | |
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
<!-- File Tree --> | |
<div class="lg:col-span-1 bg-gray-50 rounded-lg p-4"> | |
<h4 class="font-medium text-gray-700 mb-4">Files</h4> | |
<div class="space-y-2" id="fileTree"> | |
<!-- Files will be added here dynamically --> | |
</div> | |
</div> | |
<!-- Code Viewer --> | |
<div class="lg:col-span-2"> | |
<div class="flex items-center justify-between mb-4"> | |
<h4 class="font-medium text-gray-700" id="currentFileTitle">Select a file to review</h4> | |
<div class="flex space-x-2"> | |
<button class="p-2 text-gray-500 hover:text-blue-500"> | |
<i class="fas fa-expand"></i> | |
</button> | |
<button class="p-2 text-gray-500 hover:text-blue-500"> | |
<i class="fas fa-copy"></i> | |
</button> | |
</div> | |
</div> | |
<div class="code-block p-4 max-h-96 overflow-y-auto" id="codeViewer"> | |
<div class="text-center text-gray-500 py-12"> | |
<i class="fas fa-file-code text-4xl mb-3"></i> | |
<p>No file selected</p> | |
</div> | |
</div> | |
<!-- Issues List --> | |
<div class="mt-6"> | |
<h4 class="font-medium text-gray-700 mb-4">Issues</h4> | |
<div class="space-y-3" id="issuesList"> | |
<div class="text-center text-gray-500 py-8"> | |
<i class="fas fa-check-circle text-3xl mb-2"></i> | |
<p>No issues found for this file</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</main> | |
<!-- Footer --> | |
<footer class="bg-white border-t border-gray-200"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> | |
<div class="md:flex md:items-center md:justify-between"> | |
<div class="flex justify-center md:justify-start space-x-6"> | |
<a href="#" class="text-gray-400 hover:text-gray-500"> | |
<i class="fab fa-github"></i> | |
</a> | |
<a href="#" class="text-gray-400 hover:text-gray-500"> | |
<i class="fab fa-twitter"></i> | |
</a> | |
<a href="#" class="text-gray-400 hover:text-gray-500"> | |
<i class="fab fa-linkedin"></i> | |
</a> | |
</div> | |
<div class="mt-4 md:mt-0 text-center md:text-right"> | |
<p class="text-sm text-gray-500">© 2023 CodeMetrics. All rights reserved.</p> | |
</div> | |
</div> | |
</div> | |
</footer> | |
</div> | |
<script> | |
// DOM Elements | |
const fileDropArea = document.getElementById('fileDropArea'); | |
const fileInput = document.getElementById('fileInput'); | |
const browseBtn = document.getElementById('browseBtn'); | |
const fileList = document.getElementById('fileList'); | |
const selectedFiles = document.getElementById('selectedFiles'); | |
const analyzeBtn = document.getElementById('analyzeBtn'); | |
const analysisResults = document.getElementById('analysisResults'); | |
// Metrics elements | |
const locValue = document.getElementById('locValue'); | |
const complexityValue = document.getElementById('complexityValue'); | |
const issuesValue = document.getElementById('issuesValue'); | |
const smellsValue = document.getElementById('smellsValue'); | |
// Table elements | |
const fileAnalysisTable = document.getElementById('fileAnalysisTable'); | |
const fileTree = document.getElementById('fileTree'); | |
const codeViewer = document.getElementById('codeViewer'); | |
const currentFileTitle = document.getElementById('currentFileTitle'); | |
const issuesList = document.getElementById('issuesList'); | |
// Sample data for analysis | |
const sampleFiles = [ | |
{ | |
name: 'app.js', | |
loc: 243, | |
complexity: 32, | |
issues: 5, | |
smells: 12, | |
coverage: '78%', | |
content: `// Main application file | |
const express = require('express'); | |
const app = express(); | |
const path = require('path'); | |
// Configuration | |
app.set('view engine', 'ejs'); | |
app.set('views', path.join(__dirname, 'views')); | |
// Middleware | |
app.use(express.json()); | |
app.use(express.urlencoded({ extended: true })); | |
app.use(express.static(path.join(__dirname, 'public'))); | |
// Routes | |
app.get('/', (req, res) => { | |
res.render('index', { title: 'Home' }); | |
}); | |
app.get('/about', (req, res) => { | |
res.render('about', { title: 'About' }); | |
}); | |
// Error handling middleware | |
app.use((err, req, res, next) => { | |
console.error(err.stack); | |
res.status(500).send('Something broke!'); | |
}); | |
// Start server | |
const PORT = process.env.PORT || 3000; | |
app.listen(PORT, () => { | |
console.log(\`Server running on port \${PORT}\`); | |
});`, | |
issues: [ | |
{ | |
line: 5, | |
severity: 'major', | |
message: 'Avoid using path.join with __dirname', | |
type: 'security' | |
}, | |
{ | |
line: 15, | |
severity: 'minor', | |
message: 'Route handler should be in a separate file', | |
type: 'maintainability' | |
}, | |
{ | |
line: 19, | |
severity: 'info', | |
message: 'Consider adding input validation', | |
type: 'reliability' | |
} | |
] | |
}, | |
{ | |
name: 'utils.js', | |
loc: 87, | |
complexity: 15, | |
issues: 2, | |
smells: 4, | |
coverage: '92%', | |
content: `// Utility functions | |
function formatDate(dateString) { | |
const date = new Date(dateString); | |
return date.toLocaleDateString('en-US', { | |
year: 'numeric', | |
month: 'long', | |
day: 'numeric' | |
}); | |
} | |
function calculateAge(birthDate) { | |
const today = new Date(); | |
const birth = new Date(birthDate); | |
let age = today.getFullYear() - birth.getFullYear(); | |
const monthDiff = today.getMonth() - birth.getMonth(); | |
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) { | |
age--; | |
} | |
return age; | |
} | |
function debounce(func, delay) { | |
let timeoutId; | |
return function(...args) { | |
clearTimeout(timeoutId); | |
timeoutId = setTimeout(() => { | |
func.apply(this, args); | |
}, delay); | |
}; | |
} | |
module.exports = { | |
formatDate, | |
calculateAge, | |
debounce | |
};`, | |
issues: [ | |
{ | |
line: 2, | |
severity: 'critical', | |
message: 'No validation for invalid date strings', | |
type: 'reliability' | |
}, | |
{ | |
line: 23, | |
severity: 'minor', | |
message: 'Consider using a library for debounce functionality', | |
type: 'maintainability' | |
} | |
] | |
} | |
]; | |
// File handling | |
let uploadedFiles = []; | |
// Event Listeners | |
browseBtn.addEventListener('click', () => fileInput.click()); | |
fileInput.addEventListener('change', handleFileSelect); | |
fileDropArea.addEventListener('dragover', (e) => { | |
e.preventDefault(); | |
fileDropArea.classList.add('active'); | |
}); | |
fileDropArea.addEventListener('dragleave', () => { | |
fileDropArea.classList.remove('active'); | |
}); | |
fileDropArea.addEventListener('drop', (e) => { | |
e.preventDefault(); | |
fileDropArea.classList.remove('active'); | |
fileInput.files = e.dataTransfer.files; | |
handleFileSelect({ target: fileInput }); | |
}); | |
analyzeBtn.addEventListener('click', analyzeFiles); | |
// Functions | |
function handleFileSelect(e) { | |
const files = Array.from(e.target.files); | |
if (files.length === 0) return; | |
uploadedFiles = files; | |
updateFileList(); | |
analyzeBtn.disabled = false; | |
} | |
function updateFileList() { | |
selectedFiles.innerHTML = ''; | |
uploadedFiles.forEach(file => { | |
const li = document.createElement('li'); | |
li.className = 'flex items-center justify-between bg-gray-50 p-3 rounded-md'; | |
li.innerHTML = ` | |
<div class="flex items-center space-x-3"> | |
<i class="fas fa-file-code text-blue-500"></i> | |
<span>${file.name}</span> | |
</div> | |
<span class="text-sm text-gray-500">${formatFileSize(file.size)}</span> | |
`; | |
selectedFiles.appendChild(li); | |
}); | |
fileList.classList.remove('hidden'); | |
} | |
function formatFileSize(bytes) { | |
if (bytes === 0) return '0 Bytes'; | |
const k = 1024; | |
const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | |
} | |
function analyzeFiles() { | |
// Show loading state | |
analyzeBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Analyzing...'; | |
analyzeBtn.disabled = true; | |
// Simulate analysis with timeout | |
setTimeout(() => { | |
// For demo purposes, we'll use sample data | |
displayAnalysisResults(sampleFiles); | |
// Reset button | |
analyzeBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Analysis Complete'; | |
// Show results | |
analysisResults.classList.remove('hidden'); | |
// Scroll to results | |
analysisResults.scrollIntoView({ behavior: 'smooth' }); | |
}, 1500); | |
} | |
function displayAnalysisResults(files) { | |
// Calculate totals | |
const totals = { | |
loc: files.reduce((sum, file) => sum + file.loc, 0), | |
complexity: files.reduce((sum, file) => sum + file.complexity, 0), | |
issues: files.reduce((sum, file) => sum + file.issues, 0), | |
smells: files.reduce((sum, file) => sum + file.smells, 0) | |
}; | |
// Update metric cards | |
locValue.textContent = totals.loc; | |
complexityValue.textContent = totals.complexity; | |
issuesValue.textContent = totals.issues; | |
smellsValue.textContent = totals.smells; | |
// Create charts | |
createMetricsChart(files); | |
createIssuesChart(files); | |
// Populate file analysis table | |
fileAnalysisTable.innerHTML = ''; | |
files.forEach(file => { | |
const row = document.createElement('tr'); | |
row.className = 'hover:bg-gray-50 cursor-pointer'; | |
row.innerHTML = ` | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="flex items-center"> | |
<i class="fas fa-file-code text-blue-500 mr-3"></i> | |
<span class="font-medium">${file.name}</span> | |
</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap">${file.loc}</td> | |
<td class="px-6 py-4 whitespace-nowrap">${file.complexity}</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<span class="px-2 py-1 text-xs font-medium rounded-full ${file.issues > 5 ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800'}"> | |
${file.issues} ${file.issues === 1 ? 'issue' : 'issues'} | |
</span> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap">${file.smells}</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="flex items-center"> | |
<div class="w-16 bg-gray-200 rounded-full h-2.5 mr-2"> | |
<div class="bg-green-500 h-2.5 rounded-full" style="width: ${file.coverage}"></div> | |
</div> | |
<span>${file.coverage}</span> | |
</div> | |
</td> | |
`; | |
fileAnalysisTable.appendChild(row); | |
// Add click event to view file details | |
row.addEventListener('click', () => displayFileDetails(file)); | |
}); | |
// Populate file tree | |
fileTree.innerHTML = ''; | |
files.forEach(file => { | |
const fileItem = document.createElement('div'); | |
fileItem.className = 'flex items-center p-2 rounded-md hover:bg-gray-100 cursor-pointer'; | |
fileItem.innerHTML = ` | |
<i class="fas fa-file-code text-blue-500 mr-3"></i> | |
<span>${file.name}</span> | |
`; | |
fileItem.addEventListener('click', () => displayFileDetails(file)); | |
fileTree.appendChild(fileItem); | |
}); | |
} | |
function createMetricsChart(files) { | |
const ctx = document.getElementById('metricsChart').getContext('2d'); | |
// Extract data for chart | |
const labels = files.map(file => file.name); | |
const locData = files.map(file => file.loc); | |
const complexityData = files.map(file => file.complexity); | |
const smellsData = files.map(file => file.smells); | |
new Chart(ctx, { | |
type: 'bar', | |
data: { | |
labels: labels, | |
datasets: [ | |
{ | |
label: 'Lines of Code', | |
data: locData, | |
backgroundColor: '#3b82f6', | |
borderColor: '#3b82f6', | |
borderWidth: 1 | |
}, | |
{ | |
label: 'Complexity', | |
data: complexityData, | |
backgroundColor: '#8b5cf6', | |
borderColor: '#8b5cf6', | |
borderWidth: 1 | |
}, | |
{ | |
label: 'Code Smells', | |
data: smellsData, | |
backgroundColor: '#f59e0b', | |
borderColor: '#f59e0b', | |
borderWidth: 1 | |
} | |
] | |
}, | |
options: { | |
responsive: true, | |
plugins: { | |
legend: { | |
position: 'top', | |
}, | |
tooltip: { | |
mode: 'index', | |
intersect: false, | |
} | |
}, | |
scales: { | |
y: { | |
beginAtZero: true | |
} | |
} | |
} | |
}); | |
} | |
function createIssuesChart(files) { | |
const ctx = document.getElementById('issuesChart').getContext('2d'); | |
// Count issue severities | |
let critical = 0, major = 0, minor = 0, info = 0; | |
files.forEach(file => { | |
file.issues.forEach(issue => { | |
switch(issue.severity) { | |
case 'critical': critical++; break; | |
case 'major': major++; break; | |
case 'minor': minor++; break; | |
case 'info': info++; break; | |
} | |
}); | |
}); | |
new Chart(ctx, { | |
type: 'doughnut', | |
data: { | |
labels: ['Critical', 'Major', 'Minor', 'Info'], | |
datasets: [{ | |
data: [critical, major, minor, info], | |
backgroundColor: [ | |
'#ef4444', | |
'#f97316', | |
'#f59e0b', | |
'#3b82f6' | |
], | |
borderWidth: 1 | |
}] | |
}, | |
options: { | |
responsive: true, | |
plugins: { | |
legend: { | |
position: 'right', | |
} | |
} | |
} | |
}); | |
} | |
function displayFileDetails(file) { | |
// Update file title | |
currentFileTitle.textContent = file.name; | |
// Display code with line numbers and issue markers | |
const lines = file.content.split('\n'); | |
let codeHtml = '<div class="flex flex-col">'; | |
lines.forEach((line, index) => { | |
const lineNumber = index + 1; | |
const hasIssue = file.issues.some(issue => issue.line === lineNumber); | |
codeHtml += ` | |
<div class="flex ${hasIssue ? 'issue-marker' : ''}"> | |
<span class="line-number">${lineNumber}</span> | |
<span>${escapeHtml(line)}</span> | |
</div> | |
`; | |
}); | |
codeHtml += '</div>'; | |
codeViewer.innerHTML = codeHtml; | |
// Display issues list | |
issuesList.innerHTML = ''; | |
if (file.issues.length === 0) { | |
issuesList.innerHTML = ` | |
<div class="text-center text-gray-500 py-8"> | |
<i class="fas fa-check-circle text-3xl mb-2"></i> | |
<p>No issues found for this file</p> | |
</div> | |
`; | |
} else { | |
file.issues.forEach(issue => { | |
const severityColor = { | |
'critical': 'bg-red-100 text-red-800', | |
'major': 'bg-orange-100 text-orange-800', | |
'minor': 'bg-yellow-100 text-yellow-800', | |
'info': 'bg-blue-100 text-blue-800' | |
}[issue.severity]; | |
const issueItem = document.createElement('div'); | |
issueItem.className = 'bg-white border border-gray-200 rounded-md p-4'; | |
issueItem.innerHTML = ` | |
<div class="flex justify-between items-start"> | |
<div> | |
<span class="px-2 py-1 text-xs font-medium rounded-full ${severityColor} mr-3"> | |
${issue.severity.charAt(0).toUpperCase() + issue.severity.slice(1)} | |
</span> | |
<span class="text-sm font-medium">${issue.type}</span> | |
</div> | |
<span class="text-sm text-gray-500">Line ${issue.line}</span> | |
</div> | |
<div class="mt-2 text-sm text-gray-700"> | |
${issue.message} | |
</div> | |
`; | |
issuesList.appendChild(issueItem); | |
}); | |
} | |
} | |
function escapeHtml(unsafe) { | |
return unsafe | |
.replace(/&/g, "&") | |
.replace(/</g, "<") | |
.replace(/>/g, ">") | |
.replace(/"/g, """) | |
.replace(/'/g, "'"); | |
} | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=asronsr/sonarqubeku" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |