Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>GitHub Portfolio Analyzer</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"> | |
<style> | |
.gradient-bg { | |
background: linear-gradient(135deg, #6e8efb, #a777e3); | |
} | |
.card-hover:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 10px 20px rgba(0,0,0,0.1); | |
} | |
.language-bar { | |
height: 10px; | |
border-radius: 5px; | |
display: inline-block; | |
vertical-align: middle; | |
margin-right: 5px; | |
} | |
.radar-chart { | |
width: 100%; | |
height: 300px; | |
} | |
.heatmap { | |
display: grid; | |
grid-template-columns: repeat(52, 1fr); | |
gap: 2px; | |
} | |
.heatmap-cell { | |
width: 10px; | |
height: 10px; | |
border-radius: 2px; | |
} | |
.repo-card:hover .repo-stats { | |
opacity: 1; | |
transform: translateY(0); | |
} | |
.repo-stats { | |
opacity: 0; | |
transform: translateY(10px); | |
transition: all 0.3s ease; | |
} | |
.animate-pulse { | |
animation: pulse 2s infinite; | |
} | |
@keyframes pulse { | |
0%, 100% { | |
opacity: 1; | |
} | |
50% { | |
opacity: 0.5; | |
} | |
} | |
</style> | |
</head> | |
<body class="bg-gray-50 font-sans"> | |
<!-- Navigation --> | |
<nav class="bg-white shadow-sm"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
<div class="flex justify-between h-16"> | |
<div class="flex items-center"> | |
<div class="flex-shrink-0 flex items-center"> | |
<i class="fab fa-github text-2xl text-gray-800 mr-2"></i> | |
<span class="text-xl font-bold text-gray-800">Portfolio Analyzer</span> | |
</div> | |
</div> | |
<div class="hidden sm:ml-6 sm:flex sm:items-center"> | |
<button id="loginBtn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | |
<i class="fab fa-github mr-2"></i> Login with GitHub | |
</button> | |
</div> | |
</div> | |
</div> | |
</nav> | |
<!-- Hero Section --> | |
<div class="gradient-bg text-white"> | |
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8 text-center"> | |
<h1 class="text-4xl font-extrabold tracking-tight sm:text-5xl lg:text-6xl mb-6"> | |
Analyze Your GitHub Portfolio | |
</h1> | |
<p class="text-xl max-w-3xl mx-auto"> | |
Get detailed insights about your coding skills, project quality, and areas for improvement based on your GitHub repositories. | |
</p> | |
<div class="mt-8"> | |
<button id="heroLoginBtn" class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-indigo-600 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-white"> | |
<i class="fab fa-github mr-2"></i> Connect Your GitHub Account | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Features Section --> | |
<div class="py-12 bg-white"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
<div class="lg:text-center"> | |
<h2 class="text-base text-indigo-600 font-semibold tracking-wide uppercase">Features</h2> | |
<p class="mt-2 text-3xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-4xl"> | |
Comprehensive Code Analysis | |
</p> | |
</div> | |
<div class="mt-10"> | |
<div class="grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-3"> | |
<div class="bg-gray-50 p-6 rounded-lg shadow-sm card-hover"> | |
<div class="flex items-center justify-center h-12 w-12 rounded-md bg-indigo-500 text-white"> | |
<i class="fas fa-code text-xl"></i> | |
</div> | |
<div class="mt-5"> | |
<h3 class="text-lg font-medium text-gray-900">Language Analysis</h3> | |
<p class="mt-2 text-base text-gray-500"> | |
Detailed breakdown of programming languages used across all your repositories with proficiency metrics. | |
</p> | |
</div> | |
</div> | |
<div class="bg-gray-50 p-6 rounded-lg shadow-sm card-hover"> | |
<div class="flex items-center justify-center h-12 w-12 rounded-md bg-indigo-500 text-white"> | |
<i class="fas fa-check-circle text-xl"></i> | |
</div> | |
<div class="mt-5"> | |
<h3 class="text-lg font-medium text-gray-900">Code Quality</h3> | |
<p class="mt-2 text-base text-gray-500"> | |
Automated assessment of code quality including complexity, maintainability, and best practices. | |
</p> | |
</div> | |
</div> | |
<div class="bg-gray-50 p-6 rounded-lg shadow-sm card-hover"> | |
<div class="flex items-center justify-center h-12 w-12 rounded-md bg-indigo-500 text-white"> | |
<i class="fas fa-vial text-xl"></i> | |
</div> | |
<div class="mt-5"> | |
<h3 class="text-lg font-medium text-gray-900">Test Coverage</h3> | |
<p class="mt-2 text-base text-gray-500"> | |
Evaluation of test coverage and testing practices across your projects. | |
</p> | |
</div> | |
</div> | |
<div class="bg-gray-50 p-6 rounded-lg shadow-sm card-hover"> | |
<div class="flex items-center justify-center h-12 w-12 rounded-md bg-indigo-500 text-white"> | |
<i class="fas fa-project-diagram text-xl"></i> | |
</div> | |
<div class="mt-5"> | |
<h3 class="text-lg font-medium text-gray-900">Project Structure</h3> | |
<p class="mt-2 text-base text-gray-500"> | |
Analysis of project architecture and organization patterns. | |
</p> | |
</div> | |
</div> | |
<div class="bg-gray-50 p-6 rounded-lg shadow-sm card-hover"> | |
<div class="flex items-center justify-center h-12 w-12 rounded-md bg-indigo-500 text-white"> | |
<i class="fas fa-chart-line text-xl"></i> | |
</div> | |
<div class="mt-5"> | |
<h3 class="text-lg font-medium text-gray-900">Activity Metrics</h3> | |
<p class="mt-2 text-base text-gray-500"> | |
Tracking of your coding activity, commit frequency, and contribution patterns. | |
</p> | |
</div> | |
</div> | |
<div class="bg-gray-50 p-6 rounded-lg shadow-sm card-hover"> | |
<div class="flex items-center justify-center h-12 w-12 rounded-md bg-indigo-500 text-white"> | |
<i class="fas fa-bullseye text-xl"></i> | |
</div> | |
<div class="mt-5"> | |
<h3 class="text-lg font-medium text-gray-900">Milestone Planning</h3> | |
<p class="mt-2 text-base text-gray-500"> | |
Personalized recommendations for skill development and project milestones. | |
</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Dashboard Section (Visible after login) --> | |
<div id="dashboard" class="hidden py-12 bg-gray-50"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
<div class="flex justify-between items-center mb-8"> | |
<div> | |
<h2 class="text-2xl font-bold text-gray-900">Your GitHub Portfolio Analysis</h2> | |
<p class="text-gray-600">Based on your public repositories</p> | |
</div> | |
<div class="flex items-center"> | |
<img id="userAvatar" class="h-10 w-10 rounded-full mr-3" src="" alt=""> | |
<span id="username" class="font-medium text-gray-900"></span> | |
</div> | |
</div> | |
<!-- Summary Cards --> | |
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4 mb-8"> | |
<div class="bg-white overflow-hidden shadow rounded-lg"> | |
<div class="px-4 py-5 sm:p-6"> | |
<div class="flex items-center"> | |
<div class="flex-shrink-0 bg-indigo-500 rounded-md p-3"> | |
<i class="fas fa-code text-white"></i> | |
</div> | |
<div class="ml-5 w-0 flex-1"> | |
<dl> | |
<dt class="text-sm font-medium text-gray-500 truncate">Total Repositories</dt> | |
<dd id="totalRepos" class="flex items-baseline"> | |
<div class="text-2xl font-semibold text-gray-900">0</div> | |
</dd> | |
</dl> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="bg-white overflow-hidden shadow rounded-lg"> | |
<div class="px-4 py-5 sm:p-6"> | |
<div class="flex items-center"> | |
<div class="flex-shrink-0 bg-green-500 rounded-md p-3"> | |
<i class="fas fa-star text-white"></i> | |
</div> | |
<div class="ml-5 w-0 flex-1"> | |
<dl> | |
<dt class="text-sm font-medium text-gray-500 truncate">Total Stars</dt> | |
<dd id="totalStars" class="flex items-baseline"> | |
<div class="text-2xl font-semibold text-gray-900">0</div> | |
</dd> | |
</dl> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="bg-white overflow-hidden shadow rounded-lg"> | |
<div class="px-4 py-5 sm:p-6"> | |
<div class="flex items-center"> | |
<div class="flex-shrink-0 bg-blue-500 rounded-md p-3"> | |
<i class="fas fa-code-branch text-white"></i> | |
</div> | |
<div class="ml-5 w-0 flex-1"> | |
<dl> | |
<dt class="text-sm font-medium text-gray-500 truncate">Primary Language</dt> | |
<dd id="primaryLanguage" class="flex items-baseline"> | |
<div class="text-2xl font-semibold text-gray-900">-</div> | |
</dd> | |
</dl> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="bg-white overflow-hidden shadow rounded-lg"> | |
<div class="px-4 py-5 sm:p-6"> | |
<div class="flex items-center"> | |
<div class="flex-shrink-0 bg-purple-500 rounded-md p-3"> | |
<i class="fas fa-chart-pie text-white"></i> | |
</div> | |
<div class="ml-5 w-0 flex-1"> | |
<dl> | |
<dt class="text-sm font-medium text-gray-500 truncate">Code Quality Score</dt> | |
<dd id="qualityScore" class="flex items-baseline"> | |
<div class="text-2xl font-semibold text-gray-900">0/100</div> | |
</dd> | |
</dl> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Language Distribution --> | |
<div class="mb-8"> | |
<div class="bg-white shadow rounded-lg p-6"> | |
<h3 class="text-lg font-medium text-gray-900 mb-4">Language Distribution</h3> | |
<div id="languageChart" class="h-64"></div> | |
<div id="languageList" class="mt-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"></div> | |
</div> | |
</div> | |
<!-- Repository List --> | |
<div class="mb-8"> | |
<div class="bg-white shadow rounded-lg p-6"> | |
<h3 class="text-lg font-medium text-gray-900 mb-4">Your Repositories</h3> | |
<div id="repoList" class="space-y-4"></div> | |
</div> | |
</div> | |
<!-- Code Quality Metrics --> | |
<div class="mb-8"> | |
<div class="bg-white shadow rounded-lg p-6"> | |
<h3 class="text-lg font-medium text-gray-900 mb-4">Code Quality Metrics</h3> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
<div> | |
<h4 class="font-medium text-gray-700 mb-2">Maintainability</h4> | |
<div class="w-full bg-gray-200 rounded-full h-4"> | |
<div id="maintainabilityBar" class="bg-green-500 h-4 rounded-full" style="width: 0%"></div> | |
</div> | |
<p class="text-sm text-gray-500 mt-1" id="maintainabilityText">Calculating...</p> | |
</div> | |
<div> | |
<h4 class="font-medium text-gray-700 mb-2">Test Coverage</h4> | |
<div class="w-full bg-gray-200 rounded-full h-4"> | |
<div id="coverageBar" class="bg-blue-500 h-4 rounded-full" style="width: 0%"></div> | |
</div> | |
<p class="text-sm text-gray-500 mt-1" id="coverageText">Calculating...</p> | |
</div> | |
<div> | |
<h4 class="font-medium text-gray-700 mb-2">Documentation</h4> | |
<div class="w-full bg-gray-200 rounded-full h-4"> | |
<div id="docsBar" class="bg-purple-500 h-4 rounded-full" style="width: 0%"></div> | |
</div> | |
<p class="text-sm text-gray-500 mt-1" id="docsText">Calculating...</p> | |
</div> | |
<div> | |
<h4 class="font-medium text-gray-700 mb-2">Code Complexity</h4> | |
<div class="w-full bg-gray-200 rounded-full h-4"> | |
<div id="complexityBar" class="bg-yellow-500 h-4 rounded-full" style="width: 0%"></div> | |
</div> | |
<p class="text-sm text-gray-500 mt-1" id="complexityText">Calculating...</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Activity Heatmap --> | |
<div class="mb-8"> | |
<div class="bg-white shadow rounded-lg p-6"> | |
<h3 class="text-lg font-medium text-gray-900 mb-4">Coding Activity</h3> | |
<div class="overflow-x-auto"> | |
<div id="heatmap" class="heatmap mb-2"></div> | |
</div> | |
<div class="flex justify-between text-xs text-gray-500 mt-2"> | |
<span>Less</span> | |
<span>More</span> | |
</div> | |
</div> | |
</div> | |
<!-- Tech Stack Analysis --> | |
<div class="mb-8"> | |
<div class="bg-white shadow rounded-lg p-6"> | |
<h3 class="text-lg font-medium text-gray-900 mb-4">Your Tech Stack</h3> | |
<div id="techStack" class="flex flex-wrap gap-2"> | |
<!-- Tech stack items will be added here --> | |
</div> | |
</div> | |
</div> | |
<!-- Milestones --> | |
<div class="mb-8"> | |
<div class="bg-white shadow rounded-lg p-6"> | |
<h3 class="text-lg font-medium text-gray-900 mb-4">Recommended Milestones</h3> | |
<div id="milestones" class="space-y-4"> | |
<div class="border-l-4 border-indigo-500 pl-4 py-2"> | |
<h4 class="font-medium">Improve Test Coverage</h4> | |
<p class="text-sm text-gray-600">Aim for at least 80% test coverage in your main projects</p> | |
<div class="mt-2 flex items-center text-sm"> | |
<span class="text-indigo-600">0/3 projects improved</span> | |
<span class="mx-2">·</span> | |
<span class="text-gray-500">Due in 2 months</span> | |
</div> | |
</div> | |
<div class="border-l-4 border-green-500 pl-4 py-2"> | |
<h4 class="font-medium">Learn a New Language</h4> | |
<p class="text-sm text-gray-600">Expand your skills by learning Rust or Go</p> | |
<div class="mt-2 flex items-center text-sm"> | |
<span class="text-green-600">Not started</span> | |
<span class="mx-2">·</span> | |
<span class="text-gray-500">Due in 3 months</span> | |
</div> | |
</div> | |
<div class="border-l-4 border-yellow-500 pl-4 py-2"> | |
<h4 class="font-medium">Improve Documentation</h4> | |
<p class="text-sm text-gray-600">Add comprehensive READMEs to all your main projects</p> | |
<div class="mt-2 flex items-center text-sm"> | |
<span class="text-yellow-600">1/5 projects documented</span> | |
<span class="mx-2">·</span> | |
<span class="text-gray-500">Due in 1 month</span> | |
</div> | |
</div> | |
</div> | |
<button class="mt-4 inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | |
Add Custom Milestone | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Loading State --> | |
<div id="loading" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> | |
<div class="bg-white rounded-lg p-8 max-w-md w-full text-center"> | |
<div class="animate-spin rounded-full h-16 w-16 border-b-2 border-indigo-600 mx-auto mb-4"></div> | |
<h3 class="text-lg font-medium text-gray-900 mb-2">Analyzing Your GitHub Portfolio</h3> | |
<p class="text-gray-600">This may take a few moments while we analyze your repositories...</p> | |
</div> | |
</div> | |
<script> | |
// GitHub OAuth configuration | |
const clientId = 'YOUR_GITHUB_CLIENT_ID'; // Replace with your GitHub OAuth app client ID | |
const clientSecret = 'YOUR_GITHUB_CLIENT_SECRET'; // Replace with your GitHub OAuth app client secret | |
const redirectUri = window.location.href.split('?')[0]; // Current page URL | |
// DOM elements | |
const loginBtn = document.getElementById('loginBtn'); | |
const heroLoginBtn = document.getElementById('heroLoginBtn'); | |
const dashboard = document.getElementById('dashboard'); | |
const loading = document.getElementById('loading'); | |
// Check for OAuth callback | |
const urlParams = new URLSearchParams(window.location.search); | |
const code = urlParams.get('code'); | |
if (code) { | |
// We have an OAuth code, exchange it for an access token | |
exchangeCodeForToken(code); | |
} | |
// Set up login buttons | |
loginBtn.addEventListener('click', initiateGitHubLogin); | |
heroLoginBtn.addEventListener('click', initiateGitHubLogin); | |
function initiateGitHubLogin() { | |
// Redirect to GitHub OAuth | |
const githubOAuthUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=user,repo`; | |
window.location.href = githubOAuthUrl; | |
} | |
async function exchangeCodeForToken(code) { | |
showLoading(); | |
try { | |
// In a real app, you would do this server-side for security | |
// This is just for demonstration | |
const response = await fetch('https://github.com/login/oauth/access_token', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
'Accept': 'application/json' | |
}, | |
body: JSON.stringify({ | |
client_id: clientId, | |
client_secret: clientSecret, | |
code: code, | |
redirect_uri: redirectUri | |
}) | |
}); | |
const data = await response.json(); | |
const accessToken = data.access_token; | |
if (accessToken) { | |
// Store token (in a real app, use secure storage) | |
localStorage.setItem('github_token', accessToken); | |
// Remove code from URL | |
window.history.replaceState({}, document.title, window.location.pathname); | |
// Load user data | |
await loadUserData(accessToken); | |
} else { | |
hideLoading(); | |
alert('Failed to authenticate with GitHub'); | |
} | |
} catch (error) { | |
hideLoading(); | |
console.error('Error exchanging code for token:', error); | |
alert('An error occurred during authentication'); | |
} | |
} | |
async function loadUserData(token) { | |
try { | |
// Get user info | |
const userResponse = await fetch('https://api.github.com/user', { | |
headers: { | |
'Authorization': `token ${token}` | |
} | |
}); | |
const user = await userResponse.json(); | |
// Get user repos | |
const reposResponse = await fetch('https://api.github.com/user/repos?per_page=100', { | |
headers: { | |
'Authorization': `token ${token}` | |
} | |
}); | |
const repos = await reposResponse.json(); | |
// Process and display data | |
displayUserData(user, repos); | |
analyzeRepositories(repos); | |
hideLoading(); | |
dashboard.classList.remove('hidden'); | |
} catch (error) { | |
hideLoading(); | |
console.error('Error loading user data:', error); | |
alert('Failed to load GitHub data'); | |
} | |
} | |
function displayUserData(user, repos) { | |
// Display basic user info | |
document.getElementById('username').textContent = user.login; | |
document.getElementById('userAvatar').src = user.avatar_url; | |
// Display repo count | |
document.getElementById('totalRepos').textContent = repos.length; | |
// Calculate total stars | |
const totalStars = repos.reduce((sum, repo) => sum + repo.stargazers_count, 0); | |
document.getElementById('totalStars').textContent = totalStars; | |
} | |
function analyzeRepositories(repos) { | |
// Analyze languages | |
const languageStats = analyzeLanguages(repos); | |
displayLanguageStats(languageStats); | |
// Display repositories | |
displayRepositories(repos); | |
// Generate mock quality metrics (in a real app, this would be more sophisticated) | |
generateQualityMetrics(); | |
// Generate mock activity heatmap | |
generateActivityHeatmap(); | |
// Generate mock tech stack | |
generateTechStack(languageStats); | |
} | |
function analyzeLanguages(repos) { | |
const languageStats = {}; | |
let totalBytes = 0; | |
// Filter out repos without language data | |
const reposWithLanguages = repos.filter(repo => repo.language); | |
// Count language occurrences | |
reposWithLanguages.forEach(repo => { | |
if (!languageStats[repo.language]) { | |
languageStats[repo.language] = { | |
count: 0, | |
bytes: 0, | |
color: getLanguageColor(repo.language), | |
stars: 0 | |
}; | |
} | |
languageStats[repo.language].count++; | |
languageStats[repo.language].stars += repo.stargazers_count; | |
}); | |
// Set primary language | |
let primaryLanguage = ''; | |
let maxCount = 0; | |
for (const lang in languageStats) { | |
if (languageStats[lang].count > maxCount) { | |
maxCount = languageStats[lang].count; | |
primaryLanguage = lang; | |
} | |
} | |
document.getElementById('primaryLanguage').textContent = primaryLanguage; | |
return languageStats; | |
} | |
function displayLanguageStats(languageStats) { | |
const languageList = document.getElementById('languageList'); | |
languageList.innerHTML = ''; | |
// Sort languages by count | |
const sortedLanguages = Object.keys(languageStats).sort((a, b) => { | |
return languageStats[b].count - languageStats[a].count; | |
}); | |
// Display top languages | |
sortedLanguages.slice(0, 6).forEach(lang => { | |
const stat = languageStats[lang]; | |
const percentage = Math.round((stat.count / Object.keys(languageStats).length) * 100); | |
const langElement = document.createElement('div'); | |
langElement.className = 'bg-gray-50 p-4 rounded-lg'; | |
langElement.innerHTML = ` | |
<div class="flex items-center justify-between"> | |
<div class="flex items-center"> | |
<span class="language-bar" style="width: 10px; height: 10px; background-color: ${stat.color || '#3182ce'};"></span> | |
<span class="font-medium">${lang}</span> | |
</div> | |
<span class="text-gray-600">${stat.count} repos</span> | |
</div> | |
<div class="mt-2"> | |
<div class="w-full bg-gray-200 rounded-full h-2"> | |
<div class="bg-blue-500 h-2 rounded-full" style="width: ${percentage}%"></div> | |
</div> | |
</div> | |
<div class="mt-2 flex justify-between text-sm text-gray-500"> | |
<span>${percentage}% of repos</span> | |
<span>${stat.stars} <i class="fas fa-star text-yellow-400"></i></span> | |
</div> | |
`; | |
languageList.appendChild(langElement); | |
}); | |
// Simple chart visualization | |
const chartContainer = document.getElementById('languageChart'); | |
chartContainer.innerHTML = ` | |
<div class="flex items-end h-48 mt-6 space-x-1"> | |
${sortedLanguages.slice(0, 5).map(lang => { | |
const stat = languageStats[lang]; | |
const height = Math.round((stat.count / languageStats[sortedLanguages[0]].count) * 100); | |
return ` | |
<div class="flex flex-col items-center flex-1"> | |
<div class="w-full bg-blue-500 rounded-t hover:bg-blue-600 transition-all" style="height: ${height}%"></div> | |
<div class="text-xs text-gray-500 mt-1">${lang}</div> | |
</div> | |
`; | |
}).join('')} | |
</div> | |
`; | |
} | |
function displayRepositories(repos) { | |
const repoList = document.getElementById('repoList'); | |
repoList.innerHTML = ''; | |
// Sort by stars | |
const sortedRepos = [...repos].sort((a, b) => b.stargazers_count - a.stargazers_count); | |
// Display top 10 repos | |
sortedRepos.slice(0, 10).forEach(repo => { | |
const repoElement = document.createElement('div'); | |
repoElement.className = 'repo-card border border-gray-200 rounded-lg p-4 hover:shadow-md transition-all'; | |
repoElement.innerHTML = ` | |
<div class="flex justify-between items-start"> | |
<div> | |
<h4 class="font-medium text-lg"> | |
<a href="${repo.html_url}" target="_blank" class="text-indigo-600 hover:text-indigo-800">${repo.name}</a> | |
</h4> | |
<p class="text-gray-600 text-sm mt-1">${repo.description || 'No description provided'}</p> | |
</div> | |
<div class="flex items-center"> | |
<span class="text-gray-500 mr-2">${repo.stargazers_count}</span> | |
<i class="fas fa-star text-yellow-400"></i> | |
</div> | |
</div> | |
<div class="repo-stats mt-3 pt-3 border-t border-gray-100"> | |
<div class="flex flex-wrap gap-4 text-sm"> | |
<div class="flex items-center"> | |
<span class="language-bar" style="width: 10px; height: 10px; background-color: ${getLanguageColor(repo.language) || '#3182ce'};"></span> | |
<span class="ml-1 text-gray-600">${repo.language || 'Unknown'}</span> | |
</div> | |
<div class="flex items-center"> | |
<i class="fas fa-code-branch text-gray-400 mr-1"></i> | |
<span class="text-gray-600">${repo.forks_count} forks</span> | |
</div> | |
<div class="flex items-center"> | |
<i class="fas fa-eye text-gray-400 mr-1"></i> | |
<span class="text-gray-600">${repo.watchers_count} watchers</span> | |
</div> | |
<div class="flex items-center"> | |
<i class="fas fa-calendar-alt text-gray-400 mr-1"></i> | |
<span class="text-gray-600">Updated ${formatDate(repo.updated_at)}</span> | |
</div> | |
</div> | |
</div> | |
`; | |
repoList.appendChild(repoElement); | |
}); | |
} | |
function generateQualityMetrics() { | |
// Mock quality metrics (in a real app, this would come from analysis) | |
const maintainability = Math.floor(Math.random() * 40) + 60; // 60-100 | |
const coverage = Math.floor(Math.random() * 50) + 30; // 30-80 | |
const docs = Math.floor(Math.random() * 60) + 20; // 20-80 | |
const complexity = Math.floor(Math.random() * 40) + 40; // 40-80 | |
document.getElementById('maintainabilityBar').style.width = `${maintainability}%`; | |
document.getElementById('coverageBar').style.width = `${coverage}%`; | |
document.getElementById('docsBar').style.width = `${docs}%`; | |
document.getElementById('complexityBar').style.width = `${100 - complexity}%`; | |
document.getElementById('maintainabilityText').textContent = | |
maintainability >= 80 ? 'Excellent maintainability' : | |
maintainability >= 60 ? 'Good maintainability' : 'Needs improvement'; | |
document.getElementById('coverageText').textContent = | |
coverage >= 70 ? 'Great test coverage' : | |
coverage >= 50 ? 'Moderate test coverage' : 'Low test coverage'; | |
document.getElementById('docsText').textContent = | |
docs >= 70 ? 'Well documented' : | |
docs >= 50 ? 'Moderate documentation' : 'Needs more documentation'; | |
document.getElementById('complexityText').textContent = | |
complexity <= 40 ? 'Simple and clean' : | |
complexity <= 60 ? 'Moderate complexity' : 'High complexity - consider refactoring'; | |
document.getElementById('qualityScore').textContent = `${Math.round((maintainability + coverage + (100 - complexity) + docs) / 4)}/100`; | |
} | |
function generateActivityHeatmap() { | |
const heatmap = document.getElementById('heatmap'); | |
heatmap.innerHTML = ''; | |
// Generate mock activity data (52 weeks) | |
for (let i = 0; i < 52 * 7; i++) { | |
const cell = document.createElement('div'); | |
cell.className = 'heatmap-cell'; | |
// Random activity level (0-4) | |
const activity = Math.floor(Math.random() * 5); | |
// Set color based on activity level | |
const colors = ['#ebedf0', '#9be9a8', '#40c463', '#30a14e', '#216e39']; | |
cell.style.backgroundColor = colors[activity]; | |
heatmap.appendChild(cell); | |
} | |
} | |
function generateTechStack(languageStats) { | |
const techStack = document.getElementById('techStack'); | |
techStack.innerHTML = ''; | |
// Sort languages by count | |
const sortedLanguages = Object.keys(languageStats).sort((a, b) => { | |
return languageStats[b].count - languageStats[a].count; | |
}); | |
// Display top 8 languages as tech stack | |
sortedLanguages.slice(0, 8).forEach(lang => { | |
const stat = languageStats[lang]; | |
const techItem = document.createElement('div'); | |
techItem.className = 'flex items-center px-3 py-1 rounded-full text-sm font-medium bg-indigo-100 text-indigo-800'; | |
techItem.innerHTML = ` | |
<span class="language-bar" style="width: 8px; height: 8px; background-color: ${stat.color || '#3182ce'};"></span> | |
<span class="ml-2">${lang}</span> | |
`; | |
techStack.appendChild(techItem); | |
}); | |
// Add some common technologies (mock data) | |
const commonTechs = ['React', 'Node.js', 'Express', 'MongoDB', 'Docker', 'Git']; | |
commonTechs.forEach(tech => { | |
const techItem = document.createElement('div'); | |
techItem.className = 'flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800'; | |
techItem.innerHTML = ` | |
<i class="fas fa-check-circle mr-2"></i> | |
<span>${tech}</span> | |
`; | |
techStack.appendChild(techItem); | |
}); | |
} | |
function getLanguageColor(language) { | |
// Simple color mapping for common languages | |
const colors = { | |
'JavaScript': '#f1e05a', | |
'TypeScript': '#3178c6', | |
'Python': '#3572A5', | |
'Java': '#b07219', | |
'C++': '#f34b7d', | |
'C': '#555555', | |
'C#': '#178600', | |
'Ruby': '#701516', | |
'PHP': '#4F5D95', | |
'Go': '#00ADD8', | |
'Rust': '#dea584', | |
'Swift': '#ffac45', | |
'Kotlin': '#A97BFF', | |
'HTML': '#e34c26', | |
'CSS': '#563d7c', | |
'Shell': '#89e051', | |
'Dart': '#00B4AB', | |
'Scala': '#c22d40', | |
'R': '#198CE7' | |
}; | |
return colors[language] || '#3182ce'; // Default to blue if unknown | |
} | |
function formatDate(dateString) { | |
const date = new Date(dateString); | |
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); | |
} | |
function showLoading() { | |
loading.classList.remove('hidden'); | |
} | |
function hideLoading() { | |
loading.classList.add('hidden'); | |
} | |
// Check for existing token on page load | |
document.addEventListener('DOMContentLoaded', () => { | |
const token = localStorage.getItem('github_token'); | |
if (token) { | |
showLoading(); | |
loadUserData(token); | |
} | |
}); | |
</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=blackrajeev/portfolio-analyser" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |