mirror of
https://github.com/lukaszraczylo/git-velocity.git
synced 2026-06-06 22:49:27 +00:00
Remove the light mode.
This commit is contained in:
@@ -244,17 +244,17 @@ const sizeClasses = {
|
||||
</div>
|
||||
|
||||
<!-- Tooltip -->
|
||||
<div class="absolute bottom-full left-1/2 -translate-x-1/2 mb-3 px-3 py-2 bg-gray-900 dark:bg-gray-800 text-white text-xs rounded-xl opacity-0 group-hover/badge:opacity-100 transition-all duration-200 pointer-events-none whitespace-nowrap z-50 shadow-xl border border-white/10">
|
||||
<div class="absolute bottom-full left-1/2 -translate-x-1/2 mb-3 px-3 py-2 bg-gray-800 text-white text-xs rounded-xl opacity-0 group-hover/badge:opacity-100 transition-all duration-200 pointer-events-none whitespace-nowrap z-50 shadow-xl border border-white/10">
|
||||
<div class="font-bold text-sm">{{ getAchievement(achievementId).name }}</div>
|
||||
<div class="text-gray-300 text-[11px] mt-0.5">{{ getAchievement(achievementId).description }}</div>
|
||||
<div class="absolute top-full left-1/2 -translate-x-1/2 border-[6px] border-transparent border-t-gray-900 dark:border-t-gray-800"></div>
|
||||
<div class="absolute top-full left-1/2 -translate-x-1/2 border-[6px] border-transparent border-t-gray-800"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Label (optional) - no truncation -->
|
||||
<span
|
||||
v-if="showLabel"
|
||||
class="text-[11px] font-medium text-gray-600 dark:text-gray-400 text-center leading-tight"
|
||||
class="text-[11px] font-medium text-gray-400 text-center leading-tight"
|
||||
style="max-width: 72px; word-wrap: break-word;"
|
||||
>
|
||||
{{ getAchievement(achievementId).name }}
|
||||
|
||||
@@ -255,7 +255,7 @@ const remainingCount = computed(() => {
|
||||
<div
|
||||
v-for="item in progressItems"
|
||||
:key="item.id"
|
||||
class="bg-gray-50 dark:bg-gray-800/50 rounded-xl p-4 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
|
||||
class="bg-gray-800/50 rounded-xl p-4 hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="flex items-center space-x-3">
|
||||
@@ -266,30 +266,30 @@ const remainingCount = computed(() => {
|
||||
<i class="fas text-white text-sm" :class="item.icon"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
<div class="text-sm font-semibold text-white">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div class="flex items-center space-x-2 text-xs text-gray-600 dark:text-gray-400">
|
||||
<div class="flex items-center space-x-2 text-xs text-gray-400">
|
||||
<span>{{ item.category }}</span>
|
||||
<span class="text-gray-600 dark:text-gray-400">•</span>
|
||||
<span class="text-gray-400">•</span>
|
||||
<span class="font-medium">Tier {{ item.tierIndex }}/{{ item.totalTiers }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-sm font-bold" :class="item.isClose ? 'text-green-500' : 'text-gray-700 dark:text-gray-200'">
|
||||
<div class="text-sm font-bold" :class="item.isClose ? 'text-green-500' : 'text-gray-200'">
|
||||
{{ formatNumber(item.currentValue) }}
|
||||
<span class="text-gray-600 dark:text-gray-400 font-normal">/</span>
|
||||
<span class="text-gray-600 dark:text-gray-400 font-medium">{{ formatNumber(item.target) }}</span>
|
||||
<span class="text-gray-400 font-normal">/</span>
|
||||
<span class="text-gray-400 font-medium">{{ formatNumber(item.target) }}</span>
|
||||
</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400 mt-0.5">
|
||||
<div class="text-xs text-gray-400 mt-0.5">
|
||||
{{ item.remaining > 0 ? `${formatNumber(item.remaining)} to go` : 'Ready to claim!' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div class="h-2.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
|
||||
<div class="h-2.5 bg-gray-700 rounded-full overflow-hidden">
|
||||
<div
|
||||
class="h-full rounded-full transition-all duration-500 ease-out"
|
||||
:class="item.progressColor"
|
||||
@@ -304,14 +304,14 @@ const remainingCount = computed(() => {
|
||||
v-for="(t, idx) in item.allTiers.slice(0, 5)"
|
||||
:key="t.threshold"
|
||||
class="w-1.5 h-1.5 rounded-full"
|
||||
:class="idx < item.tierIndex ? 'bg-green-500' : 'bg-gray-300 dark:bg-gray-600'"
|
||||
:class="idx < item.tierIndex ? 'bg-green-500' : 'bg-gray-600'"
|
||||
:title="`Tier ${idx + 1}: ${t.name} (${formatNumber(t.threshold)})`"
|
||||
></span>
|
||||
<span v-if="item.totalTiers > 5" class="text-[10px] text-gray-600 dark:text-gray-400">+{{ item.totalTiers - 5 }}</span>
|
||||
<span v-if="item.totalTiers > 5" class="text-[10px] text-gray-400">+{{ item.totalTiers - 5 }}</span>
|
||||
</div>
|
||||
<span
|
||||
class="text-xs font-semibold"
|
||||
:class="item.isClose ? 'text-green-500' : 'text-gray-600 dark:text-gray-400'"
|
||||
:class="item.isClose ? 'text-green-500' : 'text-gray-400'"
|
||||
>
|
||||
{{ item.progress }}%
|
||||
</span>
|
||||
@@ -319,16 +319,16 @@ const remainingCount = computed(() => {
|
||||
</div>
|
||||
|
||||
<!-- Show more indicator -->
|
||||
<div v-if="remainingCount > 0" class="text-center text-xs text-gray-600 dark:text-gray-400 pt-2">
|
||||
<div v-if="remainingCount > 0" class="text-center text-xs text-gray-400 pt-2">
|
||||
+{{ remainingCount }} more achievements to unlock
|
||||
</div>
|
||||
|
||||
<!-- Empty state -->
|
||||
<div v-if="!progressItems.length" class="text-center py-8 text-gray-600 dark:text-gray-400">
|
||||
<div v-if="!progressItems.length" class="text-center py-8 text-gray-400">
|
||||
<div class="w-16 h-16 mx-auto mb-3 rounded-2xl bg-gradient-to-br from-yellow-400 to-amber-500 flex items-center justify-center shadow-lg">
|
||||
<i class="fas fa-trophy text-2xl text-white"></i>
|
||||
</div>
|
||||
<p class="font-medium text-gray-700 dark:text-gray-300">All achievements unlocked!</p>
|
||||
<p class="font-medium text-gray-300">All achievements unlocked!</p>
|
||||
<p class="text-sm mt-1">You're a legend!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ defineProps({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center space-x-2 text-sm text-gray-600 dark:text-gray-400 mb-6">
|
||||
<div class="flex items-center space-x-2 text-sm text-gray-400 mb-6">
|
||||
<template v-for="(item, index) in items" :key="index">
|
||||
<RouterLink
|
||||
v-if="item.to"
|
||||
@@ -22,7 +22,7 @@ defineProps({
|
||||
</RouterLink>
|
||||
<span
|
||||
v-else
|
||||
:class="index === items.length - 1 ? 'text-gray-900 dark:text-white' : ''"
|
||||
:class="index === items.length - 1 ? 'text-white' : ''"
|
||||
>
|
||||
{{ item.label }}
|
||||
</span>
|
||||
|
||||
@@ -8,7 +8,7 @@ defineProps({
|
||||
<template>
|
||||
<div
|
||||
:class="[
|
||||
'rounded-xl bg-white dark:bg-gray-800 text-gray-900 dark:text-white border border-gray-200 dark:border-gray-700 shadow',
|
||||
'rounded-xl bg-gray-800 text-white border border-gray-700 shadow',
|
||||
padding ? 'p-6' : '',
|
||||
hover ? 'hover:shadow-lg transition-shadow' : ''
|
||||
]"
|
||||
|
||||
@@ -37,7 +37,7 @@ defineProps({
|
||||
:src="contributor.avatar_url"
|
||||
:name="contributor.login"
|
||||
:size="featured ? 'xl' : 'lg'"
|
||||
class="ring-2 ring-gray-100 dark:ring-gray-700"
|
||||
class="ring-2 ring-gray-700"
|
||||
/>
|
||||
<RankBadge
|
||||
v-if="showRank && rank > 0"
|
||||
@@ -47,10 +47,10 @@ defineProps({
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-bold text-lg text-gray-900 dark:text-white group-hover:text-primary-500 transition-colors">
|
||||
<h3 class="font-bold text-lg text-white group-hover:text-primary-500 transition-colors">
|
||||
{{ contributor.name || contributor.login }}
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
<p class="text-sm text-gray-400">
|
||||
@{{ contributor.login }}
|
||||
</p>
|
||||
<p v-if="contributor.team" class="text-xs text-accent-500 mt-0.5">{{ contributor.team }}</p>
|
||||
@@ -59,16 +59,16 @@ defineProps({
|
||||
</div>
|
||||
|
||||
<!-- Score display -->
|
||||
<div class="flex items-center justify-between py-3 px-4 -mx-2 rounded-lg bg-gradient-to-r from-primary-50 to-accent-50 dark:from-primary-900/20 dark:to-accent-900/20 mb-4">
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Score</span>
|
||||
<span class="text-2xl font-bold bg-gradient-to-r from-primary-600 to-accent-600 dark:from-primary-400 dark:to-accent-400 bg-clip-text text-transparent">
|
||||
<div class="flex items-center justify-between py-3 px-4 -mx-2 rounded-lg bg-gradient-to-r from-primary-900/20 to-accent-900/20 mb-4">
|
||||
<span class="text-sm font-medium text-gray-300">Score</span>
|
||||
<span class="text-2xl font-bold bg-gradient-to-r from-primary-400 to-accent-400 bg-clip-text text-transparent">
|
||||
{{ formatNumber(contributor.score?.total || contributor.score || 0) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Achievements -->
|
||||
<div v-if="contributor.achievements?.length" class="mt-auto">
|
||||
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 mb-2">Achievements</div>
|
||||
<div class="text-xs font-medium text-gray-400 mb-2">Achievements</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<AchievementBadge
|
||||
v-for="achievement in contributor.achievements.slice(0, 8)"
|
||||
@@ -78,7 +78,7 @@ defineProps({
|
||||
/>
|
||||
<span
|
||||
v-if="contributor.achievements.length > 8"
|
||||
class="inline-flex items-center justify-center px-2 h-7 rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 text-xs font-bold"
|
||||
class="inline-flex items-center justify-center px-2 h-7 rounded-lg bg-gray-700 text-gray-300 text-xs font-bold"
|
||||
>
|
||||
+{{ contributor.achievements.length - 8 }}
|
||||
</span>
|
||||
|
||||
@@ -30,7 +30,7 @@ defineProps({
|
||||
class="ring-2 ring-transparent group-hover:ring-primary-500 transition-all"
|
||||
/>
|
||||
<div>
|
||||
<div class="font-medium text-gray-900 dark:text-white group-hover:text-primary-500 transition-colors">
|
||||
<div class="font-medium text-white group-hover:text-primary-500 transition-colors">
|
||||
{{ contributor.name || contributor.login }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
@@ -39,13 +39,13 @@ defineProps({
|
||||
:href="`https://github.com/${contributor.login}`"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="font-medium text-gray-800 dark:text-gray-400 hover:text-primary-500 transition-colors"
|
||||
class="font-medium text-gray-400 hover:text-primary-500 transition-colors"
|
||||
@click.stop
|
||||
>
|
||||
@{{ contributor.login }}
|
||||
<i class="fas fa-external-link-alt text-xs ml-1 opacity-50"></i>
|
||||
</a>
|
||||
<span v-else class="font-medium text-gray-800 dark:text-gray-400">
|
||||
<span v-else class="font-medium text-gray-400">
|
||||
@{{ contributor.login }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,7 @@ defineProps({
|
||||
},
|
||||
rowClass: {
|
||||
type: String,
|
||||
default: 'hover:bg-gray-50 dark:hover:bg-gray-800/30 transition'
|
||||
default: 'hover:bg-gray-800/30 transition'
|
||||
},
|
||||
clickableRows: {
|
||||
type: Boolean,
|
||||
@@ -43,13 +43,13 @@ const getAlignClass = (align) => {
|
||||
<template>
|
||||
<Card :padding="false" class="overflow-hidden">
|
||||
<table class="w-full">
|
||||
<thead class="bg-gray-50 dark:bg-gray-800/50">
|
||||
<thead class="bg-gray-800/50">
|
||||
<tr>
|
||||
<th
|
||||
v-for="col in columns"
|
||||
:key="col.key"
|
||||
:class="[
|
||||
'px-3 sm:px-6 py-3 sm:py-4 text-xs font-semibold text-gray-700 dark:text-gray-400 uppercase tracking-wider',
|
||||
'px-3 sm:px-6 py-3 sm:py-4 text-xs font-semibold text-gray-400 uppercase tracking-wider',
|
||||
getAlignClass(col.align),
|
||||
col.headerClass
|
||||
]"
|
||||
@@ -58,7 +58,7 @@ const getAlignClass = (align) => {
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<tbody class="divide-y divide-gray-700">
|
||||
<tr
|
||||
v-for="(item, index) in items"
|
||||
:key="item.id || item.login || index"
|
||||
@@ -80,8 +80,8 @@ const getAlignClass = (align) => {
|
||||
|
||||
<!-- Empty State -->
|
||||
<div v-if="!items.length" class="text-center py-12">
|
||||
<i :class="emptyIcon" class="text-4xl text-gray-400 dark:text-gray-600 mb-4"></i>
|
||||
<p class="text-gray-600 dark:text-gray-400">{{ emptyMessage }}</p>
|
||||
<i :class="emptyIcon" class="text-4xl text-gray-600 mb-4"></i>
|
||||
<p class="text-gray-400">{{ emptyMessage }}</p>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
@@ -15,7 +15,7 @@ defineProps({
|
||||
<div class="flex items-center justify-center min-h-[60vh]">
|
||||
<div class="text-center">
|
||||
<i :class="icon" class="text-4xl text-red-500 mb-4"></i>
|
||||
<p class="text-gray-600 dark:text-gray-400">{{ message }}</p>
|
||||
<p class="text-gray-400">{{ message }}</p>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,20 +14,20 @@ const generatedAt = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer class="py-8 px-4 mt-16 border-t border-gray-200 dark:border-gray-700">
|
||||
<footer class="py-8 px-4 mt-16 border-t border-gray-700">
|
||||
<div class="container mx-auto text-center">
|
||||
<p class="text-gray-700 dark:text-gray-400">
|
||||
<p class="text-gray-400">
|
||||
Generated by
|
||||
<a
|
||||
href="https://github.com/lukaszraczylo/git-velocity"
|
||||
class="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 font-medium"
|
||||
class="text-primary-400 hover:text-primary-300 font-medium"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Git Velocity
|
||||
</a>
|
||||
</p>
|
||||
<p v-if="generatedAt" class="text-sm text-gray-600 dark:text-gray-500 mt-2">
|
||||
<p v-if="generatedAt" class="text-sm text-gray-500 mt-2">
|
||||
{{ generatedAt }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ defineProps({
|
||||
<div class="flex items-center justify-center min-h-[60vh]">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin text-4xl text-primary-500 mb-4"></i>
|
||||
<p class="text-gray-600 dark:text-gray-400">{{ message }}</p>
|
||||
<p class="text-gray-400">{{ message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -28,37 +28,37 @@ defineProps({
|
||||
<div class="flex items-center space-x-4 mb-4">
|
||||
<Avatar :src="member.avatar_url" :name="member.login" size="lg" />
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-900 dark:text-white">
|
||||
<h3 class="font-semibold text-white">
|
||||
{{ member.name || member.login }}
|
||||
</h3>
|
||||
<p class="text-sm text-gray-800 dark:text-gray-400">@{{ member.login }}</p>
|
||||
<p class="text-sm text-gray-400">@{{ member.login }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4 text-center mb-4">
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
<div class="text-lg font-semibold text-white">
|
||||
{{ formatNumber(member.commit_count) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">Commits</div>
|
||||
<div class="text-xs text-gray-400">Commits</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
<div class="text-lg font-semibold text-white">
|
||||
{{ formatNumber(member.prs_opened) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">PRs</div>
|
||||
<div class="text-xs text-gray-400">PRs</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
<div class="text-lg font-semibold text-white">
|
||||
{{ formatNumber(member.reviews_given) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">Reviews</div>
|
||||
<div class="text-xs text-gray-400">Reviews</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">Score</span>
|
||||
<span class="text-xl font-bold bg-gradient-to-r from-primary-600 to-accent-600 dark:from-primary-400 dark:to-accent-400 bg-clip-text text-transparent">
|
||||
<div class="flex items-center justify-between pt-4 border-t border-gray-700">
|
||||
<span class="text-sm text-gray-400">Score</span>
|
||||
<span class="text-xl font-bold bg-gradient-to-r from-primary-400 to-accent-400 bg-clip-text text-transparent">
|
||||
{{ formatNumber(member.score?.total || 0) }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -72,7 +72,7 @@ defineProps({
|
||||
/>
|
||||
<span
|
||||
v-if="member.achievements.length > 4"
|
||||
class="inline-flex items-center justify-center w-8 h-8 rounded-lg bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 text-xs font-bold"
|
||||
class="inline-flex items-center justify-center w-8 h-8 rounded-lg bg-gray-700 text-gray-300 text-xs font-bold"
|
||||
>
|
||||
+{{ member.achievements.length - 4 }}
|
||||
</span>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup>
|
||||
import { ref, inject, computed } from 'vue'
|
||||
import { RouterLink, useRoute } from 'vue-router'
|
||||
import ThemeToggle from './ThemeToggle.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const globalData = inject('globalData')
|
||||
@@ -11,32 +10,32 @@ const repositories = computed(() => globalData.value?.Repositories || [])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="sticky top-0 z-50 bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-700 shadow-lg">
|
||||
<nav class="sticky top-0 z-50 bg-gray-900/80 backdrop-blur-md border-b border-gray-700 shadow-lg">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex items-center justify-between h-16">
|
||||
<!-- Logo -->
|
||||
<RouterLink to="/" class="flex items-center space-x-2">
|
||||
<i class="fas fa-rocket text-2xl bg-gradient-to-r from-primary-600 to-accent-600 dark:from-primary-400 dark:to-accent-400 bg-clip-text text-transparent"></i>
|
||||
<span class="text-xl font-bold bg-gradient-to-r from-primary-600 to-accent-600 dark:from-primary-400 dark:to-accent-400 bg-clip-text text-transparent">Git Velocity</span>
|
||||
<i class="fas fa-rocket text-2xl bg-gradient-to-r from-primary-400 to-accent-400 bg-clip-text text-transparent"></i>
|
||||
<span class="text-xl font-bold bg-gradient-to-r from-primary-400 to-accent-400 bg-clip-text text-transparent">Git Velocity</span>
|
||||
</RouterLink>
|
||||
|
||||
<!-- Desktop Navigation -->
|
||||
<div class="hidden md:flex items-center space-x-6">
|
||||
<RouterLink
|
||||
to="/"
|
||||
:class="route.path === '/' ? 'text-primary-500 font-medium' : 'text-gray-800 dark:text-gray-200 font-medium hover:text-primary-600 dark:hover:text-primary-400 transition-colors'"
|
||||
:class="route.path === '/' ? 'text-primary-500 font-medium' : 'text-gray-200 font-medium hover:text-primary-400 transition-colors'"
|
||||
>
|
||||
Dashboard
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
to="/leaderboard"
|
||||
:class="route.path === '/leaderboard' ? 'text-primary-500 font-medium' : 'text-gray-800 dark:text-gray-200 font-medium hover:text-primary-600 dark:hover:text-primary-400 transition-colors'"
|
||||
:class="route.path === '/leaderboard' ? 'text-primary-500 font-medium' : 'text-gray-200 font-medium hover:text-primary-400 transition-colors'"
|
||||
>
|
||||
Leaderboard
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
to="/how-scoring-works"
|
||||
:class="route.path === '/how-scoring-works' ? 'text-primary-500 font-medium' : 'text-gray-800 dark:text-gray-200 font-medium hover:text-primary-600 dark:hover:text-primary-400 transition-colors'"
|
||||
:class="route.path === '/how-scoring-works' ? 'text-primary-500 font-medium' : 'text-gray-200 font-medium hover:text-primary-400 transition-colors'"
|
||||
>
|
||||
How Scoring Works
|
||||
</RouterLink>
|
||||
@@ -44,27 +43,23 @@ const repositories = computed(() => globalData.value?.Repositories || [])
|
||||
v-for="repo in repositories"
|
||||
:key="`${repo.Owner}/${repo.Name}`"
|
||||
:to="`/repos/${repo.Owner}/${repo.Name}`"
|
||||
:class="route.path.includes(`/repos/${repo.Owner}/${repo.Name}`) ? 'text-primary-500 font-medium' : 'text-gray-800 dark:text-gray-200 font-medium hover:text-primary-600 dark:hover:text-primary-400 transition-colors'"
|
||||
:class="route.path.includes(`/repos/${repo.Owner}/${repo.Name}`) ? 'text-primary-500 font-medium' : 'text-gray-200 font-medium hover:text-primary-400 transition-colors'"
|
||||
>
|
||||
{{ repo.Name }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex items-center space-x-4">
|
||||
<ThemeToggle />
|
||||
|
||||
<button
|
||||
@click="mobileMenuOpen = !mobileMenuOpen"
|
||||
class="md:hidden p-2 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition"
|
||||
>
|
||||
<i class="fas fa-bars text-gray-700 dark:text-gray-200"></i>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Mobile Menu Button -->
|
||||
<button
|
||||
@click="mobileMenuOpen = !mobileMenuOpen"
|
||||
class="md:hidden p-2 rounded-lg hover:bg-gray-700 transition"
|
||||
>
|
||||
<i class="fas fa-bars text-gray-200"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu -->
|
||||
<div v-if="mobileMenuOpen" class="md:hidden py-2 border-t border-gray-200 dark:border-gray-700">
|
||||
<div v-if="mobileMenuOpen" class="md:hidden py-2 border-t border-gray-700">
|
||||
<div class="flex flex-col space-y-1">
|
||||
<RouterLink
|
||||
to="/"
|
||||
@@ -72,8 +67,8 @@ const repositories = computed(() => globalData.value?.Repositories || [])
|
||||
: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'
|
||||
? 'bg-primary-900/20 text-primary-400'
|
||||
: 'text-gray-200 hover:bg-gray-800'
|
||||
]"
|
||||
>
|
||||
<i class="fas fa-home mr-3 w-5 text-center"></i>Dashboard
|
||||
@@ -84,8 +79,8 @@ const repositories = computed(() => globalData.value?.Repositories || [])
|
||||
: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'
|
||||
? 'bg-primary-900/20 text-primary-400'
|
||||
: 'text-gray-200 hover:bg-gray-800'
|
||||
]"
|
||||
>
|
||||
<i class="fas fa-trophy mr-3 w-5 text-center"></i>Leaderboard
|
||||
@@ -96,8 +91,8 @@ const repositories = computed(() => globalData.value?.Repositories || [])
|
||||
: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'
|
||||
? 'bg-primary-900/20 text-primary-400'
|
||||
: 'text-gray-200 hover:bg-gray-800'
|
||||
]"
|
||||
>
|
||||
<i class="fas fa-calculator mr-3 w-5 text-center"></i>How Scoring Works
|
||||
@@ -110,8 +105,8 @@ const repositories = computed(() => globalData.value?.Repositories || [])
|
||||
: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'
|
||||
? 'bg-primary-900/20 text-primary-400'
|
||||
: 'text-gray-200 hover:bg-gray-800'
|
||||
]"
|
||||
>
|
||||
<i class="fas fa-code-branch mr-3 w-5 text-center"></i>{{ repo.Name }}
|
||||
|
||||
@@ -38,11 +38,11 @@ defineProps({
|
||||
<slot name="prefix"></slot>
|
||||
<h1 class="text-4xl font-bold mb-4">
|
||||
<i v-if="icon" :class="[icon, iconColor]" class="mr-3"></i>
|
||||
<span class="bg-gradient-to-r from-primary-600 to-accent-600 dark:from-primary-400 dark:to-accent-400 bg-clip-text text-transparent">{{ title }}</span>
|
||||
<span class="bg-gradient-to-r from-primary-400 to-accent-400 bg-clip-text text-transparent">{{ title }}</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<p v-if="subtitle || $slots.subtitle" class="text-gray-600 dark:text-gray-300">
|
||||
<p v-if="subtitle || $slots.subtitle" class="text-gray-300">
|
||||
<slot name="subtitle">{{ subtitle }}</slot>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const rankClass = computed(() => {
|
||||
if (props.rank === 1) return 'bg-gradient-to-r from-yellow-400 to-amber-500'
|
||||
if (props.rank === 2) return 'bg-gradient-to-r from-slate-400 to-slate-500'
|
||||
if (props.rank === 3) return 'bg-gradient-to-r from-amber-600 to-amber-700'
|
||||
return 'bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300'
|
||||
return 'bg-gray-700 text-gray-300'
|
||||
})
|
||||
|
||||
const classes = computed(() => sizeClasses[props.size] || sizeClasses.md)
|
||||
|
||||
@@ -18,31 +18,31 @@ defineProps({
|
||||
>
|
||||
<Card hover>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="font-semibold text-gray-900 dark:text-white group-hover:text-primary-500 transition">
|
||||
<h3 class="font-semibold text-white group-hover:text-primary-500 transition">
|
||||
{{ repo.name }}
|
||||
</h3>
|
||||
<i class="fas fa-arrow-right text-gray-600 dark:text-gray-400 group-hover:text-primary-500 transition"></i>
|
||||
<i class="fas fa-arrow-right text-gray-400 group-hover:text-primary-500 transition"></i>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">{{ repo.owner }}/{{ repo.name }}</p>
|
||||
<p class="text-sm text-gray-400 mb-4">{{ repo.owner }}/{{ repo.name }}</p>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4 text-center">
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
<div class="text-lg font-semibold text-white">
|
||||
{{ formatNumber(repo.total_commits) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">Commits</div>
|
||||
<div class="text-xs text-gray-400">Commits</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
<div class="text-lg font-semibold text-white">
|
||||
{{ formatNumber(repo.total_prs) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">PRs</div>
|
||||
<div class="text-xs text-gray-400">PRs</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
<div class="text-lg font-semibold text-white">
|
||||
{{ repo.active_contributors }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">Contributors</div>
|
||||
<div class="text-xs text-gray-400">Contributors</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -16,7 +16,7 @@ defineProps({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-6">
|
||||
<h2 class="text-2xl font-bold text-white mb-6">
|
||||
<i v-if="icon" :class="[icon, iconColor]" class="mr-2"></i>{{ title }}
|
||||
<slot name="suffix"></slot>
|
||||
</h2>
|
||||
|
||||
@@ -7,7 +7,7 @@ defineProps({
|
||||
label: { type: String, required: true },
|
||||
icon: { type: String, default: '' },
|
||||
iconColor: { type: String, default: 'text-gray-500' },
|
||||
valueClass: { type: String, default: 'bg-gradient-to-r from-primary-600 to-accent-600 dark:from-primary-400 dark:to-accent-400 bg-clip-text text-transparent' },
|
||||
valueClass: { type: String, default: 'bg-gradient-to-r from-primary-400 to-accent-400 bg-clip-text text-transparent' },
|
||||
delay: { type: String, default: '0s' }
|
||||
})
|
||||
</script>
|
||||
@@ -19,7 +19,7 @@ defineProps({
|
||||
<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-xs sm:text-sm text-gray-700 dark:text-gray-400 mt-1 truncate">{{ label }}</div>
|
||||
<div class="text-xs sm:text-sm text-gray-400 mt-1 truncate">{{ label }}</div>
|
||||
</div>
|
||||
<div v-if="icon" class="text-2xl sm:text-3xl opacity-50 ml-2 flex-shrink-0" :class="iconColor">
|
||||
<i :class="icon"></i>
|
||||
|
||||
@@ -19,7 +19,7 @@ defineProps({
|
||||
>
|
||||
<Card hover>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="font-semibold text-gray-900 dark:text-white group-hover:text-primary-500 transition">
|
||||
<h3 class="font-semibold text-white group-hover:text-primary-500 transition">
|
||||
{{ team.name }}
|
||||
</h3>
|
||||
<span
|
||||
@@ -34,7 +34,7 @@ defineProps({
|
||||
</template>
|
||||
<span
|
||||
v-if="(team.members?.length || 0) > 5"
|
||||
class="w-8 h-8 rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center text-gray-600 dark:text-gray-300 text-xs font-bold"
|
||||
class="w-8 h-8 rounded-full bg-gray-700 flex items-center justify-center text-gray-300 text-xs font-bold"
|
||||
>
|
||||
+{{ team.members.length - 5 }}
|
||||
</span>
|
||||
@@ -42,16 +42,16 @@ defineProps({
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 text-center">
|
||||
<div>
|
||||
<div class="text-lg font-semibold bg-gradient-to-r from-primary-600 to-accent-600 dark:from-primary-400 dark:to-accent-400 bg-clip-text text-transparent">
|
||||
<div class="text-lg font-semibold bg-gradient-to-r from-primary-400 to-accent-400 bg-clip-text text-transparent">
|
||||
{{ formatNumber(team.total_score) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">Total Score</div>
|
||||
<div class="text-xs text-gray-400">Total Score</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
<div class="text-lg font-semibold text-white">
|
||||
{{ team.members?.length || 0 }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">Members</div>
|
||||
<div class="text-xs text-gray-400">Members</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
|
||||
const isDark = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
const savedTheme = localStorage.getItem('theme')
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
|
||||
isDark.value = savedTheme === 'dark' || (!savedTheme && prefersDark)
|
||||
updateTheme()
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
||||
if (!localStorage.getItem('theme')) {
|
||||
isDark.value = e.matches
|
||||
updateTheme()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
watch(isDark, () => {
|
||||
updateTheme()
|
||||
localStorage.setItem('theme', isDark.value ? 'dark' : 'light')
|
||||
})
|
||||
|
||||
function updateTheme() {
|
||||
if (isDark.value) {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
isDark.value = !isDark.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
@click="toggle"
|
||||
class="p-2 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition"
|
||||
:aria-label="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
|
||||
>
|
||||
<i v-if="isDark" class="fas fa-moon text-purple-400"></i>
|
||||
<i v-else class="fas fa-sun text-yellow-500"></i>
|
||||
</button>
|
||||
</template>
|
||||
@@ -51,6 +51,15 @@ const chartData = computed(() => {
|
||||
|
||||
const isMobile = ref(window.innerWidth < 640)
|
||||
|
||||
// Dark mode colors
|
||||
const themeColors = {
|
||||
gridColor: 'rgba(255, 255, 255, 0.1)',
|
||||
textColor: 'rgba(255, 255, 255, 0.7)',
|
||||
tooltipBg: 'rgba(30, 30, 30, 0.95)',
|
||||
tooltipText: '#fff',
|
||||
tooltipBorder: 'rgba(255, 255, 255, 0.1)'
|
||||
}
|
||||
|
||||
const chartOptions = computed(() => ({
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
@@ -65,13 +74,18 @@ const chartOptions = computed(() => ({
|
||||
usePointStyle: true,
|
||||
padding: isMobile.value ? 10 : 20,
|
||||
boxWidth: isMobile.value ? 8 : 12,
|
||||
color: themeColors.textColor,
|
||||
font: {
|
||||
size: isMobile.value ? 10 : 12
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||||
backgroundColor: themeColors.tooltipBg,
|
||||
titleColor: themeColors.tooltipText,
|
||||
bodyColor: themeColors.tooltipText,
|
||||
borderColor: themeColors.tooltipBorder,
|
||||
borderWidth: 1,
|
||||
padding: isMobile.value ? 8 : 12,
|
||||
titleFont: {
|
||||
size: isMobile.value ? 12 : 14
|
||||
@@ -92,6 +106,7 @@ const chartOptions = computed(() => ({
|
||||
display: false
|
||||
},
|
||||
ticks: {
|
||||
color: themeColors.textColor,
|
||||
font: {
|
||||
size: isMobile.value ? 9 : 11
|
||||
},
|
||||
@@ -103,9 +118,10 @@ const chartOptions = computed(() => ({
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0, 0, 0, 0.05)'
|
||||
color: themeColors.gridColor
|
||||
},
|
||||
ticks: {
|
||||
color: themeColors.textColor,
|
||||
font: {
|
||||
size: isMobile.value ? 9 : 11
|
||||
},
|
||||
@@ -177,7 +193,7 @@ watch(() => props.showScore, () => {
|
||||
<div class="velocity-chart" :style="{ height }">
|
||||
<canvas ref="chartRef"></canvas>
|
||||
<div v-if="!timeline?.labels?.length" class="flex items-center justify-center h-full">
|
||||
<p class="text-gray-500 dark:text-gray-400">No velocity data available</p>
|
||||
<p class="text-gray-400">No velocity data available</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user