diff --git a/actions/overview/overview.ts b/actions/overview/overview.ts
new file mode 100644
index 0000000000..8d83ab7088
--- /dev/null
+++ b/actions/overview/overview.ts
@@ -0,0 +1,49 @@
+"use server";
+import { revalidatePath } from "next/cache";
+import { redirect } from "next/navigation";
+
+import { auth } from "@/auth.config";
+import { parseStringify } from "@/lib";
+
+export const getProvidersOverview = async ({
+ page = 1,
+ query = "",
+ sort = "",
+ filters = {},
+}) => {
+ const session = await auth();
+
+ if (isNaN(Number(page)) || page < 1) redirect("/providers-overview");
+
+ const keyServer = process.env.API_BASE_URL;
+ const url = new URL(`${keyServer}/overviews/providers`);
+
+ if (page) url.searchParams.append("page[number]", page.toString());
+ if (query) url.searchParams.append("filter[search]", query);
+ if (sort) url.searchParams.append("sort", sort);
+
+ // Handle multiple filters
+ Object.entries(filters).forEach(([key, value]) => {
+ if (key !== "filter[search]") {
+ url.searchParams.append(key, String(value));
+ }
+ });
+
+ try {
+ const response = await fetch(url.toString(), {
+ headers: {
+ Accept: "application/vnd.api+json",
+ Authorization: `Bearer ${session?.accessToken}`,
+ },
+ });
+
+ const data = await response.json();
+ const parsedData = parseStringify(data);
+ revalidatePath("/providers-overview");
+ return parsedData;
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error("Error fetching providers overview:", error);
+ return undefined;
+ }
+};
diff --git a/app/(prowler)/page.tsx b/app/(prowler)/page.tsx
index 335b53b4f4..bae0d835c4 100644
--- a/app/(prowler)/page.tsx
+++ b/app/(prowler)/page.tsx
@@ -1,32 +1,42 @@
import { Spacer } from "@nextui-org/react";
+import { Suspense } from "react";
+import { getProvidersOverview } from "@/actions/overview/overview";
+import {
+ ProvidersOverview,
+ SkeletonProvidersOverview,
+} from "@/components/overview";
import { Header } from "@/components/ui";
export default function Home() {
return (
<>
-
- {/*
*/}
+
+
+
+ {/* Providers Overview */}
+
+ }>
+
+
+
+
+
+
+
+
+
>
);
}
+
+const SSRProvidersOverview = async () => {
+ const providersOverview = await getProvidersOverview({});
+
+ if (!providersOverview) {
+ return There is no providers overview info available
;
+ }
+
+ return ;
+};
diff --git a/components/overview/index.ts b/components/overview/index.ts
index 452749b068..eb393767f1 100644
--- a/components/overview/index.ts
+++ b/components/overview/index.ts
@@ -1 +1,3 @@
export * from "./AttackSurface";
+export * from "./provider-overview/provider-overview";
+export * from "./provider-overview/skeleton-provider-overview";
diff --git a/components/overview/provider-overview/provider-overview.tsx b/components/overview/provider-overview/provider-overview.tsx
new file mode 100644
index 0000000000..6c5edfb102
--- /dev/null
+++ b/components/overview/provider-overview/provider-overview.tsx
@@ -0,0 +1,111 @@
+"use client";
+
+import { Card, CardBody, CardHeader } from "@nextui-org/react";
+
+import { AddIcon } from "@/components/icons/Icons";
+import {
+ AWSProviderBadge,
+ AzureProviderBadge,
+ GCPProviderBadge,
+ KS8ProviderBadge,
+} from "@/components/icons/providers-badge";
+import { CustomButton } from "@/components/ui/custom/custom-button";
+import { ProviderOverviewProps } from "@/types";
+
+export const ProvidersOverview = ({
+ providersOverview,
+}: {
+ providersOverview: ProviderOverviewProps;
+}) => {
+ console.log(providersOverview);
+ if (!providersOverview || !Array.isArray(providersOverview.data)) {
+ return No provider data available
;
+ }
+
+ const calculatePassingPercentage = (pass: number, total: number) =>
+ total > 0 ? ((pass / total) * 100).toFixed(2) : "0.00";
+
+ const renderProviderBadge = (providerId: string) => {
+ switch (providerId) {
+ case "aws":
+ return ;
+ case "azure":
+ return ;
+ case "gcp":
+ return ;
+ case "kubernetes":
+ return ;
+ default:
+ return null;
+ }
+ };
+
+ return (
+
+
+ Providers Overview
+
+
+
+
+ Provider
+
+ Percent
+ Passing
+
+
+ Failing
+ Checks
+
+
+ Total
+ Resources
+
+
+
+ {providersOverview.data.length === 0 ? (
+
+ -
+ -
+ -
+ -
+
+ ) : (
+ providersOverview.data.map((provider) => {
+ const { pass, fail, total } = provider.attributes.findings;
+ const resourcesTotal = provider.attributes.resources.total;
+
+ return (
+
+
+ {renderProviderBadge(provider.id)}
+
+
+ {calculatePassingPercentage(pass, total)}%
+
+ {fail}
+ {resourcesTotal}
+
+ );
+ })
+ )}
+
+
+ }
+ >
+ Go to Providers
+
+
+
+
+ );
+};
diff --git a/components/overview/provider-overview/skeleton-provider-overview.tsx b/components/overview/provider-overview/skeleton-provider-overview.tsx
new file mode 100644
index 0000000000..47febd1700
--- /dev/null
+++ b/components/overview/provider-overview/skeleton-provider-overview.tsx
@@ -0,0 +1,64 @@
+import { Card, CardBody, CardHeader, Skeleton } from "@nextui-org/react";
+
+export const SkeletonProvidersOverview = () => {
+ const rows = 4;
+
+ return (
+
+
+
+
+
+
+
+
+ {/* Header Skeleton */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Row Skeletons */}
+ {Array.from({ length: rows }).map((_, index) => (
+
+ {/* Provider Name */}
+
+ {/* Percent Passing */}
+
+
+
+ {/* Failing Checks */}
+
+
+
+ {/* Total Resources */}
+
+
+
+
+ ))}
+
+
+
+ );
+};
diff --git a/types/components.ts b/types/components.ts
index 7ec383d408..bbc7024646 100644
--- a/types/components.ts
+++ b/types/components.ts
@@ -26,6 +26,27 @@ export type NextUIColors =
| "danger"
| "default";
+export interface ProviderOverviewProps {
+ data: {
+ type: "provider-overviews";
+ id: "aws" | "gcp" | "azure" | "kubernetes";
+ attributes: {
+ findings: {
+ pass: number;
+ fail: number;
+ manual: number;
+ total: number;
+ };
+ resources: {
+ total: number;
+ };
+ };
+ }[];
+ meta: {
+ version: string;
+ };
+}
+
export interface TaskDetails {
attributes: {
state: string;