mirror of
https://github.com/lukaszraczylo/gohoarder.git
synced 2026-10-14 00:57:40 +00:00
130 lines
4.5 KiB
Vue
130 lines
4.5 KiB
Vue
<template>
|
|
<div class="flex items-center gap-2">
|
|
<!-- Critical Vulnerabilities -->
|
|
<button
|
|
v-if="counts.critical > 0"
|
|
@click="handleClick('critical')"
|
|
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-red-100 text-red-800 hover:bg-red-200 border border-red-300 transition-colors cursor-pointer"
|
|
:title="`${counts.critical} critical vulnerabilities - click for details`"
|
|
>
|
|
<i class="fas fa-shield-virus mr-1"></i>
|
|
CRITICAL: {{ counts.critical }}
|
|
</button>
|
|
|
|
<!-- High Vulnerabilities -->
|
|
<button
|
|
v-if="counts.high > 0"
|
|
@click="handleClick('high')"
|
|
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-orange-100 text-orange-800 hover:bg-orange-200 border border-orange-300 transition-colors cursor-pointer"
|
|
:title="`${counts.high} high severity vulnerabilities - click for details`"
|
|
>
|
|
<i class="fas fa-exclamation-triangle mr-1"></i>
|
|
HIGH: {{ counts.high }}
|
|
</button>
|
|
|
|
<!-- Moderate Vulnerabilities -->
|
|
<button
|
|
v-if="counts.moderate > 0"
|
|
@click="handleClick('moderate')"
|
|
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-yellow-100 text-yellow-800 hover:bg-yellow-200 border border-yellow-300 transition-colors cursor-pointer"
|
|
:title="`${counts.moderate} moderate severity vulnerabilities - click for details`"
|
|
>
|
|
<i class="fas fa-exclamation-circle mr-1"></i>
|
|
MODERATE: {{ counts.moderate }}
|
|
</button>
|
|
|
|
<!-- Low Vulnerabilities -->
|
|
<button
|
|
v-if="counts.low > 0"
|
|
@click="handleClick('low')"
|
|
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-blue-100 text-blue-800 hover:bg-blue-200 border border-blue-300 transition-colors cursor-pointer"
|
|
:title="`${counts.low} low severity vulnerabilities - click for details`"
|
|
>
|
|
<i class="fas fa-info-circle mr-1"></i>
|
|
LOW: {{ counts.low }}
|
|
</button>
|
|
|
|
<!-- Clean Badge (no vulnerabilities) -->
|
|
<span
|
|
v-if="status === 'clean' && total === 0"
|
|
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-green-100 text-green-800 border border-green-300"
|
|
:title="scannedAt ? `No vulnerabilities found - Scanned ${formatTimestamp(scannedAt)}` : 'No vulnerabilities found'"
|
|
>
|
|
<i class="fas fa-check-circle mr-1"></i>
|
|
CLEAN
|
|
<span v-if="scannedAt" class="ml-1 text-[10px] opacity-70">
|
|
({{ formatRelativeTime(scannedAt) }})
|
|
</span>
|
|
</span>
|
|
|
|
<!-- Pending Badge -->
|
|
<span
|
|
v-if="status === 'pending'"
|
|
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-gray-100 text-gray-700 border border-gray-300"
|
|
title="Security scan in progress"
|
|
>
|
|
<i class="fas fa-spinner fa-spin mr-1"></i>
|
|
SCANNING...
|
|
</span>
|
|
|
|
<!-- Not Scanned Badge -->
|
|
<span
|
|
v-if="status === 'not_scanned' || !scanned"
|
|
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-gray-100 text-gray-600 border border-gray-300"
|
|
title="Not yet scanned for vulnerabilities"
|
|
>
|
|
<i class="fas fa-question-circle mr-1"></i>
|
|
NOT SCANNED
|
|
</span>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import type { VulnerabilityCounts } from '../stores/packages'
|
|
|
|
interface Props {
|
|
scanned?: boolean
|
|
status?: 'clean' | 'vulnerable' | 'pending' | 'not_scanned'
|
|
counts?: VulnerabilityCounts
|
|
total?: number
|
|
scannedAt?: string // ISO 8601 timestamp
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
scanned: false,
|
|
status: 'not_scanned',
|
|
total: 0,
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
click: [severity: string]
|
|
}>()
|
|
|
|
const counts = computed(() => props.counts || { critical: 0, high: 0, moderate: 0, low: 0 })
|
|
|
|
function handleClick(severity: string) {
|
|
emit('click', severity)
|
|
}
|
|
|
|
function formatTimestamp(timestamp: string): string {
|
|
const date = new Date(timestamp)
|
|
return date.toLocaleString()
|
|
}
|
|
|
|
function formatRelativeTime(timestamp: string): string {
|
|
const date = new Date(timestamp)
|
|
const now = new Date()
|
|
const diffMs = now.getTime() - date.getTime()
|
|
const diffMins = Math.floor(diffMs / 60000)
|
|
const diffHours = Math.floor(diffMs / 3600000)
|
|
const diffDays = Math.floor(diffMs / 86400000)
|
|
|
|
if (diffMins < 1) return 'just now'
|
|
if (diffMins < 60) return `${diffMins}m ago`
|
|
if (diffHours < 24) return `${diffHours}h ago`
|
|
if (diffDays < 7) return `${diffDays}d ago`
|
|
return formatTimestamp(timestamp).split(',')[0] // Just the date part
|
|
}
|
|
</script>
|