"use client"; import { Bell } from "lucide-react"; import { useState } from "react"; import { cn } from "@/lib/utils"; import { SEVERITY_ORDER } from "./shared/constants"; import { getSeverityColorByName } from "./shared/utils"; import { BarDataPoint } from "./types"; interface HorizontalBarChartProps { data: BarDataPoint[]; height?: number; title?: string; onBarClick?: (dataPoint: BarDataPoint, index: number) => void; } export function HorizontalBarChart({ data, title, onBarClick, }: HorizontalBarChartProps) { const [hoveredIndex, setHoveredIndex] = useState(null); const total = data.reduce((sum, d) => sum + (Number(d.value) || 0), 0); const isEmpty = total <= 0; const emptyData: BarDataPoint[] = [ { name: "Critical", value: 1, percentage: 100 }, { name: "High", value: 1, percentage: 100 }, { name: "Medium", value: 1, percentage: 100 }, { name: "Low", value: 1, percentage: 100 }, { name: "Informational", value: 1, percentage: 100 }, ]; const sortedData = (isEmpty ? emptyData : [...data]).sort((a, b) => { const orderA = SEVERITY_ORDER[a.name as keyof typeof SEVERITY_ORDER] ?? 999; const orderB = SEVERITY_ORDER[b.name as keyof typeof SEVERITY_ORDER] ?? 999; return orderA - orderB; }); return (
{title && (

{title}

)}
{sortedData.map((item, index) => { const isHovered = !isEmpty && hoveredIndex === index; const isFaded = !isEmpty && hoveredIndex !== null && !isHovered; const barColor = isEmpty ? "var(--bg-neutral-tertiary)" : item.color || getSeverityColorByName(item.name) || "var(--bg-neutral-tertiary)"; const isClickable = !isEmpty && onBarClick; return (
!isEmpty && setHoveredIndex(index)} onMouseLeave={() => !isEmpty && setHoveredIndex(null)} onClick={() => { if (isClickable) { const originalIndex = data.findIndex( (d) => d.name === item.name, ); onBarClick(data[originalIndex], originalIndex); } }} onKeyDown={(e) => { if (isClickable && (e.key === "Enter" || e.key === " ")) { e.preventDefault(); const originalIndex = data.findIndex( (d) => d.name === item.name, ); onBarClick(data[originalIndex], originalIndex); } }} > {/* Label */}
{item.name === "Informational" ? "Info" : item.name}
{/* Bar - flexible */}
{(item.value > 0 || isEmpty) && (
d.value))) * 100}%`, backgroundColor: barColor, opacity: isFaded ? 0.5 : 1, }} /> )} {isHovered && (
{/* Title with color chip */}

{item.value.toLocaleString()}{" "} {item.name === "Informational" ? "Info" : item.name}{" "} {item.name === "Fail" || item.name === "Pass" ? "Findings" : "Risk"}

{/* New Findings row */} {item.newFindings !== undefined && (

{item.newFindings} New Findings

)} {/* Change percentage row */} {item.change !== undefined && (

{item.change > 0 ? "+" : ""} {item.change}% Since last scan

)}
)}
{/* Percentage and Count */}
{isEmpty ? "0" : item.percentage}% {isEmpty ? "0" : item.value.toLocaleString()}
); })}
); }