Files
prowler/ui/app/(prowler)/compliance/page.tsx
Alejandro Bailo 5c1a47d108 feat: compliance detail view + ENS (#7853)
Co-authored-by: Víctor Fernández Poyatos <victor@prowler.com>
2025-06-02 18:20:22 +02:00

170 lines
4.7 KiB
TypeScript

export const dynamic = "force-dynamic";
import { Suspense } from "react";
import { getCompliancesOverview } from "@/actions/compliances";
import { getComplianceOverviewMetadataInfo } from "@/actions/compliances";
import { getProvider } from "@/actions/providers";
import { getScans } from "@/actions/scans";
import {
ComplianceCard,
ComplianceSkeletonGrid,
NoScansAvailable,
} from "@/components/compliance";
import { ComplianceHeader } from "@/components/compliance/compliance-header/compliance-header";
import { ContentLayout } from "@/components/ui";
import { ScanProps, SearchParamsProps } from "@/types";
import { ComplianceOverviewData } from "@/types/compliance";
export default async function Compliance({
searchParams,
}: {
searchParams: SearchParamsProps;
}) {
const searchParamsKey = JSON.stringify(searchParams || {});
const filters = Object.fromEntries(
Object.entries(searchParams).filter(([key]) => key.startsWith("filter[")),
);
const scansData = await getScans({
filters: {
"filter[state]": "completed",
},
pageSize: 50,
});
if (!scansData?.data) {
return <NoScansAvailable />;
}
// Expand scans with provider information
const expandedScansData = await Promise.all(
scansData.data.map(async (scan: ScanProps) => {
const providerId = scan.relationships?.provider?.data?.id;
if (!providerId) {
return { ...scan, providerInfo: null };
}
const formData = new FormData();
formData.append("id", providerId);
const providerData = await getProvider(formData);
return {
...scan,
providerInfo: providerData?.data
? {
provider: providerData.data.attributes.provider,
uid: providerData.data.attributes.uid,
alias: providerData.data.attributes.alias,
}
: null,
};
}),
);
const selectedScanId =
searchParams.scanId || expandedScansData[0]?.id || null;
const query = (filters["filter[search]"] as string) || "";
const metadataInfoData = await getComplianceOverviewMetadataInfo({
query,
filters: {
"filter[scan_id]": selectedScanId,
},
});
const uniqueRegions = metadataInfoData?.data?.attributes?.regions || [];
return (
<ContentLayout title="Compliance" icon="fluent-mdl2:compliance-audit">
{selectedScanId ? (
<>
<ComplianceHeader
scans={expandedScansData}
uniqueRegions={uniqueRegions}
/>
<Suspense key={searchParamsKey} fallback={<ComplianceSkeletonGrid />}>
<SSRComplianceGrid searchParams={searchParams} />
</Suspense>
</>
) : (
<NoScansAvailable />
)}
</ContentLayout>
);
}
const SSRComplianceGrid = async ({
searchParams,
}: {
searchParams: SearchParamsProps;
}) => {
const scanId = searchParams.scanId?.toString() || "";
const regionFilter = searchParams["filter[region__in]"]?.toString() || "";
// Extract all filter parameters
const filters = Object.fromEntries(
Object.entries(searchParams).filter(([key]) => key.startsWith("filter[")),
);
// Extract query from filters
const query = (filters["filter[search]"] as string) || "";
const compliancesData = await getCompliancesOverview({
scanId,
region: regionFilter,
query,
});
// Check if the response contains no data
if (
!compliancesData ||
!compliancesData.data ||
compliancesData.data.length === 0
) {
return (
<div className="flex h-full items-center">
<div className="text-sm text-default-500">
No compliance data available for the selected scan.
</div>
</div>
);
}
// Handle errors returned by the API
if (compliancesData?.errors?.length > 0) {
return (
<div className="flex h-full items-center">
<div className="text-sm text-default-500">Provide a valid scan ID.</div>
</div>
);
}
return (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
{compliancesData.data.map((compliance: ComplianceOverviewData) => {
const { attributes, id } = compliance;
const { framework, version, requirements_passed, total_requirements } =
attributes;
return (
<ComplianceCard
key={id}
title={framework}
version={version}
passingRequirements={requirements_passed}
totalRequirements={total_requirements}
prevPassingRequirements={requirements_passed}
prevTotalRequirements={total_requirements}
scanId={scanId}
complianceId={id}
id={id}
/>
);
})}
</div>
);
};