"use client"; import { Bell } from "lucide-react"; import { useState } from "react"; import { CartesianGrid, Legend, Line, LineChart as RechartsLine, ResponsiveContainer, Tooltip, TooltipProps, XAxis, YAxis, } from "recharts"; import { AlertPill } from "./shared/AlertPill"; import { ChartLegend } from "./shared/ChartLegend"; import { CHART_COLORS } from "./shared/constants"; import { LineConfig, LineDataPoint } from "./types"; interface LineChartProps { data: LineDataPoint[]; lines: LineConfig[]; xLabel?: string; yLabel?: string; height?: number; } interface TooltipPayloadItem { dataKey: string; value: number; stroke: string; name: string; payload: LineDataPoint; } const CustomLineTooltip = ({ active, payload, label, }: TooltipProps) => { if (!active || !payload || payload.length === 0) { return null; } const typedPayload = payload as unknown as TooltipPayloadItem[]; const totalValue = typedPayload.reduce((sum, item) => sum + item.value, 0); return (

{label}

{typedPayload.map((item) => { const newFindings = item.payload[`${item.dataKey}_newFindings`]; const change = item.payload[`${item.dataKey}_change`]; return (
{item.value}
{newFindings !== undefined && (
{newFindings} New Findings
)} {change !== undefined && typeof change === "number" && (

{change > 0 ? "+" : ""} {change}% {" "} Since Last Scan

)}
); })}
); }; const CustomLegend = ({ payload }: any) => { const severityOrder = [ "Informational", "Low", "Medium", "High", "Critical", "Muted", ]; const sortedPayload = [...payload].sort((a, b) => { const indexA = severityOrder.indexOf(a.value); const indexB = severityOrder.indexOf(b.value); return indexA - indexB; }); const items = sortedPayload.map((entry: any) => ({ label: entry.value, color: entry.color, })); return ; }; export function LineChart({ data, lines, xLabel, yLabel, height = 400, }: LineChartProps) { const [hoveredLine, setHoveredLine] = useState(null); return ( } /> } /> {lines.map((line) => { const isHovered = hoveredLine === line.dataKey; const isFaded = hoveredLine !== null && !isHovered; return ( setHoveredLine(line.dataKey)} onMouseLeave={() => setHoveredLine(null)} style={{ transition: "stroke-opacity 0.2s" }} /> ); })} ); }