mirror of
https://github.com/lukaszraczylo/ghtracker.git
synced 2026-10-14 00:58:36 +00:00
feat: add GitHub repository dashboard with Prometheus metrics
ghtracker authenticates as a GitHub App and scans a configured list of repositories on a schedule. It serves a dashboard, a JSON state endpoint, a health probe and Prometheus metrics. The dashboard lists only items that need attention: failing workflows, pull requests that wait too long or fail their checks, stale issues and projects that failed to scan. It has search, dynamic filters and a button that rescans on demand. The Vue app is embedded in the Go binary. The client protects the GitHub rate limit with conditional requests, a request reserve and Retry-After handling.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import type { Repo } from '@/lib/types'
|
||||
import type { RepoCounts } from '@/stores/tracker'
|
||||
|
||||
defineProps<{
|
||||
repos: Repo[]
|
||||
counts: Map<string, RepoCounts>
|
||||
focused: string | null
|
||||
matching: Set<string>
|
||||
}>()
|
||||
defineEmits<{ select: [fullName: string] }>()
|
||||
|
||||
const SEGMENT = { crit: 'bg-crit', warn: 'bg-warn', ok: 'bg-ok/30 hover:bg-ok/60' } as const
|
||||
|
||||
function summary(c: RepoCounts | undefined): string {
|
||||
if (!c) return 'healthy'
|
||||
const parts = []
|
||||
if (c.crit) parts.push(`${c.crit} critical`)
|
||||
if (c.warn) parts.push(`${c.warn} ${c.warn === 1 ? 'warning' : 'warnings'}`)
|
||||
return parts.join(' · ')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipProvider :delay-duration="80">
|
||||
<div class="flex gap-1" role="group" aria-label="Project health" data-test="strip">
|
||||
<Tooltip v-for="repo in repos" :key="repo.fullName">
|
||||
<TooltipTrigger as-child>
|
||||
<button
|
||||
type="button"
|
||||
:aria-label="`${repo.fullName}: ${summary(counts.get(repo.fullName))}`"
|
||||
:aria-pressed="focused === repo.fullName"
|
||||
class="h-14 min-w-0 flex-1 rounded-[3px] transition-colors focus-visible:outline-offset-1"
|
||||
:class="[
|
||||
SEGMENT[repo.health],
|
||||
focused === repo.fullName
|
||||
? 'ring-foreground ring-2 ring-offset-2 ring-offset-background'
|
||||
: '',
|
||||
]"
|
||||
data-test="segment"
|
||||
@click="$emit('select', repo.fullName)"
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p class="font-medium">{{ repo.fullName }}</p>
|
||||
<p class="opacity-80">{{ summary(counts.get(repo.fullName)) }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</template>
|
||||
Reference in New Issue
Block a user