mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-30 15:24:22 +00:00
Merge branch 'master' of github.com:prowler-cloud/prowler into showdown-demo
This commit is contained in:
commit
37d9da55d3
22 files changed
+147
-283
No files matched your search
@@ -12,7 +12,7 @@ import { Accordion } from "@/components/ui/accordion/Accordion";
|
||||
import { DataTable } from "@/components/ui/table";
|
||||
import { createDict } from "@/lib";
|
||||
import { getComplianceMapper } from "@/lib/compliance/commons";
|
||||
import { ComplianceId, Requirement } from "@/types/compliance";
|
||||
import { Requirement } from "@/types/compliance";
|
||||
import { FindingProps, FindingsResponse } from "@/types/components";
|
||||
|
||||
interface ClientAccordionContentProps {
|
||||
@@ -32,7 +32,7 @@ export const ClientAccordionContent = ({
|
||||
const [expandedFindings, setExpandedFindings] = useState<FindingProps[]>([]);
|
||||
const searchParams = useSearchParams();
|
||||
const pageNumber = searchParams.get("page") || "1";
|
||||
const complianceId = searchParams.get("complianceId") as ComplianceId;
|
||||
const complianceId = searchParams.get("complianceId");
|
||||
const defaultSort = "severity,status,-inserted_at";
|
||||
const sort = searchParams.get("sort") || defaultSort;
|
||||
const loadedPageRef = useRef<string | null>(null);
|
||||
@@ -116,8 +116,8 @@ export const ClientAccordionContent = ({
|
||||
return (
|
||||
<div className="w-full">
|
||||
{renderDetails()}
|
||||
<p className="text-sm text-gray-500">
|
||||
This requirement has no checks; therefore, there are no findings.
|
||||
<p className="mt-2 text-sm font-medium text-gray-800">
|
||||
⚠️ This requirement has no checks; therefore, there are no findings.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@@ -164,7 +164,11 @@ export const ClientAccordionContent = ({
|
||||
);
|
||||
}
|
||||
|
||||
return <div>There are no findings for this regions</div>;
|
||||
return (
|
||||
<div className="text-sm font-medium text-gray-800">
|
||||
⚠️ There are no findings for this regions
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -172,7 +176,7 @@ export const ClientAccordionContent = ({
|
||||
{renderDetails()}
|
||||
|
||||
{checks.length > 0 && (
|
||||
<div className="mb-6 mt-2">
|
||||
<div className="mb-2 mt-2">
|
||||
<Accordion
|
||||
items={accordionChecksItems}
|
||||
variant="light"
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@ export const ComplianceAccordionRequirementTitle = ({
|
||||
}: ComplianceAccordionRequirementTitleProps) => {
|
||||
return (
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
<div className="flex w-3/4 items-center gap-1">
|
||||
<span className="whitespace-nowrap text-sm">{name}</span>
|
||||
<div className="flex w-5/6 items-center gap-1">
|
||||
<span>{name}</span>
|
||||
</div>
|
||||
<StatusFindingBadge status={status} />
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@ export const ComplianceAccordionTitle = ({
|
||||
<div className="flex flex-col items-start justify-between gap-1 md:flex-row md:items-center md:gap-2">
|
||||
<div className="overflow-hidden md:min-w-0 md:flex-1">
|
||||
<span
|
||||
className="block w-full overflow-hidden truncate text-ellipsis text-sm"
|
||||
className="block max-w-[600px] overflow-hidden truncate text-ellipsis text-sm"
|
||||
title={label}
|
||||
>
|
||||
{label.charAt(0).toUpperCase() + label.slice(1)}
|
||||
|
||||
@@ -71,14 +71,15 @@ export const ComplianceCard: React.FC<ComplianceCardProps> = ({
|
||||
return "success";
|
||||
};
|
||||
|
||||
const isPressable =
|
||||
id.includes("ens") ||
|
||||
id.includes("iso") ||
|
||||
id.includes("cis_") ||
|
||||
id.includes("pillar");
|
||||
|
||||
const navigateToDetail = () => {
|
||||
// We will unlock this while developing the rest of complainces.
|
||||
if (
|
||||
!id.includes("ens") &&
|
||||
!id.includes("iso") &&
|
||||
!id.includes("cis_") &&
|
||||
!id.includes("pillar")
|
||||
) {
|
||||
if (!isPressable) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,7 +107,7 @@ export const ComplianceCard: React.FC<ComplianceCardProps> = ({
|
||||
fullWidth
|
||||
isHoverable
|
||||
shadow="sm"
|
||||
isPressable
|
||||
isPressable={isPressable}
|
||||
onPress={navigateToDetail}
|
||||
>
|
||||
<CardBody className="flex flex-row items-center justify-between space-x-4 dark:bg-prowler-blue-800">
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@nextui-org/react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useState } from "react";
|
||||
|
||||
import { CategoryData, RegionData } from "@/types/compliance";
|
||||
import { CategoryData } from "@/types/compliance";
|
||||
|
||||
interface HeatmapChartProps {
|
||||
regions: RegionData[];
|
||||
categories?: CategoryData[];
|
||||
isRegionFiltered?: boolean; // Indicates if a region filter is active
|
||||
filteredRegionName?: string; // Name of the filtered region
|
||||
}
|
||||
|
||||
const getHeatmapColor = (percentage: number): string => {
|
||||
@@ -32,48 +30,32 @@ const capitalizeFirstLetter = (text: string): string => {
|
||||
);
|
||||
};
|
||||
|
||||
export const HeatmapChart = ({
|
||||
regions,
|
||||
categories = [],
|
||||
isRegionFiltered = false,
|
||||
}: HeatmapChartProps) => {
|
||||
export const HeatmapChart = ({ categories = [] }: HeatmapChartProps) => {
|
||||
const { theme } = useTheme();
|
||||
const [hoveredItem, setHoveredItem] = useState<
|
||||
RegionData | CategoryData | null
|
||||
>(null);
|
||||
const [hoveredItem, setHoveredItem] = useState<CategoryData | null>(null);
|
||||
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
|
||||
|
||||
// Determine what data to show and prepare it
|
||||
const dataToShow = isRegionFiltered ? categories : regions;
|
||||
const heatmapData = dataToShow
|
||||
// Use categories data and prepare it
|
||||
const heatmapData = categories
|
||||
.filter((item) => item.totalRequirements > 0)
|
||||
.sort((a, b) => b.failurePercentage - a.failurePercentage)
|
||||
.slice(0, 9); // Exactly 9 items for 3x3 grid
|
||||
|
||||
// Check if there are no items with data
|
||||
if (!dataToShow || dataToShow.length === 0 || heatmapData.length === 0) {
|
||||
const noDataMessage = isRegionFiltered
|
||||
? "No category data available"
|
||||
: "No regional data available";
|
||||
|
||||
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">
|
||||
{isRegionFiltered
|
||||
? "Categories Failure Rate"
|
||||
: "Failure Rate by Region"}
|
||||
Sections Failure Rate
|
||||
</h3>
|
||||
<div className="flex h-[320px] w-full items-center justify-center">
|
||||
<p className="text-sm text-gray-500">{noDataMessage}</p>
|
||||
<p className="text-sm text-gray-500">No category data available</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const handleMouseEnter = (
|
||||
item: RegionData | CategoryData,
|
||||
event: React.MouseEvent,
|
||||
) => {
|
||||
const handleMouseEnter = (item: CategoryData, event: React.MouseEvent) => {
|
||||
setHoveredItem(item);
|
||||
setMousePosition({ x: event.clientX, y: event.clientY });
|
||||
};
|
||||
@@ -90,19 +72,27 @@ export const HeatmapChart = ({
|
||||
<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">
|
||||
{isRegionFiltered
|
||||
? "Categories Failure Rate"
|
||||
: "Failure Rate by Region"}
|
||||
Sections Failure Rate
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="h-full w-full p-4">
|
||||
{/* 3x3 Grid */}
|
||||
<div className="grid h-full w-full grid-cols-3 gap-1">
|
||||
<div className="h-full w-full p-2">
|
||||
<div
|
||||
className={cn(
|
||||
"grid h-full w-full gap-1",
|
||||
heatmapData.length < 3 ? "grid-cols-1" : "grid-cols-3",
|
||||
)}
|
||||
style={{
|
||||
gridTemplateRows:
|
||||
heatmapData.length < 3
|
||||
? `repeat(${heatmapData.length}, ${heatmapData.length}fr)`
|
||||
: `repeat(${Math.min(Math.ceil(heatmapData.length / 3), 3)}, 1fr)`,
|
||||
}}
|
||||
>
|
||||
{heatmapData.map((item) => (
|
||||
<div
|
||||
key={item.name}
|
||||
className="flex items-center justify-center rounded border"
|
||||
className="flex items-center justify-center rounded border p-1"
|
||||
style={{
|
||||
backgroundColor: getHeatmapColor(item.failurePercentage),
|
||||
borderColor: theme === "dark" ? "#374151" : "#e5e7eb",
|
||||
@@ -111,16 +101,15 @@ export const HeatmapChart = ({
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<div className="text-center">
|
||||
<div className="w-full px-1 text-center">
|
||||
<div
|
||||
className="text-xs font-semibold"
|
||||
className="truncate text-xs font-semibold"
|
||||
style={{
|
||||
color: theme === "dark" ? "#ffffff" : "#000000",
|
||||
}}
|
||||
title={capitalizeFirstLetter(item.name)}
|
||||
>
|
||||
{isRegionFiltered
|
||||
? capitalizeFirstLetter(item.name)
|
||||
: item.name}
|
||||
{capitalizeFirstLetter(item.name)}
|
||||
</div>
|
||||
<div
|
||||
className="text-xs"
|
||||
@@ -148,9 +137,7 @@ export const HeatmapChart = ({
|
||||
}}
|
||||
>
|
||||
<div className="mb-1 font-semibold">
|
||||
{isRegionFiltered
|
||||
? capitalizeFirstLetter(hoveredItem.name)
|
||||
: hoveredItem.name}
|
||||
{capitalizeFirstLetter(hoveredItem.name)}
|
||||
</div>
|
||||
<div>Failure Rate: {hoveredItem.failurePercentage}%</div>
|
||||
<div>
|
||||
|
||||
@@ -14,6 +14,7 @@ interface ComplianceHeaderProps {
|
||||
showSearch?: boolean;
|
||||
showRegionFilter?: boolean;
|
||||
framework?: string; // Framework name to show specific filters
|
||||
showProviders?: boolean;
|
||||
}
|
||||
|
||||
export const ComplianceHeader = ({
|
||||
@@ -22,6 +23,7 @@ export const ComplianceHeader = ({
|
||||
showSearch = true,
|
||||
showRegionFilter = true,
|
||||
framework,
|
||||
showProviders = true,
|
||||
}: ComplianceHeaderProps) => {
|
||||
const frameworkFilters = [];
|
||||
|
||||
@@ -56,10 +58,10 @@ export const ComplianceHeader = ({
|
||||
<>
|
||||
{showSearch && <FilterControls search />}
|
||||
<Spacer y={8} />
|
||||
<DataCompliance scans={scans} />
|
||||
{showProviders && <DataCompliance scans={scans} />}
|
||||
{allFilters.length > 0 && (
|
||||
<>
|
||||
<Spacer y={8} />
|
||||
{showProviders && <Spacer y={8} />}
|
||||
<DataTableFilterCustom filters={allFilters} defaultOpen={true} />
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -138,7 +138,7 @@ export const Accordion = ({
|
||||
indicator={<ChevronDown className="text-gray-500" />}
|
||||
classNames={{
|
||||
base: index === 0 || index === 1 ? "my-1" : "my-1",
|
||||
title: "text-sm font-medium max-w-full overflow-hidden truncate",
|
||||
title: "text-sm",
|
||||
subtitle: "text-xs text-gray-500",
|
||||
trigger:
|
||||
"py-2 px-2 rounded-lg data-[hover=true]:bg-gray-50 dark:data-[hover=true]:bg-gray-800/50 w-full flex items-center",
|
||||
|
||||
Reference in new issue
Block a user