158 lines
7.0 KiB
TypeScript
158 lines
7.0 KiB
TypeScript
import React, { useState, useMemo } from 'react';
|
|
import { PieChart, Pie, Sector, Label } from 'recharts';
|
|
import { PieSectorDataItem } from "recharts/types/polar/Pie";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/Components/ui/card";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/Components/ui/select";
|
|
import {SecuritySummaryReportResultType} from "@/types/security-summary";
|
|
|
|
export default function NumberOfVulnerabilitiesPieChart({reportData}: { reportData: SecuritySummaryReportResultType }) {
|
|
const vulnerabilityData = useMemo(() => [
|
|
{ name: 'Active Critical', value: reportData.n_vulns.active.critical, fill: 'hsl(var(--chart-1))' },
|
|
{ name: 'Active High', value: reportData.n_vulns.active.high, fill: 'hsl(var(--chart-2))' },
|
|
{ name: 'Active Medium', value: reportData.n_vulns.active.medium, fill: 'hsl(var(--chart-3))' },
|
|
{ name: 'Passive High', value: reportData.n_vulns.passive.high, fill: 'hsl(var(--chart-4))' },
|
|
{ name: 'Passive Medium', value: reportData.n_vulns.passive.medium, fill: 'hsl(var(--chart-5))' }
|
|
], [reportData]);
|
|
|
|
const [activeMetric, setActiveMetric] = useState(vulnerabilityData[0].name);
|
|
|
|
const activeIndex = useMemo(
|
|
() => vulnerabilityData.findIndex((item) => item.name === activeMetric),
|
|
[activeMetric, vulnerabilityData]
|
|
);
|
|
|
|
const metrics = useMemo(() => vulnerabilityData.map((item) => item.name), [vulnerabilityData]);
|
|
|
|
const chartConfig: any = {
|
|
'Active Critical': {
|
|
label: "Active Critical",
|
|
color: 'hsl(var(--chart-1))'
|
|
},
|
|
'Active High': {
|
|
label: "Active High",
|
|
color: 'hsl(var(--chart-2))'
|
|
},
|
|
'Active Medium': {
|
|
label: "Active Medium",
|
|
color: 'hsl(var(--chart-3))'
|
|
},
|
|
'Passive High': {
|
|
label: "Passive High",
|
|
color: 'hsl(var(--chart-4))'
|
|
},
|
|
'Passive Medium': {
|
|
label: "Passive Medium",
|
|
color: 'hsl(var(--chart-5))'
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-0">
|
|
<div className="grid gap-1">
|
|
<CardTitle>Number of Vulnerabilities</CardTitle>
|
|
<CardDescription>Vulnerabilities details</CardDescription>
|
|
</div>
|
|
<Select value={activeMetric} onValueChange={setActiveMetric}>
|
|
<SelectTrigger
|
|
className="ml-auto h-8 w-[180px] rounded-lg"
|
|
aria-label="Select security metric"
|
|
>
|
|
<SelectValue placeholder="Select metric" />
|
|
</SelectTrigger>
|
|
<SelectContent align="end" className="rounded-lg">
|
|
{metrics.map((metric) => (
|
|
<SelectItem
|
|
key={metric}
|
|
value={metric}
|
|
className="rounded-lg [&_span]:flex"
|
|
>
|
|
<div className="flex items-center gap-2 text-xs">
|
|
<span
|
|
className="h-3 w-3 rounded-sm"
|
|
style={{
|
|
backgroundColor: chartConfig[metric]?.color
|
|
}}
|
|
/>
|
|
{chartConfig[metric].label}
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</CardHeader>
|
|
<CardContent className="flex flex-1 justify-center pb-0">
|
|
<div className="mx-auto aspect-square w-full max-w-[400px]">
|
|
<PieChart width={400} height={400}>
|
|
<Pie
|
|
data={vulnerabilityData}
|
|
dataKey="value"
|
|
nameKey="name"
|
|
cx="50%"
|
|
cy="50%"
|
|
innerRadius={60}
|
|
outerRadius={80}
|
|
activeIndex={activeIndex}
|
|
activeShape={({
|
|
outerRadius = 0,
|
|
...props
|
|
}: PieSectorDataItem) => (
|
|
<g>
|
|
<Sector {...props} outerRadius={outerRadius + 10} />
|
|
<Sector
|
|
{...props}
|
|
outerRadius={outerRadius + 25}
|
|
innerRadius={outerRadius + 12}
|
|
/>
|
|
</g>
|
|
)}
|
|
>
|
|
<Label
|
|
content={({ viewBox }) => {
|
|
if (viewBox && "cx" in viewBox && "cy" in viewBox) {
|
|
return (
|
|
<text
|
|
x={viewBox.cx}
|
|
y={viewBox.cy}
|
|
textAnchor="middle"
|
|
dominantBaseline="middle"
|
|
>
|
|
<tspan
|
|
x={viewBox.cx}
|
|
y={viewBox.cy}
|
|
className="fill-foreground text-3xl font-bold"
|
|
>
|
|
{vulnerabilityData[activeIndex].value}
|
|
</tspan>
|
|
<tspan
|
|
x={viewBox.cx}
|
|
y={(viewBox.cy || 0) + 24}
|
|
className="fill-muted-foreground text-sm"
|
|
>
|
|
{vulnerabilityData[activeIndex].name}
|
|
</tspan>
|
|
</text>
|
|
)
|
|
}
|
|
}}
|
|
/>
|
|
</Pie>
|
|
</PieChart>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|