mirror of
https://github.com/yyhuni/xingrin.git
synced 2026-01-31 11:46:16 +08:00
refactor(frontend): centralize severity styling configuration
- Extract severity color and style definitions into dedicated severity-config module - Create SEVERITY_STYLES constant with unified badge styling for all severity levels - Create SEVERITY_COLORS constant for chart visualization consistency - Add getSeverityStyle() helper function for dynamic severity badge generation - Add SEVERITY_CARD_STYLES and SEVERITY_ICON_BG constants for notification styling - Update dashboard components to use centralized severity configuration - Update fingerprint columns to use getSeverityStyle() helper - Update notification drawer to reference centralized severity styles - Update search result cards to use centralized configuration - Update vulnerability components to import from severity-config module - Eliminate duplicate severity styling definitions across multiple components - Improve maintainability by having single source of truth for severity styling
This commit is contained in:
@@ -26,15 +26,7 @@ import { IconExternalLink } from "@tabler/icons-react"
|
||||
import type { VulnerabilitySeverity } from "@/types/vulnerability.types"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useLocale } from "next-intl"
|
||||
|
||||
// Unified vulnerability severity color configuration (consistent with charts)
|
||||
const severityStyles: Record<VulnerabilitySeverity, string> = {
|
||||
critical: "bg-[#da3633]/10 text-[#da3633] border border-[#da3633]/20 dark:text-[#f85149]",
|
||||
high: "bg-[#d29922]/10 text-[#d29922] border border-[#d29922]/20",
|
||||
medium: "bg-[#d4a72c]/10 text-[#d4a72c] border border-[#d4a72c]/20",
|
||||
low: "bg-[#238636]/10 text-[#238636] border border-[#238636]/20 dark:text-[#3fb950]",
|
||||
info: "bg-[#848d97]/10 text-[#848d97] border border-[#848d97]/20",
|
||||
}
|
||||
import { SEVERITY_STYLES } from "@/lib/severity-config"
|
||||
|
||||
export function RecentVulnerabilities() {
|
||||
const router = useRouter()
|
||||
@@ -54,11 +46,11 @@ export function RecentVulnerabilities() {
|
||||
}
|
||||
|
||||
const severityConfig = useMemo(() => ({
|
||||
critical: { label: tSeverity("critical"), className: severityStyles.critical },
|
||||
high: { label: tSeverity("high"), className: severityStyles.high },
|
||||
medium: { label: tSeverity("medium"), className: severityStyles.medium },
|
||||
low: { label: tSeverity("low"), className: severityStyles.low },
|
||||
info: { label: tSeverity("info"), className: severityStyles.info },
|
||||
critical: { label: tSeverity("critical"), className: SEVERITY_STYLES.critical.className },
|
||||
high: { label: tSeverity("high"), className: SEVERITY_STYLES.high.className },
|
||||
medium: { label: tSeverity("medium"), className: SEVERITY_STYLES.medium.className },
|
||||
low: { label: tSeverity("low"), className: SEVERITY_STYLES.low.className },
|
||||
info: { label: tSeverity("info"), className: SEVERITY_STYLES.info.className },
|
||||
}), [tSeverity])
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
|
||||
@@ -18,15 +18,7 @@ import {
|
||||
} from "@/components/ui/chart"
|
||||
import { Skeleton } from "@/components/ui/skeleton"
|
||||
import { useTranslations } from "next-intl"
|
||||
|
||||
// 漏洞严重程度使用固定语义化颜色
|
||||
const SEVERITY_COLORS = {
|
||||
critical: "#dc2626", // 红色
|
||||
high: "#f97316", // 橙色
|
||||
medium: "#eab308", // 黄色
|
||||
low: "#3b82f6", // 蓝色
|
||||
info: "#6b7280", // 灰色
|
||||
}
|
||||
import { SEVERITY_COLORS } from "@/lib/severity-config"
|
||||
|
||||
export function VulnSeverityChart() {
|
||||
const { data, isLoading } = useAssetStatistics()
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ExpandableCell, ExpandableMonoCell } from "@/components/ui/data-table/e
|
||||
import { ChevronDown, ChevronUp } from "lucide-react"
|
||||
import { useTranslations } from "next-intl"
|
||||
import type { FingerPrintHubFingerprint } from "@/types/fingerprint.types"
|
||||
import { getSeverityStyle } from "@/lib/severity-config"
|
||||
|
||||
interface ColumnOptions {
|
||||
formatDate: (date: string) => string
|
||||
@@ -18,15 +19,7 @@ interface ColumnOptions {
|
||||
* Severity badge with color coding (matching Vulnerabilities style)
|
||||
*/
|
||||
function SeverityBadge({ severity }: { severity: string }) {
|
||||
const severityConfig: Record<string, { className: string }> = {
|
||||
critical: { className: "bg-[#da3633]/10 text-[#da3633] border border-[#da3633]/20 dark:text-[#f85149]" },
|
||||
high: { className: "bg-[#d29922]/10 text-[#d29922] border border-[#d29922]/20" },
|
||||
medium: { className: "bg-[#d4a72c]/10 text-[#d4a72c] border border-[#d4a72c]/20" },
|
||||
low: { className: "bg-[#238636]/10 text-[#238636] border border-[#238636]/20 dark:text-[#3fb950]" },
|
||||
info: { className: "bg-[#848d97]/10 text-[#848d97] border border-[#848d97]/20" },
|
||||
}
|
||||
|
||||
const config = severityConfig[severity?.toLowerCase()] || severityConfig.info
|
||||
const config = getSeverityStyle(severity)
|
||||
|
||||
return (
|
||||
<Badge className={config.className}>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { cn } from "@/lib/utils"
|
||||
import { transformBackendNotification, useNotificationSSE } from "@/hooks/use-notification-sse"
|
||||
import { useMarkAllAsRead, useNotifications } from "@/hooks/use-notifications"
|
||||
import type { Notification, NotificationType, NotificationSeverity } from "@/types/notification.types"
|
||||
import { SEVERITY_CARD_STYLES, SEVERITY_ICON_BG } from "@/lib/severity-config"
|
||||
|
||||
/**
|
||||
* Notification drawer component
|
||||
@@ -199,10 +200,10 @@ export function NotificationDrawer() {
|
||||
}
|
||||
|
||||
const severityCardClassMap: Record<NotificationSeverity, string> = {
|
||||
critical: "border-[#da3633]/30 bg-[#da3633]/5 hover:bg-[#da3633]/10 dark:border-[#f85149]/30 dark:bg-[#f85149]/5 dark:hover:bg-[#f85149]/10",
|
||||
high: "border-[#d29922]/30 bg-[#d29922]/5 hover:bg-[#d29922]/10 dark:border-[#d29922]/30 dark:bg-[#d29922]/5 dark:hover:bg-[#d29922]/10",
|
||||
medium: "border-[#d4a72c]/30 bg-[#d4a72c]/5 hover:bg-[#d4a72c]/10 dark:border-[#d4a72c]/30 dark:bg-[#d4a72c]/5 dark:hover:bg-[#d4a72c]/10",
|
||||
low: "border-[#848d97]/30 bg-[#848d97]/5 hover:bg-[#848d97]/10 dark:border-[#848d97]/30 dark:bg-[#848d97]/5 dark:hover:bg-[#848d97]/10",
|
||||
critical: SEVERITY_CARD_STYLES.critical,
|
||||
high: SEVERITY_CARD_STYLES.high,
|
||||
medium: SEVERITY_CARD_STYLES.medium,
|
||||
low: SEVERITY_CARD_STYLES.low,
|
||||
}
|
||||
|
||||
const getNotificationCardClasses = (severity?: NotificationSeverity) => {
|
||||
@@ -256,10 +257,10 @@ export function NotificationDrawer() {
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={cn(
|
||||
"mt-0.5 p-1.5 rounded-full shrink-0",
|
||||
notification.severity === 'critical' && "bg-[#da3633]/10 dark:bg-[#f85149]/10",
|
||||
notification.severity === 'high' && "bg-[#d29922]/10",
|
||||
notification.severity === 'medium' && "bg-[#d4a72c]/10",
|
||||
(!notification.severity || notification.severity === 'low') && "bg-muted"
|
||||
notification.severity === 'critical' && SEVERITY_ICON_BG.critical,
|
||||
notification.severity === 'high' && SEVERITY_ICON_BG.high,
|
||||
notification.severity === 'medium' && SEVERITY_ICON_BG.medium,
|
||||
(!notification.severity || notification.severity === 'low') && SEVERITY_ICON_BG.info
|
||||
)}>
|
||||
{getNotificationIcon(notification.type, notification.severity)}
|
||||
</div>
|
||||
|
||||
@@ -37,13 +37,15 @@ interface SearchResultCardProps {
|
||||
onViewVulnerability?: (vuln: Vulnerability) => void
|
||||
}
|
||||
|
||||
import { SEVERITY_STYLES } from "@/lib/severity-config"
|
||||
|
||||
// 漏洞严重程度颜色配置
|
||||
const severityColors: Record<string, string> = {
|
||||
critical: "bg-[#da3633]/10 text-[#da3633] border border-[#da3633]/20 dark:text-[#f85149]",
|
||||
high: "bg-[#d29922]/10 text-[#d29922] border border-[#d29922]/20",
|
||||
medium: "bg-[#d4a72c]/10 text-[#d4a72c] border border-[#d4a72c]/20",
|
||||
low: "bg-[#238636]/10 text-[#238636] border border-[#238636]/20 dark:text-[#3fb950]",
|
||||
info: "bg-[#848d97]/10 text-[#848d97] border border-[#848d97]/20",
|
||||
critical: SEVERITY_STYLES.critical.className,
|
||||
high: SEVERITY_STYLES.high.className,
|
||||
medium: SEVERITY_STYLES.medium.className,
|
||||
low: SEVERITY_STYLES.low.className,
|
||||
info: SEVERITY_STYLES.info.className,
|
||||
}
|
||||
|
||||
// 状态码 Badge variant
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Badge } from "@/components/ui/badge"
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
|
||||
import { ExpandableUrlCell } from "@/components/ui/data-table/expandable-cell"
|
||||
import { DataTableColumnHeader } from "@/components/ui/data-table/column-header"
|
||||
import { SEVERITY_STYLES } from "@/lib/severity-config"
|
||||
|
||||
import type { Vulnerability, VulnerabilitySeverity } from "@/types/vulnerability.types"
|
||||
|
||||
@@ -54,12 +55,13 @@ export function createVulnerabilityColumns({
|
||||
t,
|
||||
}: ColumnActions): ColumnDef<Vulnerability>[] {
|
||||
// Unified vulnerability severity color configuration
|
||||
// Color progression: cool (info) → warm (low/medium) → hot (high/critical)
|
||||
const severityConfig: Record<VulnerabilitySeverity, { className: string }> = {
|
||||
critical: { className: "bg-[#da3633]/10 text-[#da3633] border border-[#da3633]/20 dark:text-[#f85149]" },
|
||||
high: { className: "bg-[#d29922]/10 text-[#d29922] border border-[#d29922]/20" },
|
||||
medium: { className: "bg-[#d4a72c]/10 text-[#d4a72c] border border-[#d4a72c]/20" },
|
||||
low: { className: "bg-[#238636]/10 text-[#238636] border border-[#238636]/20 dark:text-[#3fb950]" },
|
||||
info: { className: "bg-[#848d97]/10 text-[#848d97] border border-[#848d97]/20" },
|
||||
critical: { className: SEVERITY_STYLES.critical.className },
|
||||
high: { className: SEVERITY_STYLES.high.className },
|
||||
medium: { className: SEVERITY_STYLES.medium.className },
|
||||
low: { className: SEVERITY_STYLES.low.className },
|
||||
info: { className: SEVERITY_STYLES.info.className },
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
@@ -60,6 +60,7 @@ interface VulnerabilitiesDataTableProps {
|
||||
reviewFilter?: ReviewFilter
|
||||
onReviewFilterChange?: (filter: ReviewFilter) => void
|
||||
pendingCount?: number
|
||||
reviewedCount?: number
|
||||
selectedRows?: Vulnerability[]
|
||||
onBulkMarkAsReviewed?: () => void
|
||||
onBulkMarkAsPending?: () => void
|
||||
@@ -89,6 +90,7 @@ export function VulnerabilitiesDataTable({
|
||||
reviewFilter = "all",
|
||||
onReviewFilterChange,
|
||||
pendingCount = 0,
|
||||
reviewedCount = 0,
|
||||
selectedRows = [],
|
||||
onBulkMarkAsReviewed,
|
||||
onBulkMarkAsPending,
|
||||
@@ -202,6 +204,11 @@ export function VulnerabilitiesDataTable({
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="reviewed">
|
||||
{tVuln("reviewStatus.reviewed")}
|
||||
{reviewedCount > 0 && (
|
||||
<Badge variant="secondary" className="ml-1.5 h-5 min-w-5 rounded-full px-1.5 text-xs">
|
||||
{reviewedCount}
|
||||
</Badge>
|
||||
)}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
|
||||
@@ -167,6 +167,10 @@ export function VulnerabilitiesDetailView({
|
||||
? 0
|
||||
: (targetId ? targetStatsQuery.data?.pendingCount : globalStatsQuery.data?.pendingCount) ?? 0
|
||||
|
||||
const reviewedCount = scanId
|
||||
? 0
|
||||
: (targetId ? targetStatsQuery.data?.reviewedCount : globalStatsQuery.data?.reviewedCount) ?? 0
|
||||
|
||||
|
||||
const formatDate = (dateString: string): string => {
|
||||
return new Date(dateString).toLocaleString(getDateLocale(locale), {
|
||||
@@ -327,6 +331,7 @@ export function VulnerabilitiesDetailView({
|
||||
reviewFilter={reviewFilter}
|
||||
onReviewFilterChange={handleReviewFilterChange}
|
||||
pendingCount={pendingCount}
|
||||
reviewedCount={reviewedCount}
|
||||
selectedRows={selectedVulnerabilities}
|
||||
onBulkMarkAsReviewed={handleBulkMarkAsReviewed}
|
||||
onBulkMarkAsPending={handleBulkMarkAsPending}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Copy, Check, Info, FileCode, Terminal, Database, ExternalLink } from "l
|
||||
import { toast } from "sonner"
|
||||
import { getDateLocale } from "@/lib/date-utils"
|
||||
import type { Vulnerability, VulnerabilitySeverity } from "@/types/vulnerability.types"
|
||||
import { SEVERITY_STYLES, SEVERITY_COLORS } from "@/lib/severity-config"
|
||||
|
||||
interface SeverityConfigItem {
|
||||
variant: "default" | "secondary" | "destructive" | "outline"
|
||||
@@ -24,11 +25,11 @@ interface SeverityConfigItem {
|
||||
}
|
||||
|
||||
const severityConfig: Record<VulnerabilitySeverity, SeverityConfigItem> = {
|
||||
critical: { variant: "outline", color: "bg-[#da3633]", className: "bg-[#da3633]/10 text-[#da3633] border-[#da3633]/20 dark:text-[#f85149]" },
|
||||
high: { variant: "outline", color: "bg-[#d29922]", className: "bg-[#d29922]/10 text-[#d29922] border-[#d29922]/20" },
|
||||
medium: { variant: "outline", color: "bg-[#d4a72c]", className: "bg-[#d4a72c]/10 text-[#d4a72c] border-[#d4a72c]/20" },
|
||||
low: { variant: "outline", color: "bg-[#238636]", className: "bg-[#238636]/10 text-[#238636] border-[#238636]/20 dark:text-[#3fb950]" },
|
||||
info: { variant: "outline", color: "bg-[#848d97]", className: "bg-[#848d97]/10 text-[#848d97] border-[#848d97]/20" },
|
||||
critical: { variant: "outline", color: `bg-[${SEVERITY_COLORS.critical}]`, className: SEVERITY_STYLES.critical.className },
|
||||
high: { variant: "outline", color: `bg-[${SEVERITY_COLORS.high}]`, className: SEVERITY_STYLES.high.className },
|
||||
medium: { variant: "outline", color: `bg-[${SEVERITY_COLORS.medium}]`, className: SEVERITY_STYLES.medium.className },
|
||||
low: { variant: "outline", color: `bg-[${SEVERITY_COLORS.low}]`, className: SEVERITY_STYLES.low.className },
|
||||
info: { variant: "outline", color: `bg-[${SEVERITY_COLORS.info}]`, className: SEVERITY_STYLES.info.className },
|
||||
}
|
||||
|
||||
interface VulnerabilityDetailDialogProps {
|
||||
|
||||
91
frontend/lib/severity-config.ts
Normal file
91
frontend/lib/severity-config.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Global severity color configuration
|
||||
* Color progression: cool (info) → warm (low/medium) → hot (high/critical)
|
||||
*
|
||||
* Used for: vulnerabilities, notifications, fingerprints, etc.
|
||||
*/
|
||||
|
||||
export type SeverityLevel = 'critical' | 'high' | 'medium' | 'low' | 'info'
|
||||
|
||||
export interface SeverityStyle {
|
||||
className: string
|
||||
color: string // solid color for charts/icons
|
||||
bgColor: string // background color
|
||||
}
|
||||
|
||||
// Core color values (for charts, icons, etc.)
|
||||
export const SEVERITY_COLORS = {
|
||||
critical: '#9b1c31',
|
||||
high: '#dc2626',
|
||||
medium: '#f97316',
|
||||
low: '#eab308',
|
||||
info: '#6b7280',
|
||||
} as const
|
||||
|
||||
// Dark mode text colors
|
||||
export const SEVERITY_COLORS_DARK = {
|
||||
critical: '#f87171',
|
||||
high: '#f87171',
|
||||
medium: '#fb923c',
|
||||
low: '#facc15',
|
||||
info: '#9ca3af',
|
||||
} as const
|
||||
|
||||
// Badge/Tag styles with background, text, and border
|
||||
export const SEVERITY_STYLES: Record<SeverityLevel, SeverityStyle> = {
|
||||
critical: {
|
||||
className: 'bg-[#9b1c31]/10 text-[#9b1c31] border border-[#9b1c31]/20 dark:bg-[#9b1c31]/20 dark:text-[#f87171]',
|
||||
color: SEVERITY_COLORS.critical,
|
||||
bgColor: 'rgba(155, 28, 49, 0.1)',
|
||||
},
|
||||
high: {
|
||||
className: 'bg-[#dc2626]/10 text-[#dc2626] border border-[#dc2626]/20 dark:text-[#f87171]',
|
||||
color: SEVERITY_COLORS.high,
|
||||
bgColor: 'rgba(220, 38, 38, 0.1)',
|
||||
},
|
||||
medium: {
|
||||
className: 'bg-[#f97316]/10 text-[#ea580c] border border-[#f97316]/20 dark:text-[#fb923c]',
|
||||
color: SEVERITY_COLORS.medium,
|
||||
bgColor: 'rgba(249, 115, 22, 0.1)',
|
||||
},
|
||||
low: {
|
||||
className: 'bg-[#eab308]/10 text-[#ca8a04] border border-[#eab308]/20 dark:text-[#facc15]',
|
||||
color: SEVERITY_COLORS.low,
|
||||
bgColor: 'rgba(234, 179, 8, 0.1)',
|
||||
},
|
||||
info: {
|
||||
className: 'bg-[#6b7280]/10 text-[#6b7280] border border-[#6b7280]/20 dark:text-[#9ca3af]',
|
||||
color: SEVERITY_COLORS.info,
|
||||
bgColor: 'rgba(107, 114, 128, 0.1)',
|
||||
},
|
||||
}
|
||||
|
||||
// Card styles for notifications (with hover states)
|
||||
export const SEVERITY_CARD_STYLES: Record<SeverityLevel, string> = {
|
||||
critical: 'border-[#9b1c31]/30 bg-[#9b1c31]/5 hover:bg-[#9b1c31]/10 dark:border-[#f87171]/30 dark:bg-[#f87171]/5 dark:hover:bg-[#f87171]/10',
|
||||
high: 'border-[#dc2626]/30 bg-[#dc2626]/5 hover:bg-[#dc2626]/10 dark:border-[#f87171]/30 dark:bg-[#f87171]/5 dark:hover:bg-[#f87171]/10',
|
||||
medium: 'border-[#f97316]/30 bg-[#f97316]/5 hover:bg-[#f97316]/10 dark:border-[#fb923c]/30 dark:bg-[#fb923c]/5 dark:hover:bg-[#fb923c]/10',
|
||||
low: 'border-[#eab308]/30 bg-[#eab308]/5 hover:bg-[#eab308]/10 dark:border-[#facc15]/30 dark:bg-[#facc15]/5 dark:hover:bg-[#facc15]/10',
|
||||
info: 'border-[#6b7280]/30 bg-[#6b7280]/5 hover:bg-[#6b7280]/10 dark:border-[#9ca3af]/30 dark:bg-[#9ca3af]/5 dark:hover:bg-[#9ca3af]/10',
|
||||
}
|
||||
|
||||
// Icon background styles
|
||||
export const SEVERITY_ICON_BG: Record<SeverityLevel, string> = {
|
||||
critical: 'bg-[#9b1c31]/10 dark:bg-[#f87171]/10',
|
||||
high: 'bg-[#dc2626]/10 dark:bg-[#f87171]/10',
|
||||
medium: 'bg-[#f97316]/10 dark:bg-[#fb923c]/10',
|
||||
low: 'bg-[#eab308]/10 dark:bg-[#facc15]/10',
|
||||
info: 'bg-muted',
|
||||
}
|
||||
|
||||
// Helper function to get severity style
|
||||
export function getSeverityStyle(severity: string): SeverityStyle {
|
||||
const normalized = severity?.toLowerCase() as SeverityLevel
|
||||
return SEVERITY_STYLES[normalized] || SEVERITY_STYLES.info
|
||||
}
|
||||
|
||||
// Helper function to get severity color
|
||||
export function getSeverityColor(severity: string): string {
|
||||
const normalized = severity?.toLowerCase() as SeverityLevel
|
||||
return SEVERITY_COLORS[normalized] || SEVERITY_COLORS.info
|
||||
}
|
||||
Reference in New Issue
Block a user