Improve mobile responsiveness, explain scoring, simple search.

This commit is contained in:
2025-12-12 20:42:58 +00:00
parent 09b0c533b4
commit a5b522c996
14 changed files with 1007 additions and 41 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -8,9 +8,9 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<script type="module" crossorigin src="./assets/index-IALpeAps.js"></script>
<script type="module" crossorigin src="./assets/index-DSNTKr2U.js"></script>
<link rel="modulepreload" crossorigin href="./assets/chart-Bcjh2pZL.js">
<link rel="stylesheet" crossorigin href="./assets/index-DOVyCPqp.css">
<link rel="stylesheet" crossorigin href="./assets/index-B0t9NqXA.css">
</head>
<body class="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 font-sans transition-colors duration-300">
<div id="app"></div>
+2 -2
View File
@@ -47,7 +47,7 @@ const getAlignClass = (align) => {
v-for="col in columns"
:key="col.key"
:class="[
'px-6 py-4 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider',
'px-3 sm:px-6 py-3 sm:py-4 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider',
getAlignClass(col.align),
col.headerClass
]"
@@ -66,7 +66,7 @@ const getAlignClass = (align) => {
<td
v-for="col in columns"
:key="col.key"
:class="['px-6 py-4', getAlignClass(col.align), col.class]"
:class="['px-3 sm:px-6 py-3 sm:py-4', getAlignClass(col.align), col.class]"
>
<slot :name="col.key" :item="item" :index="index">
{{ item[col.key] }}
+41 -8
View File
@@ -34,6 +34,12 @@ const repositories = computed(() => globalData.value?.Repositories || [])
>
Leaderboard
</RouterLink>
<RouterLink
to="/how-scoring-works"
:class="route.path === '/how-scoring-works' ? 'nav-link-active' : 'nav-link'"
>
How Scoring Works
</RouterLink>
<RouterLink
v-for="repo in repositories"
:key="`${repo.Owner}/${repo.Name}`"
@@ -58,30 +64,57 @@ const repositories = computed(() => globalData.value?.Repositories || [])
</div>
<!-- Mobile Menu -->
<div v-if="mobileMenuOpen" class="md:hidden py-4 border-t border-gray-200 dark:border-gray-700">
<div class="flex flex-col space-y-3">
<div v-if="mobileMenuOpen" class="md:hidden py-2 border-t border-gray-200 dark:border-gray-700">
<div class="flex flex-col space-y-1">
<RouterLink
to="/"
@click="mobileMenuOpen = false"
:class="route.path === '/' ? 'nav-link-active' : 'nav-link'"
:class="[
'block px-4 py-3 rounded-lg text-base font-medium transition-colors',
route.path === '/'
? 'bg-primary-50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400'
: 'text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800'
]"
>
Dashboard
<i class="fas fa-home mr-3 w-5 text-center"></i>Dashboard
</RouterLink>
<RouterLink
to="/leaderboard"
@click="mobileMenuOpen = false"
:class="route.path === '/leaderboard' ? 'nav-link-active' : 'nav-link'"
:class="[
'block px-4 py-3 rounded-lg text-base font-medium transition-colors',
route.path === '/leaderboard'
? 'bg-primary-50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400'
: 'text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800'
]"
>
Leaderboard
<i class="fas fa-trophy mr-3 w-5 text-center"></i>Leaderboard
</RouterLink>
<RouterLink
to="/how-scoring-works"
@click="mobileMenuOpen = false"
:class="[
'block px-4 py-3 rounded-lg text-base font-medium transition-colors',
route.path === '/how-scoring-works'
? 'bg-primary-50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400'
: 'text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800'
]"
>
<i class="fas fa-calculator mr-3 w-5 text-center"></i>How Scoring Works
</RouterLink>
<RouterLink
v-for="repo in repositories"
:key="`${repo.Owner}/${repo.Name}`"
:to="`/repos/${repo.Owner}/${repo.Name}`"
@click="mobileMenuOpen = false"
:class="route.path.includes(`/repos/${repo.Owner}/${repo.Name}`) ? 'nav-link-active' : 'nav-link'"
:class="[
'block px-4 py-3 rounded-lg text-base font-medium transition-colors',
route.path.includes(`/repos/${repo.Owner}/${repo.Name}`)
? 'bg-primary-50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400'
: 'text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800'
]"
>
{{ repo.Name }}
<i class="fas fa-code-branch mr-3 w-5 text-center"></i>{{ repo.Name }}
</RouterLink>
</div>
</div>
+4 -4
View File
@@ -14,13 +14,13 @@ defineProps({
<template>
<div class="card animate-fade-in-up" :style="{ animationDelay: delay }">
<div class="flex items-center justify-between">
<div>
<div class="text-3xl font-bold" :class="valueClass">
<div class="min-w-0 flex-1">
<div class="text-xl sm:text-2xl md:text-3xl font-bold truncate" :class="valueClass">
{{ typeof value === 'number' ? formatNumber(value) : value }}
</div>
<div class="text-sm text-gray-500 dark:text-gray-400 mt-1">{{ label }}</div>
<div class="text-xs sm:text-sm text-gray-500 dark:text-gray-400 mt-1 truncate">{{ label }}</div>
</div>
<div v-if="icon" class="text-3xl opacity-50" :class="iconColor">
<div v-if="icon" class="text-2xl sm:text-3xl opacity-50 ml-2 flex-shrink-0" :class="iconColor">
<i :class="icon"></i>
</div>
</div>
+34 -12
View File
@@ -1,5 +1,5 @@
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { Chart, registerables } from 'chart.js'
Chart.register(...registerables)
@@ -49,7 +49,9 @@ const chartData = computed(() => {
}
})
const chartOptions = {
const isMobile = ref(window.innerWidth < 640)
const chartOptions = computed(() => ({
responsive: true,
maintainAspectRatio: false,
interaction: {
@@ -61,20 +63,21 @@ const chartOptions = {
position: 'top',
labels: {
usePointStyle: true,
padding: 20,
padding: isMobile.value ? 10 : 20,
boxWidth: isMobile.value ? 8 : 12,
font: {
size: 12
size: isMobile.value ? 10 : 12
}
}
},
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
padding: 12,
padding: isMobile.value ? 8 : 12,
titleFont: {
size: 14
size: isMobile.value ? 12 : 14
},
bodyFont: {
size: 13
size: isMobile.value ? 11 : 13
},
callbacks: {
label: (context) => {
@@ -90,8 +93,11 @@ const chartOptions = {
},
ticks: {
font: {
size: 11
}
size: isMobile.value ? 9 : 11
},
maxRotation: isMobile.value ? 45 : 0,
autoSkip: true,
maxTicksLimit: isMobile.value ? 6 : 12
}
},
y: {
@@ -101,7 +107,7 @@ const chartOptions = {
},
ticks: {
font: {
size: 11
size: isMobile.value ? 9 : 11
},
callback: (value) => {
if (value >= 1000) {
@@ -112,7 +118,7 @@ const chartOptions = {
}
}
}
}
}))
function createChart() {
if (!chartRef.value || !chartData.value.labels.length) return
@@ -125,7 +131,7 @@ function createChart() {
chartInstance = new Chart(ctx, {
type: 'line',
data: chartData.value,
options: chartOptions
options: chartOptions.value
})
}
@@ -138,8 +144,24 @@ function updateChart() {
}
}
function handleResize() {
const newIsMobile = window.innerWidth < 640
if (newIsMobile !== isMobile.value) {
isMobile.value = newIsMobile
createChart() // Recreate chart with new options
}
}
onMounted(() => {
createChart()
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
if (chartInstance) {
chartInstance.destroy()
}
})
watch(() => props.timeline, () => {
+2
View File
@@ -9,10 +9,12 @@ import Leaderboard from './views/Leaderboard.vue'
import Repository from './views/Repository.vue'
import Team from './views/Team.vue'
import Contributor from './views/Contributor.vue'
import HowScoringWorks from './views/HowScoringWorks.vue'
const routes = [
{ path: '/', name: 'dashboard', component: Dashboard },
{ path: '/leaderboard', name: 'leaderboard', component: Leaderboard },
{ path: '/how-scoring-works', name: 'how-scoring-works', component: HowScoringWorks },
{ path: '/repos/:owner/:name', name: 'repository', component: Repository },
{ path: '/teams/:slug', name: 'team', component: Team },
{ path: '/contributors/:login', name: 'contributor', component: Contributor },
+8 -6
View File
@@ -23,12 +23,12 @@ const showScoreInChart = ref(false)
<template>
<div>
<!-- Hero Section -->
<header class="py-16 px-4">
<header class="py-10 sm:py-16 px-4">
<div class="container mx-auto text-center animate-fade-in-up">
<h1 class="text-4xl md:text-6xl font-bold mb-4">
<h1 class="text-3xl sm:text-4xl md:text-6xl font-bold mb-3 sm:mb-4">
<span class="gradient-text">Git Velocity</span>
</h1>
<p class="text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
<p class="text-base sm:text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto px-2">
Celebrate your team's achievements and contributions with beautiful insights.
</p>
<!-- Period and Generation Info -->
@@ -49,10 +49,10 @@ const showScoreInChart = ref(false)
</header>
<!-- Velocity Timeline Chart -->
<section v-if="velocityTimeline" class="py-8 px-4">
<section v-if="velocityTimeline" class="py-6 sm:py-8 px-4">
<div class="container mx-auto">
<div class="card">
<div class="flex items-center justify-between mb-6">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 mb-4 sm:mb-6">
<SectionHeader title="Velocity Timeline" icon="fas fa-chart-line" icon-color="text-primary-500" />
<label class="flex items-center space-x-2 text-sm text-gray-600 dark:text-gray-400 cursor-pointer">
<input
@@ -63,7 +63,9 @@ const showScoreInChart = ref(false)
<span>Show Score</span>
</label>
</div>
<VelocityChart :timeline="velocityTimeline" :show-score="showScoreInChart" height="320px" />
<div class="h-[200px] sm:h-[280px] md:h-[320px]">
<VelocityChart :timeline="velocityTimeline" :show-score="showScoreInChart" height="100%" />
</div>
</div>
</div>
</section>
+823
View File
@@ -0,0 +1,823 @@
<script setup>
import SectionHeader from '../components/SectionHeader.vue'
</script>
<template>
<div>
<!-- Hero Section -->
<header class="py-10 sm:py-16 px-4">
<div class="container mx-auto text-center animate-fade-in-up">
<h1 class="text-3xl sm:text-4xl md:text-5xl font-bold mb-3 sm:mb-4">
How <span class="gradient-text">Scoring</span> Works
</h1>
<p class="text-base sm:text-lg md:text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto px-2">
Understanding the point system, leaderboard rankings, and achievement criteria that power Git Velocity.
</p>
</div>
</header>
<!-- Overview Section -->
<section class="py-8 px-4">
<div class="container mx-auto">
<div class="card glass shadow-modern mb-8">
<h2 class="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center">
<i class="fas fa-info-circle mr-3 text-blue-500"></i>
Overview
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-4">
Git Velocity calculates developer contributions by analyzing GitHub activity across configured repositories.
The scoring system is designed to encourage well-rounded contributions including code commits, pull requests,
code reviews, and collaboration.
</p>
<div class="grid sm:grid-cols-3 gap-4 mt-6">
<div class="text-center p-4 bg-primary-50 dark:bg-primary-900/20 rounded-lg">
<i class="fas fa-calculator text-primary-500 text-2xl mb-2"></i>
<h3 class="font-medium text-gray-900 dark:text-gray-100">Point-Based</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">Activities earn configurable points</p>
</div>
<div class="text-center p-4 bg-accent-50 dark:bg-accent-900/20 rounded-lg">
<i class="fas fa-layer-group text-accent-500 text-2xl mb-2"></i>
<h3 class="font-medium text-gray-900 dark:text-gray-100">Aggregated</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">Combined across all repositories</p>
</div>
<div class="text-center p-4 bg-indigo-50 dark:bg-indigo-900/20 rounded-lg">
<i class="fas fa-trophy text-indigo-500 text-2xl mb-2"></i>
<h3 class="font-medium text-gray-900 dark:text-gray-100">Achievement-Driven</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">Unlock badges for milestones</p>
</div>
</div>
</div>
</div>
</section>
<!-- Scoring Section -->
<section id="scoring" class="py-8 px-4">
<div class="container mx-auto">
<SectionHeader title="Point Calculations" icon="fas fa-coins" icon-color="text-yellow-500" />
<div class="space-y-6">
<!-- Score Formula -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-lg sm:text-xl">
<i class="fas fa-function mr-2 text-primary-500"></i>
Score Formula
</h3>
<div class="bg-gray-900 text-gray-100 p-3 sm:p-4 rounded-lg overflow-x-auto mb-4 -mx-2 sm:mx-0">
<pre class="text-xs sm:text-sm font-mono whitespace-pre-wrap sm:whitespace-pre"><code>Total Score = Commits + Lines + PRs + Reviews + Comments + Issues + Bonuses
Where:
Commits = commit_count x 10 pts
Lines = (added x 0.1) + (deleted x 0.05) pts
PRs = (opened x 25) + (merged x 50) pts
Reviews = reviews_given x 30 pts
Comments = review_comments x 5 pts
Issues = (opened x 10) + (closed x 20) + (comments x 5) + (refs x 5) pts
Response = fast review bonus (0-50 pts)
Out of Hrs = commits outside 9-5 x 2 pts</code></pre>
</div>
<p class="text-xs sm:text-sm text-gray-500 dark:text-gray-400">
<i class="fas fa-info-circle mr-1"></i>
All point values are configurable in your <code class="text-primary-600 dark:text-primary-400">.git-velocity.yaml</code> file.
</p>
</div>
<!-- Default Point Values -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fas fa-coins mr-2 text-yellow-500"></i>
Default Point Values
</h3>
<!-- Mobile: Card Layout -->
<div class="grid grid-cols-1 gap-3 sm:hidden">
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-code-commit text-primary-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Commit</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">10 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-flask text-green-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Commit + Tests</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">15 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-plus text-blue-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Lines Added</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">0.1 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-minus text-red-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Lines Deleted</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">0.05 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-code-pull-request text-accent-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">PR Opened</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">25 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-code-merge text-indigo-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">PR Merged</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">50 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-eye text-cyan-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">PR Reviewed</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">30 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-comment text-orange-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Review Comment</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">5 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-bolt text-yellow-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Fast Review &lt;1h</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">50 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-stopwatch text-yellow-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Fast Review &lt;4h</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">25 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-clock text-yellow-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Fast Review &lt;24h</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">10 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-moon text-gray-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Out of Hours</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">2 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-circle-exclamation text-teal-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Issue Opened</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">10 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-circle-check text-green-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Issue Closed</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">20 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-comment-dots text-blue-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Issue Comment</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">5 pts</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2">
<i class="fas fa-link text-accent-500"></i>
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">Issue Reference</span>
</div>
<span class="font-mono font-bold text-primary-600 dark:text-primary-400">5 pts</span>
</div>
</div>
<!-- Desktop: Table Layout -->
<div class="hidden sm:block overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-gray-200 dark:border-gray-700">
<th class="text-left py-3 text-gray-600 dark:text-gray-400">Activity</th>
<th class="text-left py-3 text-gray-600 dark:text-gray-400">Points</th>
<th class="text-left py-3 text-gray-600 dark:text-gray-400">Description</th>
</tr>
</thead>
<tbody class="text-gray-700 dark:text-gray-300">
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-code-commit text-primary-500 mr-2"></i>Commit</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">10</td>
<td class="py-3">Per commit pushed</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-flask text-green-500 mr-2"></i>Commit with Tests</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">15</td>
<td class="py-3">Commit that includes test files</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-plus text-blue-500 mr-2"></i>Lines Added</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">0.1</td>
<td class="py-3">Per meaningful line added</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-minus text-red-500 mr-2"></i>Lines Deleted</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">0.05</td>
<td class="py-3">Per meaningful line removed</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-code-pull-request text-accent-500 mr-2"></i>PR Opened</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">25</td>
<td class="py-3">Per pull request created</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-code-merge text-indigo-500 mr-2"></i>PR Merged</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">50</td>
<td class="py-3">Per pull request merged</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-eye text-cyan-500 mr-2"></i>PR Reviewed</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">30</td>
<td class="py-3">Per PR review submitted</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-comment text-orange-500 mr-2"></i>Review Comment</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">5</td>
<td class="py-3">Per comment on PR reviews</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-bolt text-yellow-500 mr-2"></i>Fast Review (&lt;1h)</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">50</td>
<td class="py-3">Bonus for average response under 1 hour</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-stopwatch text-yellow-500 mr-2"></i>Fast Review (&lt;4h)</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">25</td>
<td class="py-3">Bonus for average response under 4 hours</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-clock text-yellow-500 mr-2"></i>Fast Review (&lt;24h)</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">10</td>
<td class="py-3">Bonus for average response under 24 hours</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-moon text-gray-500 mr-2"></i>Out of Hours</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">2</td>
<td class="py-3">Per commit outside 9am-5pm</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-circle-exclamation text-teal-500 mr-2"></i>Issue Opened</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">10</td>
<td class="py-3">Per issue created</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-circle-check text-green-500 mr-2"></i>Issue Closed</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">20</td>
<td class="py-3">Per issue resolved/closed</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3"><i class="fas fa-comment-dots text-blue-500 mr-2"></i>Issue Comment</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">5</td>
<td class="py-3">Per comment on issues</td>
</tr>
<tr>
<td class="py-3"><i class="fas fa-link text-accent-500 mr-2"></i>Issue Reference</td>
<td class="py-3 font-mono text-primary-600 dark:text-primary-400">5</td>
<td class="py-3">Per commit referencing an issue (#123)</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Meaningful Lines -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fas fa-filter mr-2 text-green-500"></i>
Meaningful Lines
</h3>
<p class="text-gray-600 dark:text-gray-400 mb-4">
By default, Git Velocity uses <strong>meaningful lines</strong> instead of raw line counts.
This filters out noise and rewards actual code contributions:
</p>
<div class="grid sm:grid-cols-2 gap-4">
<div class="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
<h4 class="font-medium text-green-700 dark:text-green-400 mb-2">
<i class="fas fa-check mr-2"></i>Counted as Meaningful
</h4>
<ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<li>Actual code logic</li>
<li>Function definitions</li>
<li>Variable declarations</li>
<li>Import statements</li>
</ul>
</div>
<div class="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg">
<h4 class="font-medium text-red-700 dark:text-red-400 mb-2">
<i class="fas fa-times mr-2"></i>Filtered Out
</h4>
<ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<li>Empty lines / whitespace</li>
<li>Single-line comments</li>
<li>Multi-line comment blocks</li>
<li>Documentation strings</li>
</ul>
</div>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-4">
<i class="fas fa-info-circle mr-1"></i>
Meaningful lines filtering is always enabled to accurately reflect code contributions.
</p>
</div>
</div>
</div>
</section>
<!-- Leaderboard Section -->
<section id="leaderboard-info" class="py-8 px-4">
<div class="container mx-auto">
<SectionHeader title="Leaderboard Rankings" icon="fas fa-list-ol" icon-color="text-accent-500" />
<div class="space-y-6">
<!-- Ranking Process -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fas fa-list-ol mr-2 text-accent-500"></i>
Ranking Process
</h3>
<ol class="space-y-4">
<li class="flex items-start gap-3">
<span class="flex-shrink-0 w-8 h-8 rounded-full bg-primary-100 dark:bg-primary-900/30 flex items-center justify-center text-primary-600 dark:text-primary-400 font-bold">1</span>
<div>
<h4 class="font-medium text-gray-900 dark:text-gray-100">Aggregate Across Repos</h4>
<p class="text-sm text-gray-600 dark:text-gray-400">Metrics from all configured repositories are combined per contributor</p>
</div>
</li>
<li class="flex items-start gap-3">
<span class="flex-shrink-0 w-8 h-8 rounded-full bg-accent-100 dark:bg-accent-900/30 flex items-center justify-center text-accent-600 dark:text-accent-400 font-bold">2</span>
<div>
<h4 class="font-medium text-gray-900 dark:text-gray-100">Calculate Total Score</h4>
<p class="text-sm text-gray-600 dark:text-gray-400">Apply point values to each activity type and sum the breakdown</p>
</div>
</li>
<li class="flex items-start gap-3">
<span class="flex-shrink-0 w-8 h-8 rounded-full bg-indigo-100 dark:bg-indigo-900/30 flex items-center justify-center text-indigo-600 dark:text-indigo-400 font-bold">3</span>
<div>
<h4 class="font-medium text-gray-900 dark:text-gray-100">Sort by Score</h4>
<p class="text-sm text-gray-600 dark:text-gray-400">Contributors are sorted in descending order by total score</p>
</div>
</li>
<li class="flex items-start gap-3">
<span class="flex-shrink-0 w-8 h-8 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center text-blue-600 dark:text-blue-400 font-bold">4</span>
<div>
<h4 class="font-medium text-gray-900 dark:text-gray-100">Assign Ranks & Percentiles</h4>
<p class="text-sm text-gray-600 dark:text-gray-400">Each contributor receives a rank (1st, 2nd...) and percentile position</p>
</div>
</li>
</ol>
</div>
<!-- Top Categories -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fas fa-medal mr-2 text-yellow-500"></i>
Top Achievers
</h3>
<p class="text-gray-600 dark:text-gray-400 mb-4">
Git Velocity tracks top performers in each category:
</p>
<div class="grid sm:grid-cols-2 gap-4">
<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2 mb-2">
<i class="fas fa-trophy text-yellow-500"></i>
<span class="font-medium text-gray-900 dark:text-gray-100">Overall Leader</span>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">Highest total score</p>
</div>
<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2 mb-2">
<i class="fas fa-code-commit text-primary-500"></i>
<span class="font-medium text-gray-900 dark:text-gray-100">Top Committer</span>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">Most commits</p>
</div>
<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2 mb-2">
<i class="fas fa-eye text-accent-500"></i>
<span class="font-medium text-gray-900 dark:text-gray-100">Top Reviewer</span>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">Most reviews given</p>
</div>
<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center gap-2 mb-2">
<i class="fas fa-code-pull-request text-indigo-500"></i>
<span class="font-medium text-gray-900 dark:text-gray-100">Top PR Author</span>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">Most PRs opened</p>
</div>
</div>
</div>
<!-- Team Scoring -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fas fa-users mr-2 text-blue-500"></i>
Team Scoring
</h3>
<p class="text-gray-600 dark:text-gray-400 mb-4">
When teams are configured, Git Velocity calculates team metrics:
</p>
<ul class="space-y-2 text-gray-600 dark:text-gray-400">
<li><i class="fas fa-check text-green-500 mr-2"></i><strong>Total Team Score:</strong> Sum of all member scores</li>
<li><i class="fas fa-check text-green-500 mr-2"></i><strong>Average Score:</strong> Total score / number of members</li>
<li><i class="fas fa-check text-green-500 mr-2"></i><strong>Member Breakdown:</strong> Individual scores and achievements per team member</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Achievements Section -->
<section id="achievements" class="py-8 px-4">
<div class="container mx-auto">
<SectionHeader title="Achievement System" icon="fas fa-trophy" icon-color="text-yellow-500" />
<p class="text-gray-600 dark:text-gray-300 mb-8 text-center">115 achievements across 26 categories with tiered progression</p>
<div class="space-y-6">
<!-- Achievement Categories -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fas fa-trophy mr-2 text-yellow-500"></i>
Achievement Categories
</h3>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Commits -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-code-commit text-primary-500 mr-2"></i>Commits
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: 1, 10, 50, 100, 500, 1000</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
First Steps, Getting Started, Contributor, Committed, Code Machine, Code Warrior
</div>
</div>
<!-- PRs Opened -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-code-pull-request text-accent-500 mr-2"></i>PRs Opened
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: 1, 10, 25, 50, 100, 250</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
PR Pioneer, PR Regular, PR Pro, Merge Master, PR Champion, PR Legend
</div>
</div>
<!-- Reviews -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-eye text-indigo-500 mr-2"></i>Reviews Given
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: 1, 10, 25, 50, 100, 250</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
First Review, Reviewer, Review Regular, Review Expert, Review Guru, Review Master
</div>
</div>
<!-- Review Comments -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-comment text-blue-500 mr-2"></i>Review Comments
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: 10, 50, 100, 250, 500</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
Commentator, Feedback Giver, Code Critic, Feedback Expert, Comment Champion
</div>
</div>
<!-- Lines Added -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-plus text-green-500 mr-2"></i>Lines Added
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: 100, 1K, 5K, 10K, 50K</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
First Hundred, Thousand Lines, Five Thousand, Ten Thousand, Code Mountain
</div>
</div>
<!-- Lines Deleted -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-minus text-red-500 mr-2"></i>Lines Deleted
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: 100, 500, 1K, 5K, 10K</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
Tidying Up, Spring Cleaning, Code Cleaner, Refactoring Hero, Deletion Master
</div>
</div>
<!-- Response Time -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-bolt text-yellow-500 mr-2"></i>Response Time
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: &lt;24h, &lt;4h, &lt;1h</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
Same Day Reviewer, Quick Responder, Speed Demon
</div>
</div>
<!-- Streaks -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-fire text-orange-500 mr-2"></i>Contribution Streaks
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: 3, 7, 14, 30 days</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
Getting Rolling, Week Warrior, Two Week Streak, Month Master
</div>
</div>
<!-- Activity Patterns -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-clock text-cyan-500 mr-2"></i>Activity Patterns
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Early Bird, Night Owl, Weekend Warrior</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
Commits at different times of day unlock special badges
</div>
</div>
<!-- Issues Opened -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-circle-exclamation text-teal-500 mr-2"></i>Issues Opened
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: 1, 5, 10, 25, 50</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
Issue Opener, Reporter, Bug Hunter, Issue Tracker, Issue Master
</div>
</div>
<!-- Issues Closed -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-circle-check text-green-500 mr-2"></i>Issues Closed
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: 1, 5, 10, 25, 50</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
Issue Closer, Problem Solver, Resolver, Issue Crusher, Closure King
</div>
</div>
<!-- Issue Comments -->
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">
<i class="fas fa-comment-dots text-blue-500 mr-2"></i>Issue Comments
</h4>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">Tiers: 5, 10, 25, 50, 100</p>
<div class="text-xs text-gray-600 dark:text-gray-400">
Issue Commenter, Discussion Starter, Feedback Provider, Issue Conversationalist, Discussion Champion
</div>
</div>
</div>
</div>
<!-- Achievement Conditions -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fas fa-unlock mr-2 text-green-500"></i>
How Achievements Are Earned
</h3>
<p class="text-gray-600 dark:text-gray-400 mb-4">
Each achievement has a <strong>condition type</strong> and <strong>threshold</strong>.
When your metrics meet or exceed the threshold, the achievement is unlocked.
</p>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-gray-200 dark:border-gray-700">
<th class="text-left py-2 text-gray-600 dark:text-gray-400">Condition Type</th>
<th class="text-left py-2 text-gray-600 dark:text-gray-400">Metric Checked</th>
<th class="text-left py-2 text-gray-600 dark:text-gray-400">Comparison</th>
</tr>
</thead>
<tbody class="text-gray-700 dark:text-gray-300">
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-2 font-mono text-xs">commit_count</td>
<td class="py-2">Total commits</td>
<td class="py-2">&ge; threshold</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-2 font-mono text-xs">pr_opened_count</td>
<td class="py-2">PRs opened</td>
<td class="py-2">&ge; threshold</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-2 font-mono text-xs">review_count</td>
<td class="py-2">Reviews given</td>
<td class="py-2">&ge; threshold</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-2 font-mono text-xs">avg_review_time_hours</td>
<td class="py-2">Average review response</td>
<td class="py-2">&le; threshold (lower is better)</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-2 font-mono text-xs">longest_streak</td>
<td class="py-2">Consecutive active days</td>
<td class="py-2">&ge; threshold</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-2 font-mono text-xs">perfect_prs</td>
<td class="py-2">PRs with no changes requested</td>
<td class="py-2">&ge; threshold</td>
</tr>
<tr class="border-b border-gray-100 dark:border-gray-800">
<td class="py-2 font-mono text-xs">issues_opened</td>
<td class="py-2">Issues created</td>
<td class="py-2">&ge; threshold</td>
</tr>
<tr>
<td class="py-2 font-mono text-xs">issues_closed</td>
<td class="py-2">Issues resolved/closed</td>
<td class="py-2">&ge; threshold</td>
</tr>
</tbody>
</table>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-4">
<i class="fas fa-shield-halved mr-1"></i>
Achievement definitions are hardcoded and cannot be customized to prevent manipulation.
</p>
</div>
<!-- Tiered Progression -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fas fa-layer-group mr-2 text-accent-500"></i>
Tiered Progression
</h3>
<p class="text-gray-600 dark:text-gray-400 mb-4">
Most achievements have multiple tiers. As you progress, you unlock higher tiers:
</p>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-2 sm:gap-3 mb-4">
<div class="flex items-center gap-2 p-2 sm:p-3 bg-gray-100 dark:bg-gray-800 rounded-lg">
<span class="w-6 h-6 sm:w-8 sm:h-8 flex items-center justify-center rounded-full bg-gray-400 text-white text-xs sm:text-sm font-bold">1</span>
<div class="text-xs sm:text-sm"><span class="font-medium text-gray-700 dark:text-gray-300">Tier 1</span></div>
</div>
<div class="flex items-center gap-2 p-2 sm:p-3 bg-gray-100 dark:bg-gray-800 rounded-lg">
<span class="w-6 h-6 sm:w-8 sm:h-8 flex items-center justify-center rounded-full bg-gray-500 text-white text-xs sm:text-sm font-bold">10</span>
<div class="text-xs sm:text-sm"><span class="font-medium text-gray-700 dark:text-gray-300">Tier 2</span></div>
</div>
<div class="flex items-center gap-2 p-2 sm:p-3 bg-green-50 dark:bg-green-900/20 rounded-lg">
<span class="w-6 h-6 sm:w-8 sm:h-8 flex items-center justify-center rounded-full bg-green-500 text-white text-xs sm:text-sm font-bold">25</span>
<div class="text-xs sm:text-sm"><span class="font-medium text-green-700 dark:text-green-400">Tier 3</span></div>
</div>
<div class="flex items-center gap-2 p-2 sm:p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<span class="w-6 h-6 sm:w-8 sm:h-8 flex items-center justify-center rounded-full bg-blue-500 text-white text-xs sm:text-sm font-bold">50</span>
<div class="text-xs sm:text-sm"><span class="font-medium text-blue-700 dark:text-blue-400">Tier 4</span></div>
</div>
<div class="flex items-center gap-2 p-2 sm:p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
<span class="w-6 h-6 sm:w-8 sm:h-8 flex items-center justify-center rounded-full bg-purple-500 text-white text-xs sm:text-sm font-bold">100</span>
<div class="text-xs sm:text-sm"><span class="font-medium text-purple-700 dark:text-purple-400">Tier 5</span></div>
</div>
<div class="flex items-center gap-2 p-2 sm:p-3 bg-primary-50 dark:bg-primary-900/20 rounded-lg">
<span class="w-6 h-6 sm:w-8 sm:h-8 flex items-center justify-center rounded-full bg-primary-500 text-white text-xs sm:text-sm font-bold">250</span>
<div class="text-xs sm:text-sm"><span class="font-medium text-primary-700 dark:text-primary-400">Tier 6</span></div>
</div>
<div class="flex items-center gap-2 p-2 sm:p-3 bg-orange-50 dark:bg-orange-900/20 rounded-lg">
<span class="w-6 h-6 sm:w-8 sm:h-8 flex items-center justify-center rounded-full bg-orange-500 text-white text-xs sm:text-sm font-bold">500</span>
<div class="text-xs sm:text-sm"><span class="font-medium text-orange-700 dark:text-orange-400">Tier 7</span></div>
</div>
<div class="flex items-center gap-2 p-2 sm:p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
<span class="w-6 h-6 sm:w-8 sm:h-8 flex items-center justify-center rounded-full bg-gradient-to-r from-yellow-500 to-amber-500 text-white text-xs sm:text-sm font-bold">1k+</span>
<div class="text-xs sm:text-sm"><span class="font-medium text-yellow-700 dark:text-yellow-400">Tier 8+</span></div>
</div>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">
The leaderboard shows only the highest tier achieved per category for each contributor.
</p>
</div>
</div>
</div>
</section>
<!-- Data Sources Section -->
<section id="data-sources" class="py-8 px-4">
<div class="container mx-auto">
<SectionHeader title="Data Sources" icon="fab fa-github" icon-color="text-gray-700 dark:text-gray-300" />
<div class="space-y-6">
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fab fa-github mr-2 text-gray-700 dark:text-gray-300"></i>
GitHub API Data
</h3>
<div class="grid sm:grid-cols-2 gap-6">
<div>
<h4 class="font-medium text-gray-800 dark:text-gray-200 mb-3">Commits</h4>
<ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<li><i class="fas fa-check text-green-500 mr-2"></i>SHA, message, timestamp</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Author (login, name, email)</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Additions, deletions, files changed</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Patch/diff for line analysis</li>
</ul>
</div>
<div>
<h4 class="font-medium text-gray-800 dark:text-gray-200 mb-3">Pull Requests</h4>
<ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<li><i class="fas fa-check text-green-500 mr-2"></i>State (open, merged, closed)</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Author and timestamps</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Size (additions, deletions)</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Comments count</li>
</ul>
</div>
<div>
<h4 class="font-medium text-gray-800 dark:text-gray-200 mb-3">Reviews</h4>
<ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<li><i class="fas fa-check text-green-500 mr-2"></i>Review state (approved, changes requested)</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Reviewer login</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Submission timestamp</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Comment count</li>
</ul>
</div>
<div>
<h4 class="font-medium text-gray-800 dark:text-gray-200 mb-3">User Profiles</h4>
<ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<li><i class="fas fa-check text-green-500 mr-2"></i>GitHub login (username)</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Display name</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Avatar URL</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Public email (for deduplication)</li>
</ul>
</div>
</div>
</div>
<!-- Calculated Metrics -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fas fa-calculator mr-2 text-blue-500"></i>
Derived Metrics
</h3>
<p class="text-gray-600 dark:text-gray-400 mb-4">
These metrics are calculated from raw data:
</p>
<div class="grid sm:grid-cols-2 gap-4 text-sm">
<div class="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<strong class="text-gray-900 dark:text-gray-100">Meaningful Lines</strong>
<p class="text-gray-500 dark:text-gray-400">Parsed from commit diffs, filtering comments/whitespace</p>
</div>
<div class="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<strong class="text-gray-900 dark:text-gray-100">Average Review Time</strong>
<p class="text-gray-500 dark:text-gray-400">Time between PR creation and first review</p>
</div>
<div class="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<strong class="text-gray-900 dark:text-gray-100">Contribution Streaks</strong>
<p class="text-gray-500 dark:text-gray-400">Consecutive days with activity</p>
</div>
<div class="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<strong class="text-gray-900 dark:text-gray-100">Perfect PRs</strong>
<p class="text-gray-500 dark:text-gray-400">PRs merged without "changes requested" reviews</p>
</div>
<div class="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<strong class="text-gray-900 dark:text-gray-100">Out of Hours</strong>
<p class="text-gray-500 dark:text-gray-400">Commits outside 9am-5pm based on commit timestamp</p>
</div>
<div class="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<strong class="text-gray-900 dark:text-gray-100">Issue References</strong>
<p class="text-gray-500 dark:text-gray-400">Commits containing #123 patterns (fixes, closes, resolves, refs)</p>
</div>
</div>
</div>
<!-- Bot Filtering -->
<div class="card glass shadow-modern">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
<i class="fas fa-robot mr-2 text-red-500"></i>
Bot Filtering
</h3>
<p class="text-gray-600 dark:text-gray-400 mb-4">
By default, bot activity is excluded from metrics. The following patterns are automatically filtered:
</p>
<div class="flex flex-wrap gap-2">
<code class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">*[bot]</code>
<code class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">dependabot*</code>
<code class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">renovate*</code>
<code class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">github-actions*</code>
<code class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">codecov*</code>
<code class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">snyk*</code>
<code class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">greenkeeper*</code>
<code class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">imgbot*</code>
<code class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">allcontributors*</code>
<code class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">semantic-release*</code>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-4">
<i class="fas fa-cog mr-1"></i>
Enable with <code class="text-primary-600 dark:text-primary-400">include_bots: true</code> or add custom patterns with <code class="text-primary-600 dark:text-primary-400">additional_bot_patterns</code>.
</p>
</div>
</div>
</div>
</section>
</div>
</template>
+39 -3
View File
@@ -1,5 +1,5 @@
<script setup>
import { inject, computed } from 'vue'
import { ref, inject, computed } from 'vue'
import PageHeader from '../components/PageHeader.vue'
import DataTable from '../components/DataTable.vue'
import ContributorRow from '../components/ContributorRow.vue'
@@ -9,7 +9,20 @@ import { formatNumber } from '../composables/formatters'
import { getHighestTierAchievements } from '../composables/achievements'
const globalData = inject('globalData')
const leaderboard = computed(() => globalData.value?.leaderboard || [])
const searchQuery = ref('')
const allContributors = computed(() => globalData.value?.leaderboard || [])
const leaderboard = computed(() => {
if (!searchQuery.value.trim()) return allContributors.value
const query = searchQuery.value.toLowerCase().trim()
return allContributors.value.filter(contributor => {
const name = (contributor.name || '').toLowerCase()
const login = (contributor.login || '').toLowerCase()
return name.includes(query) || login.includes(query)
})
})
const tableColumns = [
{ key: 'rank', label: 'Rank', align: 'left' },
@@ -41,9 +54,32 @@ const categoryIcon = (category) => {
centered
/>
<!-- Leaderboard Table -->
<!-- Search and Leaderboard Table -->
<section class="py-8 px-4">
<div class="container mx-auto max-w-5xl">
<!-- Search Input -->
<div class="mb-6">
<div class="relative max-w-md">
<i class="fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input
v-model="searchQuery"
type="text"
placeholder="Search by name or username..."
class="w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent transition"
/>
<button
v-if="searchQuery"
@click="searchQuery = ''"
class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200"
>
<i class="fas fa-times"></i>
</button>
</div>
<p v-if="searchQuery && leaderboard.length !== allContributors.length" class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Showing {{ leaderboard.length }} of {{ allContributors.length }} contributors
</p>
</div>
<DataTable
:columns="tableColumns"
:items="leaderboard"
+40 -2
View File
@@ -15,6 +15,20 @@ const route = useRoute()
const repository = ref(null)
const loading = ref(true)
const error = ref(null)
const searchQuery = ref('')
const allContributors = computed(() => repository.value?.contributors || [])
const filteredContributors = computed(() => {
if (!searchQuery.value.trim()) return allContributors.value
const query = searchQuery.value.toLowerCase().trim()
return allContributors.value.filter(contributor => {
const name = (contributor.name || '').toLowerCase()
const login = (contributor.login || '').toLowerCase()
return name.includes(query) || login.includes(query)
})
})
const breadcrumbs = computed(() => [
{ label: 'Dashboard', to: '/' },
@@ -104,11 +118,35 @@ watch(() => route.params, loadRepository)
<!-- Contributors -->
<section class="py-8 px-4">
<div class="container mx-auto">
<SectionHeader title="Contributors" icon="fas fa-users" icon-color="text-blue-500" />
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
<SectionHeader title="Contributors" icon="fas fa-users" icon-color="text-blue-500" class="mb-0" />
<!-- Search Input -->
<div class="relative w-full sm:w-64">
<i class="fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input
v-model="searchQuery"
type="text"
placeholder="Search contributors..."
class="w-full pl-10 pr-4 py-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent transition text-sm"
/>
<button
v-if="searchQuery"
@click="searchQuery = ''"
class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200"
>
<i class="fas fa-times"></i>
</button>
</div>
</div>
<p v-if="searchQuery && filteredContributors.length !== allContributors.length" class="mb-4 text-sm text-gray-500 dark:text-gray-400">
Showing {{ filteredContributors.length }} of {{ allContributors.length }} contributors
</p>
<DataTable
:columns="tableColumns"
:items="repository.contributors"
:items="filteredContributors"
empty-icon="fas fa-users"
empty-message="No contributors found"
row-class="hover:bg-gray-50 dark:hover:bg-gray-800/30 transition group"