Files
git-velocity/web/src/views/Team.vue
T
lukaszraczylo 7ba4d438dd improvements jan2025 (#9)
* feat(scoring): add tests bonus and fix average calculations

- [x] Add CommitsWithTests metric to track commits with test file changes
- [x] Add TestsBonus to score breakdown (15 points per commit with tests)
- [x] Fix AvgTimeToMerge calculation to use count of PRs with valid data
- [x] Fix AvgReviewTime calculation to use count of reviews with valid data
- [x] Fix AvgPRSize calculation to only include merged PRs
- [x] Add trackActivityDay helper to deduplicate activity tracking code
- [x] Track activity days for PR creation, reviews, and issue comments
- [x] Separate issue close tracking from issue open tracking
- [x] Update early bird window from 5am-9am to 6am-9am
- [x] Add time-based multipliers to velocity timeline scoring
- [x] Update GraphQL query to fetch OPEN, MERGED, CLOSED PRs
- [x] Fix PR filtering logic to handle all PR states correctly
- [x] Improve watch handlers in Vue components to prevent double-loading
- [x] Fix formatDuration to handle zero and negative values
- [x] Update scoring documentation to include Tests component

* refactor: use standard library and consolidate constants

- [x] Replace custom contains function with slices.Contains
- [x] Remove duplicate contains function implementations
- [x] Extract magic numbers to named constants in formatters
- [x] Create constants composable for app-wide values
- [x] Add ESLint configuration with browser globals
- [x] Add lint npm scripts to package.json
- [x] Reorder Vue template attributes for consistency
- [x] Remove unused variable in AchievementProgress
- [x] Add pnpm lock file
2026-01-13 11:39:35 +00:00

126 lines
3.6 KiB
Vue

<script setup>
import { ref, computed, onMounted, watch, inject } from 'vue'
import { useRoute } from 'vue-router'
import PageHeader from '../components/PageHeader.vue'
import LoadingState from '../components/LoadingState.vue'
import ErrorState from '../components/ErrorState.vue'
import StatCard from '../components/StatCard.vue'
import MemberCard from '../components/MemberCard.vue'
import SectionHeader from '../components/SectionHeader.vue'
import { slugify } from '../composables/formatters'
import { DEFAULT_TEAM_COLOR } from '../composables/constants'
const route = useRoute()
const globalData = inject('globalData')
const team = ref(null)
const loading = ref(true)
const error = ref(null)
const breadcrumbs = computed(() => [
{ label: 'Dashboard', to: '/' },
{ label: 'Teams' },
{ label: team.value?.name || route.params.slug }
])
function loadTeam() {
loading.value = true
error.value = null
const teams = globalData.value?.teams || []
const found = teams.find(t => slugify(t.name) === route.params.slug)
if (found) {
team.value = found
} else {
error.value = 'Team not found'
}
loading.value = false
}
onMounted(loadTeam)
// Watch for route changes (navigation to different team)
watch(() => route.params.slug, (newSlug, oldSlug) => {
if (newSlug && newSlug !== oldSlug) {
loadTeam()
}
})
// Watch for globalData changes, but only reload if we don't have team data yet
watch(globalData, (newData, oldData) => {
if (newData && !oldData && (error.value || !team.value)) {
loadTeam()
}
})
</script>
<template>
<div>
<LoadingState v-if="loading" message="Loading team..." />
<ErrorState v-else-if="error" :message="error" />
<template v-else-if="team">
<PageHeader
:title="team.name"
:breadcrumbs="breadcrumbs"
:subtitle="`${team.members?.length || 0} team members`"
>
<template #prefix>
<div
class="w-4 h-4 rounded-full mr-4"
:style="{ backgroundColor: team.color || DEFAULT_TEAM_COLOR }"
></div>
</template>
</PageHeader>
<!-- Team Stats -->
<section class="py-8 px-4">
<div class="container mx-auto">
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<StatCard
:value="team.total_score"
label="Total Score"
icon="fas fa-star"
icon-color="text-yellow-500"
/>
<StatCard
:value="team.aggregated_metrics?.commit_count || 0"
label="Commits"
icon="fas fa-code-commit"
icon-color="text-green-500"
/>
<StatCard
:value="team.aggregated_metrics?.prs_merged || 0"
label="PRs Merged"
icon="fas fa-code-merge"
icon-color="text-purple-500"
/>
<StatCard
:value="team.aggregated_metrics?.reviews_given || 0"
label="Reviews"
icon="fas fa-eye"
icon-color="text-blue-500"
/>
</div>
</div>
</section>
<!-- Team Members -->
<section class="py-8 px-4">
<div class="container mx-auto">
<SectionHeader title="Team Members" icon="fas fa-users" icon-color="text-blue-500" />
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
<MemberCard
v-for="member in team.member_metrics"
:key="member.login"
:member="member"
/>
</div>
</div>
</section>
</template>
</div>
</template>