"use client"; import { CartesianGrid, Legend, ResponsiveContainer, Scatter, ScatterChart, Tooltip, XAxis, YAxis, } from "recharts"; import { AlertPill } from "./shared/alert-pill"; import { ChartLegend } from "./shared/chart-legend"; import { getSeverityColorByRiskScore } from "./shared/utils"; import type { ScatterDataPoint } from "./types"; interface ScatterPlotProps { data: ScatterDataPoint[]; xLabel?: string; yLabel?: string; height?: number; onSelectPoint?: (point: ScatterDataPoint | null) => void; selectedPoint?: ScatterDataPoint | null; } const PROVIDER_COLORS = { AWS: "var(--color-bg-data-aws)", Azure: "var(--color-bg-data-azure)", Google: "var(--color-bg-data-gcp)", Default: "var(--color-text-neutral-tertiary)", }; interface ScatterTooltipProps { active?: boolean; payload?: Array<{ payload: ScatterDataPoint }>; } const CustomTooltip = ({ active, payload }: ScatterTooltipProps) => { if (active && payload && payload.length) { const data = payload[0].payload; const severityColor = getSeverityColorByRiskScore(data.x); return (

{data.name}

{data.x} Risk Score

); } return null; }; interface ScatterDotProps { cx: number; cy: number; payload: ScatterDataPoint; selectedPoint?: ScatterDataPoint | null; onSelectPoint?: (point: ScatterDataPoint) => void; } const CustomScatterDot = ({ cx, cy, payload, selectedPoint, onSelectPoint, }: ScatterDotProps) => { const isSelected = selectedPoint?.name === payload.name; const size = isSelected ? 18 : 8; const fill = isSelected ? "#86DA26" : PROVIDER_COLORS[payload.provider as keyof typeof PROVIDER_COLORS] || "var(--color-text-neutral-tertiary)"; return ( onSelectPoint?.(payload)} /> ); }; interface LegendPayloadItem { value: string; color: string; } interface LegendProps { payload?: LegendPayloadItem[]; } const CustomLegend = ({ payload }: LegendProps) => { const items = (payload || []).map((entry) => ({ label: entry.value, color: entry.color, })); return ; }; export function ScatterPlot({ data, xLabel = "Risk Score", yLabel = "Failed Findings", height = 400, onSelectPoint, selectedPoint, }: ScatterPlotProps) { const handlePointClick = (point: ScatterDataPoint) => { if (onSelectPoint) { if (selectedPoint?.name === point.name) { onSelectPoint(null); } else { onSelectPoint(point); } } }; const dataByProvider = data.reduce( (acc, point) => { const provider = point.provider; if (!acc[provider]) { acc[provider] = []; } acc[provider].push(point); return acc; }, {} as Record, ); return ( } /> } /> {Object.entries(dataByProvider).map(([provider, points]) => ( { const dotProps = props as ScatterDotProps; return ( ); }} /> ))} ); }