| |
| class GitHubAnalyzer { |
| constructor() { |
| this.baseURL = 'https://api.github.com'; |
| this.init(); |
| } |
|
|
| init() { |
| this.bindEvents(); |
| } |
|
|
| bindEvents() { |
| const repoForm = document.getElementById('repoForm'); |
| if (repoForm) { |
| repoForm.addEventListener('submit', (e) => this.handleRepoSubmit(e)); |
| } |
| } |
|
|
| async handleRepoSubmit(e) { |
| e.preventDefault(); |
| |
| const username = document.getElementById('username').value.trim(); |
| const repoName = document.getElementById('repoName').value.trim(); |
| |
| if (!username || !repoName) { |
| this.showError('Please enter both username and repository name'); |
| return; |
| } |
|
|
| this.showLoading(); |
| this.showResultsSection(); |
|
|
| try { |
| const repoData = await this.fetchRepoData(username, repoName); |
| const contributorData = await this.fetchContributors(username, repoName); |
| const languagesData = await this.fetchLanguages(username, repoName); |
| |
| this.displayAnalysisResults(repoData, contributorData, languagesData); |
| } catch (error) { |
| this.showError('Failed to fetch repository data. Please check the username and repository name.'); |
| console.error('Error:', error); |
| } finally { |
| this.hideLoading(); |
| } |
| } |
|
|
| async fetchRepoData(username, repoName) { |
| const response = await fetch(`${this.baseURL}/repos/${username}/${repoName}`); |
| if (!response.ok) { |
| throw new Error('Repository not found'); |
| } |
| return await response.json(); |
| } |
|
|
| async fetchContributors(username, repoName) { |
| const response = await fetch(`${this.baseURL}/repos/${username}/${repoName}/contributors`); |
| if (!response.ok) { |
| return []; |
| } |
| return await response.json(); |
| } |
|
|
| async fetchLanguages(username, repoName) { |
| const response = await fetch(`${this.baseURL}/repos/${username}/${repoName}/languages`); |
| if (!response.ok) { |
| return {}; |
| } |
| return await response.json(); |
| } |
|
|
| displayAnalysisResults(repoData, contributors, languages) { |
| const analysisData = document.getElementById('analysisData'); |
| |
| |
| const stars = repoData.stargazers_count; |
| const forks = repoData.forks_count; |
| const openIssues = repoData.open_issues_count; |
| const watchers = repoData.watchers_count; |
| const sizeKB = Math.round(repoData.size / 1024); |
| const createdAt = new Date(repoData.created_at).toLocaleDateString(); |
| const lastUpdate = new Date(repoData.updated_at).toLocaleDateString(); |
| |
| |
| const totalBytes = Object.values(languages).reduce((sum, bytes) => sum + bytes, 0); |
| const languagePercentages = Object.entries(languages).map(([lang, bytes]) => ({ |
| language: lang, |
| percentage: Math.round((bytes / totalBytes) * 100) |
| })); |
|
|
| analysisData.innerHTML = ` |
| <!-- Repository Overview --> |
| <div class="analysis-card bg-white/10 backdrop-blur-lg rounded-xl p-6 border border-white/20"> |
| <h3 class="text-xl font-semibold text-white mb-4 flex items-center"> |
| <i data-feather="book" class="mr-2"></i> |
| Repository Overview |
| </h3> |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-4"> |
| <div class="text-center"> |
| <div class="text-2xl font-bold text-purple-400">${stars}</div> |
| <div class="text-sm text-gray-300">Stars</div> |
| </div> |
| <div class="text-center"> |
| <div class="text-2xl font-bold text-purple-400">${forks}</div> |
| <div class="text-sm text-gray-300">Forks</div> |
| </div> |
| <div class="text-center"> |
| <div class="text-2xl font-bold text-purple-400">${openIssues}</div> |
| <div class="text-sm text-gray-300">Open Issues</div> |
| </div> |
| <div class="text-center"> |
| <div class="text-2xl font-bold text-purple-400">${watchers}</div> |
| <div class="text-sm text-gray-300">Watchers</div> |
| </div> |
| </div> |
| </div> |
| |
| <!-- Language Distribution --> |
| <div class="analysis-card bg-white/10 backdrop-blur-lg rounded-xl p-6 border border-white/20"> |
| <h3 class="text-xl font-semibold text-white mb-4 flex items-center"> |
| <i data-feather="code" class="mr-2"></i> |
| Languages |
| </h3> |
| <div class="space-y-3"> |
| ${languagePercentages.map(lang => ` |
| <div class="flex items-center justify-between"> |
| <span class="text-gray-300">${lang.language}</span> |
| <span class="text-purple-400 font-semibold">${lang.percentage}%</span> |
| </div> |
| `).join('')} |
| </div> |
| </div> |
| |
| <!-- Contributors --> |
| <div class="analysis-card bg-white/10 backdrop-blur-lg rounded-xl p-6 border border-white/20"> |
| <h3 class="text-xl font-semibold text-white mb-4 flex items-center"> |
| <i data-feather="users" class="mr-2"></i> |
| Contributors (${contributors.length}) |
| </h3> |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-4"> |
| ${contributors.slice(0, 8).map(contributor => ` |
| <div class="text-center"> |
| <img src="${contributor.avatar_url}" alt="${contributor.login}" class="w-12 h-12 rounded-full mx-auto mb-2 border-2 border-purple-400"> |
| <div class="text-sm text-gray-300 truncate">${contributor.login}</div> |
| <div class="text-xs text-purple-400">${contributor.contributions} commits</div> |
| </div> |
| `).join('')} |
| </div> |
| </div> |
| |
| <!-- Repository Details --> |
| <div class="analysis-card bg-white/10 backdrop-blur-lg rounded-xl p-6 border border-white/20"> |
| <h3 class="text-xl font-semibold text-white mb-4 flex items-center"> |
| <i data-feather="info" class="mr-2"></i> |
| Repository Details |
| </h3> |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| <div class="flex justify-between"> |
| <span class="text-gray-300">Size</span> |
| <span class="text-purple-400">${sizeKB} KB</span> |
| </div> |
| <div class="flex justify-between"> |
| <span class="text-gray-300">Created</span> |
| <span class="text-purple-400">${createdAt}</span> |
| </div> |
| <div class="flex justify-between"> |
| <span class="text-gray-300">Last Updated</span> |
| <span class="text-purple-400">${lastUpdate}</span> |
| </div> |
| <div class="flex justify-between"> |
| <span class="text-gray-300">Default Branch</span> |
| <span class="text-purple-400">${repoData.default_branch}</span> |
| </div> |
| </div> |
| </div> |
| |
| <!-- Repository Description --> |
| ${repoData.description ? ` |
| <div class="analysis-card bg-white/10 backdrop-blur-lg rounded-xl p-6 border border-white/20"> |
| <h3 class="text-xl font-semibold text-white mb-4 flex items-center"> |
| <i data-feather="file-text" class="mr-2"></i> |
| Description |
| </h3> |
| <p class="text-gray-300">${repoData.description}</p> |
| </div> |
| ` : ''} |
| `; |
|
|
| feather.replace(); |
| } |
|
|
| showLoading() { |
| const loading = document.getElementById('loading'); |
| if (loading) loading.classList.remove('hidden'); |
| } |
|
|
| hideLoading() { |
| const loading = document.getElementById('loading'); |
| if (loading) loading.classList.add('hidden'); |
| } |
|
|
| showResultsSection() { |
| const results = document.getElementById('results'); |
| if (results) results.classList.remove('hidden'); |
| } |
|
|
| showError(message) { |
| alert(message); |
| } |
| } |
|
|
| |
| document.addEventListener('DOMContentLoaded', () => { |
| new GitHubAnalyzer(); |
| }); |
|
|
| |
| async function fetchWithErrorHandling(url, options = {}) { |
| const response = await fetch(url, options); |
| if (!response.ok) { |
| throw new Error(`HTTP error! status: ${response.status}`); |
| } |
| return await response.json(); |
| } |