feat: KISA detailed view (#7965)

This commit is contained in:
Alejandro Bailo authored and GitHub committed 2025-06-09 09:29:34 +02:00
1 parent 3a99909b75
commit 2eaa37921d
12 files changed
+400 -35

No files matched your search

@@ -116,7 +116,7 @@ export const ClientAccordionContent = ({
return (
<div className="w-full">
{renderDetails()}
<p className="mt-2 text-sm font-medium text-gray-800">
<p className="mb-1 mt-3 text-sm font-medium text-gray-800 dark:text-gray-200">
⚠️ This requirement has no checks; therefore, there are no findings.
</p>
</div>
@@ -165,7 +165,7 @@ export const ClientAccordionContent = ({
}
return (
<div className="text-sm font-medium text-gray-800">
<div className="mb-1 mt-3 text-sm font-medium text-gray-800 dark:text-gray-200">
⚠️ There are no findings for this regions
</div>
);
@@ -75,6 +75,7 @@ export const ComplianceCard: React.FC<ComplianceCardProps> = ({
id.includes("ens") ||
id.includes("iso") ||
id.includes("cis_") ||
id.includes("kisa") ||
id.includes("pillar");
const navigateToDetail = () => {
@@ -14,12 +14,36 @@ import {
import { translateType } from "@/lib/compliance/ens";
import { FailedSection } from "@/types/compliance";
const CustomYAxisTick = (props: any) => {
const { x, y, payload, theme } = props;
const text = payload.value;
const maxLength = 50;
const truncatedText =
text.length > maxLength ? `${text.slice(0, maxLength)}...` : text;
return (
<g transform={`translate(${x},${y})`}>
<text
x={10}
y={-24}
fill={theme === "dark" ? "#94a3b8" : "#374151"}
fontSize={12}
textAnchor="start"
dominantBaseline="middle"
>
{truncatedText}
</text>
</g>
);
};
interface FailedSectionsListProps {
sections: FailedSection[];
}
const title = (
<h3 className="whitespace-nowrap text-xs font-semibold uppercase tracking-wide">
<h3 className="mb-2 whitespace-nowrap text-xs font-semibold uppercase tracking-wide">
Failed Sections (Top 5)
</h3>
);
@@ -74,26 +98,28 @@ export const BarChart = ({ sections }: FailedSectionsListProps) => {
// Check if there are no failed sections
if (!sections || sections.length === 0) {
return (
<div className="flex w-[400px] flex-col items-center justify-between lg:w-[600px]">
<div className="flex w-[400px] flex-col items-center justify-between">
{title}
<div className="flex h-[320px] w-full items-center justify-center">
<p className="text-sm text-gray-500">There are no failed sections</p>
<p className="text-sm text-gray-800 dark:text-gray-200">
There are no failed sections
</p>
</div>
</div>
);
}
return (
<div className="flex h-[320px] w-[400px] flex-col items-center justify-between lg:w-[400px]">
<div>{title}</div>
<div className="flex h-[320px] w-[400px] flex-col items-center justify-between">
{title}
<div className="h-full w-full">
<ResponsiveContainer width="100%" height="100%">
<RechartsBarChart
data={chartData}
layout="vertical"
margin={{ top: 12, bottom: 0 }}
maxBarSize={32}
margin={{ top: 12, bottom: 0, right: 0, left: -56 }}
maxBarSize={30}
>
<XAxis
type="number"
@@ -111,16 +137,7 @@ export const BarChart = ({ sections }: FailedSectionsListProps) => {
<YAxis
type="category"
dataKey="name"
width={1}
tick={{
fontSize: 12,
fill: theme === "dark" ? "#94a3b8" : "#374151",
textAnchor: "start",
style: {
transform: "translateX(10px) translateY(-26px)",
},
width: 400,
}}
tick={(props) => <CustomYAxisTick {...props} theme={theme} />}
axisLine={false}
tickLine={false}
/>
@@ -153,8 +170,13 @@ export const BarChart = ({ sections }: FailedSectionsListProps) => {
}}
>
{props.payload.map((entry: any, index: number) => (
<div key={index} style={{ color: entry.color }}>
{translateType(entry.dataKey)}: {entry.value}
<div key={index} className="max-w-[200px]">
<p>{data.name}</p>
<p>
<span style={{ color: entry.color }}>
{translateType(entry.dataKey)}: {entry.value}
</span>
</p>
</div>
))}
</div>
@@ -180,6 +202,7 @@ export const BarChart = ({ sections }: FailedSectionsListProps) => {
width: "100%",
paddingTop: "16px",
marginBottom: "16px",
marginLeft: "56px",
}}
iconType="circle"
layout="horizontal"
@@ -30,6 +30,12 @@ const capitalizeFirstLetter = (text: string): string => {
);
};
const title = (
<h3 className="mb-2 whitespace-nowrap text-xs font-semibold uppercase tracking-wide">
Sections Failure Rate
</h3>
);
export const HeatmapChart = ({ categories = [] }: HeatmapChartProps) => {
const { theme } = useTheme();
const [hoveredItem, setHoveredItem] = useState<CategoryData | null>(null);
@@ -45,9 +51,7 @@ export const HeatmapChart = ({ categories = [] }: HeatmapChartProps) => {
if (!categories.length || heatmapData.length === 0) {
return (
<div className="flex w-[400px] flex-col items-center justify-between lg:w-[400px]">
<h3 className="whitespace-nowrap text-xs font-semibold uppercase tracking-wide">
Sections Failure Rate
</h3>
{title}
<div className="flex h-[320px] w-full items-center justify-center">
<p className="text-sm text-gray-500">No category data available</p>
</div>
@@ -70,11 +74,7 @@ export const HeatmapChart = ({ categories = [] }: HeatmapChartProps) => {
return (
<div className="flex h-[320px] w-[400px] flex-col items-center justify-between lg:w-[400px]">
<div>
<h3 className="whitespace-nowrap text-xs font-semibold uppercase tracking-wide">
Sections Failure Rate
</h3>
</div>
{title}
<div className="h-full w-full p-2">
<div
@@ -7,7 +7,7 @@ export const ISOCustomDetails = ({
}) => {
return (
<div className="mb-4">
<div className="mb-2 text-sm text-gray-600">
<div className="mb-2 text-sm text-gray-800 dark:text-gray-200">
{requirement.description}
</div>
<div className="flex flex-col gap-2 text-sm">
@@ -0,0 +1,93 @@
import { Requirement } from "@/types/compliance";
export const KISACustomDetails = ({
requirement,
}: {
requirement: Requirement;
}) => {
const auditChecklist = requirement.audit_checklist as string[] | undefined;
const relatedRegulations = requirement.related_regulations as
| string[]
| undefined;
const auditEvidence = requirement.audit_evidence as string[] | undefined;
const nonComplianceCases = requirement.non_compliance_cases as
| string[]
| undefined;
return (
<div className="space-y-4">
{requirement.description && (
<div>
<h4 className="text-muted-foreground mb-1 text-sm font-medium">
Description
</h4>
<p className="text-sm">{requirement.description}</p>
</div>
)}
{auditChecklist && auditChecklist.length > 0 && (
<div>
<h4 className="text-muted-foreground mb-1 text-sm font-medium">
Audit Checklist
</h4>
<div className="space-y-2">
{auditChecklist.map((item: string, index: number) => (
<div key={index} className="flex items-start gap-2">
<span className="text-muted-foreground mt-1 text-xs">•</span>
<p className="text-sm">{item}</p>
</div>
))}
</div>
</div>
)}
{relatedRegulations && relatedRegulations.length > 0 && (
<div>
<h4 className="text-muted-foreground mb-1 text-sm font-medium">
Related Regulations
</h4>
<div className="space-y-2">
{relatedRegulations.map((regulation: string, index: number) => (
<div key={index} className="flex items-start gap-2">
<span className="text-muted-foreground mt-1 text-xs">•</span>
<p className="text-sm">{regulation}</p>
</div>
))}
</div>
</div>
)}
{auditEvidence && auditEvidence.length > 0 && (
<div>
<h4 className="text-muted-foreground mb-1 text-sm font-medium">
Audit Evidence
</h4>
<div className="space-y-2">
{auditEvidence.map((evidence: string, index: number) => (
<div key={index} className="flex items-start gap-2">
<span className="text-muted-foreground mt-1 text-xs">•</span>
<p className="text-sm">{evidence}</p>
</div>
))}
</div>
</div>
)}
{nonComplianceCases && nonComplianceCases.length > 0 && (
<div>
<h4 className="text-muted-foreground mb-1 text-sm font-medium">
Non-Compliance Cases
</h4>
<div className="space-y-2">
{nonComplianceCases.map((caseItem: string, index: number) => (
<div key={index} className="flex items-start gap-2">
<span className="text-muted-foreground mt-1 text-xs">•</span>
<p className="text-sm">{caseItem}</p>
</div>
))}
</div>
</div>
)}
</div>
);
};
@@ -4,7 +4,7 @@ import { Skeleton } from "@nextui-org/react";
export const BarChartSkeleton = () => {
return (
<div className="flex w-[400px] flex-col items-center justify-between lg:w-[600px]">
<div className="flex w-[400px] flex-col items-center justify-between">
{/* Title skeleton */}
<Skeleton className="h-4 w-40 rounded-lg">
<div className="h-4 bg-default-200" />