From c44ea3943e7d12b006b72ab52ed9aa1dd7017371 Mon Sep 17 00:00:00 2001 From: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Date: Thu, 22 May 2025 08:58:25 +0200 Subject: [PATCH] feat: resources in finding tables (#7813) --- ui/CHANGELOG.md | 6 +-- ui/app/(prowler)/findings/page.tsx | 1 - .../findings/table/column-findings.tsx | 28 +++++++++-- .../table/column-new-findings-to-date.tsx | 29 ++++++++++-- .../providers/table/column-providers.tsx | 4 +- .../ui/entities/entity-info-short.tsx | 8 ++-- ui/components/ui/entities/index.ts | 3 +- ui/components/ui/entities/snippet-chip.tsx | 47 +++++++++++++++++++ ui/components/ui/entities/snippet-id.tsx | 39 --------------- ui/components/ui/entities/snippet-label.tsx | 32 ------------- .../users/profile/user-basic-info-card.tsx | 36 ++------------ 11 files changed, 111 insertions(+), 122 deletions(-) create mode 100644 ui/components/ui/entities/snippet-chip.tsx delete mode 100644 ui/components/ui/entities/snippet-id.tsx delete mode 100644 ui/components/ui/entities/snippet-label.tsx diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index 595d678296..f56f24cec8 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to the **Prowler UI** are documented in this file. ### 🚀 Added - New profile page with details about the user and their roles. [(#7780)](https://github.com/prowler-cloud/prowler/pull/7780) +- Improved `SnippetChip` component and show resource name in new findings table. [(#7813)](https://github.com/prowler-cloud/prowler/pull/7813) --- @@ -22,14 +23,13 @@ All notable changes to the **Prowler UI** are documented in this file. ## [v1.7.0] (Prowler v5.7.0) - ### 🚀 Added - Add a new chart to show the split between passed and failed findings. [(#7680)](https://github.com/prowler-cloud/prowler/pull/7680) - Added `Accordion` component. [(#7700)](https://github.com/prowler-cloud/prowler/pull/7700) - Improve `Provider UID` filter by adding more context and enhancing the UI/UX. [(#7741)](https://github.com/prowler-cloud/prowler/pull/7741) - Added an AWS CloudFormation Quick Link to the IAM Role credentials step [(#7735)](https://github.com/prowler-cloud/prowler/pull/7735) -– Use `getLatestFindings` on findings page when no scan or date filters are applied. [(#7756)](https://github.com/prowler-cloud/prowler/pull/7756) + – Use `getLatestFindings` on findings page when no scan or date filters are applied. [(#7756)](https://github.com/prowler-cloud/prowler/pull/7756) ### 🐞 Fixes @@ -102,7 +102,7 @@ All notable changes to the **Prowler UI** are documented in this file. - Revalidate the page when a role is deleted. [(#6976)](https://github.com/prowler-cloud/prowler/pull/6976) - Allows removing group visibility when creating a role. [(#7088)](https://github.com/prowler-cloud/prowler/pull/7088) - Displays correct error messages when deleting a user. [(#7089)](https://github.com/prowler-cloud/prowler/pull/7089) -- Updated label: *"Select a scan job"* → *"Select a cloud provider"*. [(#7107)](https://github.com/prowler-cloud/prowler/pull/7107) +- Updated label: _"Select a scan job"_ → _"Select a cloud provider"_. [(#7107)](https://github.com/prowler-cloud/prowler/pull/7107) - Display uid if alias is missing when creating a group. [(#7137)](https://github.com/prowler-cloud/prowler/pull/7137) --- diff --git a/ui/app/(prowler)/findings/page.tsx b/ui/app/(prowler)/findings/page.tsx index f137b451cc..f1c5fac71f 100644 --- a/ui/app/(prowler)/findings/page.tsx +++ b/ui/app/(prowler)/findings/page.tsx @@ -52,7 +52,6 @@ export default async function Findings({ const uniqueServices = metadataInfoData?.data?.attributes?.services || []; const uniqueResourceTypes = metadataInfoData?.data?.attributes?.resource_types || []; - // Get findings data // Extract provider UIDs const providerUIDs: string[] = Array.from( diff --git a/ui/components/findings/table/column-findings.tsx b/ui/components/findings/table/column-findings.tsx index 8bfeac0a7f..a8636ab750 100644 --- a/ui/components/findings/table/column-findings.tsx +++ b/ui/components/findings/table/column-findings.tsx @@ -1,11 +1,16 @@ "use client"; import { ColumnDef } from "@tanstack/react-table"; +import { Database } from "lucide-react"; import { useSearchParams } from "next/navigation"; import { DataTableRowDetails } from "@/components/findings/table"; import { InfoIcon } from "@/components/icons"; -import { DateWithTime, EntityInfoShort } from "@/components/ui/entities"; +import { + DateWithTime, + EntityInfoShort, + SnippetChip, +} from "@/components/ui/entities"; import { TriggerSheet } from "@/components/ui/sheet"; import { DataTableColumnHeader, @@ -63,7 +68,7 @@ const FindingDetailsCell = ({ row }: { row: any }) => { }; return ( -
+
} title="Finding Details" @@ -105,8 +110,10 @@ export const ColumnFindings: ColumnDef[] = [ return (
- {(delta === "new" || delta === "changed") && ( + {delta === "new" || delta === "changed" ? ( + ) : ( +
)}

{checktitle} @@ -119,6 +126,21 @@ export const ColumnFindings: ColumnDef[] = [ ); }, }, + { + accessorKey: "resourceName", + header: "Resource name", + cell: ({ row }) => { + const resourceName = getResourceData(row, "name"); + + return ( + `...${value.slice(-10)}`} + icon={} + /> + ); + }, + }, { accessorKey: "severity", header: ({ column }) => ( diff --git a/ui/components/overview/new-findings-table/table/column-new-findings-to-date.tsx b/ui/components/overview/new-findings-table/table/column-new-findings-to-date.tsx index 7d04fa9722..488f8b190b 100644 --- a/ui/components/overview/new-findings-table/table/column-new-findings-to-date.tsx +++ b/ui/components/overview/new-findings-table/table/column-new-findings-to-date.tsx @@ -1,18 +1,22 @@ "use client"; import { ColumnDef } from "@tanstack/react-table"; +import { Database } from "lucide-react"; import { useSearchParams } from "next/navigation"; +import { Muted } from "@/components/findings/muted"; import { DataTableRowDetails } from "@/components/findings/table"; import { DeltaIndicator } from "@/components/findings/table/delta-indicator"; import { InfoIcon } from "@/components/icons"; -import { DateWithTime, EntityInfoShort } from "@/components/ui/entities"; +import { + DateWithTime, + EntityInfoShort, + SnippetChip, +} from "@/components/ui/entities"; import { TriggerSheet } from "@/components/ui/sheet"; import { SeverityBadge, StatusFindingBadge } from "@/components/ui/table"; import { FindingProps, ProviderType } from "@/types"; -import { Muted } from "../../../findings/muted"; - const getFindingsData = (row: { original: FindingProps }) => { return row.original; }; @@ -81,8 +85,10 @@ export const ColumnNewFindingsToDate: ColumnDef[] = [ return (

- {(delta === "new" || delta === "changed") && ( + {delta === "new" || delta === "changed" ? ( + ) : ( +
)}

{checktitle} @@ -95,6 +101,21 @@ export const ColumnNewFindingsToDate: ColumnDef[] = [ ); }, }, + { + accessorKey: "resourceName", + header: "Resource name", + cell: ({ row }) => { + const resourceName = getResourceData(row, "name"); + + return ( + `...${value.slice(-10)}`} + icon={} + /> + ); + }, + }, { accessorKey: "severity", header: "Severity", diff --git a/ui/components/providers/table/column-providers.tsx b/ui/components/providers/table/column-providers.tsx index f16585e34b..8527bfd754 100644 --- a/ui/components/providers/table/column-providers.tsx +++ b/ui/components/providers/table/column-providers.tsx @@ -3,7 +3,7 @@ import { Chip } from "@nextui-org/react"; import { ColumnDef } from "@tanstack/react-table"; -import { DateWithTime, SnippetId } from "@/components/ui/entities"; +import { DateWithTime, SnippetChip } from "@/components/ui/entities"; import { DataTableColumnHeader } from "@/components/ui/table"; import { ProviderProps } from "@/types"; @@ -74,7 +74,7 @@ export const ColumnProviders: ColumnDef[] = [ const { attributes: { uid }, } = getProviderData(row); - return ; + return ; }, }, { diff --git a/ui/components/ui/entities/entity-info-short.tsx b/ui/components/ui/entities/entity-info-short.tsx index c1c957642b..ecf7687e2f 100644 --- a/ui/components/ui/entities/entity-info-short.tsx +++ b/ui/components/ui/entities/entity-info-short.tsx @@ -1,9 +1,10 @@ import React from "react"; +import { IdIcon } from "@/components/icons"; import { ProviderType } from "@/types"; import { getProviderLogo } from "./get-provider-logo"; -import { SnippetId } from "./snippet-id"; +import { SnippetChip } from "./snippet-chip"; interface EntityInfoProps { cloudProvider: ProviderType; @@ -26,9 +27,10 @@ export const EntityInfoShort: React.FC = ({ {entityAlias && ( {entityAlias} )} - } />

diff --git a/ui/components/ui/entities/index.ts b/ui/components/ui/entities/index.ts index cb329a03e3..fb13c99282 100644 --- a/ui/components/ui/entities/index.ts +++ b/ui/components/ui/entities/index.ts @@ -3,5 +3,4 @@ export * from "./entity-info-short"; export * from "./get-provider-logo"; export * from "./info-field"; export * from "./scan-status"; -export * from "./snippet-id"; -export * from "./snippet-label"; +export * from "./snippet-chip"; diff --git a/ui/components/ui/entities/snippet-chip.tsx b/ui/components/ui/entities/snippet-chip.tsx new file mode 100644 index 0000000000..2652c2d76a --- /dev/null +++ b/ui/components/ui/entities/snippet-chip.tsx @@ -0,0 +1,47 @@ +import { cn, Snippet, Tooltip } from "@nextui-org/react"; +import React from "react"; + +import { CopyIcon, DoneIcon } from "@/components/icons"; + +interface SnippetChipProps { + value: string; + ariaLabel?: string; + icon?: React.ReactNode; + hideCopyButton?: boolean; + formatter?: (value: string) => string; + className?: string; +} +export const SnippetChip = ({ + value, + hideCopyButton = false, + ariaLabel = `Copy ${value} to clipboard`, + icon, + formatter, + className, + ...props +}: SnippetChipProps) => { + return ( + } + checkIcon={} + hideCopyButton={hideCopyButton} + codeString={value} + {...props} + > +
+ {icon} + + + {formatter ? formatter(value) : value} + + +
+
+ ); +}; diff --git a/ui/components/ui/entities/snippet-id.tsx b/ui/components/ui/entities/snippet-id.tsx deleted file mode 100644 index cb4b1a7e87..0000000000 --- a/ui/components/ui/entities/snippet-id.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Snippet, Tooltip } from "@nextui-org/react"; -import React from "react"; - -import { CopyIcon, DoneIcon, IdIcon } from "@/components/icons"; - -interface SnippetIdProps { - entityId: string; - hideCopyButton?: boolean; - [key: string]: any; -} -export const SnippetId: React.FC = ({ - entityId, - hideCopyButton = false, - ...props -}) => { - return ( - } - checkIcon={} - hideCopyButton={hideCopyButton} - {...props} - > -

- - - - {entityId} - - -

-
- ); -}; diff --git a/ui/components/ui/entities/snippet-label.tsx b/ui/components/ui/entities/snippet-label.tsx deleted file mode 100644 index 18525d6fe5..0000000000 --- a/ui/components/ui/entities/snippet-label.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Snippet } from "@nextui-org/react"; -import React from "react"; - -import { CopyIcon, DoneIcon } from "@/components/icons"; - -interface SnippetLabelProps { - label: string; - [key: string]: any; -} -export const SnippetLabel: React.FC = ({ - label, - ...props -}) => { - return ( - label !== "" && ( - } - checkIcon={} - {...props} - > -

- {label} -

-
- ) - ); -}; diff --git a/ui/components/users/profile/user-basic-info-card.tsx b/ui/components/users/profile/user-basic-info-card.tsx index 5c4ea0bbbd..456f9b777e 100644 --- a/ui/components/users/profile/user-basic-info-card.tsx +++ b/ui/components/users/profile/user-basic-info-card.tsx @@ -1,48 +1,18 @@ "use client"; -import { Card, CardBody, Divider, Tooltip } from "@nextui-org/react"; +import { Card, CardBody, Divider } from "@nextui-org/react"; import { CircleUserRound } from "lucide-react"; -import { useState } from "react"; -import { CopyIcon, DoneIcon } from "@/components/icons"; -import { CustomButton } from "@/components/ui/custom/custom-button"; -import { DateWithTime } from "@/components/ui/entities"; +import { DateWithTime, SnippetChip } from "@/components/ui/entities"; import { UserDataWithRoles } from "@/types/users/users"; const TenantIdCopy = ({ id }: { id: string }) => { - const [copied, setCopied] = useState(false); - - const handleCopyTenantId = () => { - navigator.clipboard.writeText(id); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - return (

Active organization ID:

-
- - - - {id} - - {copied ? ( - - ) : ( - - )} - - -
+
); };