"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; const maxValue = data.length > 0 ? Math.max(...data.map((d) => d.value)) : 0; const calculatedWidth = isEmpty ? item.percentage : (item.percentage ?? (maxValue > 0 ? (item.value / maxValue) * 100 : 0)); // Calculate display percentage (value / total * 100) const displayPercentage = isEmpty ? 0 : (item.percentage ?? (total > 0 ? Math.round((item.value / total) * 100) : 0)); 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) && (
)} {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 */}
{displayPercentage}% {isEmpty ? "0" : item.value.toLocaleString()}
); })}
); }