mirror of
https://github.com/lukaszraczylo/ghtracker.git
synced 2026-10-12 00:45:38 +00:00
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.
53 lines
1.8 KiB
Vue
53 lines
1.8 KiB
Vue
<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>
|