mirror of
https://github.com/lukaszraczylo/gohoarder.git
synced 2026-10-17 01:17:31 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user