feat: resources in finding tables (#7813)

This commit is contained in:
Alejandro Bailo
2025-05-22 08:58:25 +02:00
committed by GitHub
parent d036e0054b
commit c44ea3943e
11 changed files with 111 additions and 122 deletions
+3 -3
View File
@@ -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)
---
-1
View File
@@ -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(
@@ -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 (
<div className="flex justify-center">
<div className="flex max-w-10 justify-center">
<TriggerSheet
triggerComponent={<InfoIcon className="text-primary" size={16} />}
title="Finding Details"
@@ -105,8 +110,10 @@ export const ColumnFindings: ColumnDef<FindingProps>[] = [
return (
<div className="relative flex max-w-[410px] flex-row items-center gap-2 3xl:max-w-[660px]">
<div className="flex flex-row items-center gap-4">
{(delta === "new" || delta === "changed") && (
{delta === "new" || delta === "changed" ? (
<DeltaIndicator delta={delta} />
) : (
<div className="w-2" />
)}
<p className="mr-7 whitespace-normal break-words text-sm">
{checktitle}
@@ -119,6 +126,21 @@ export const ColumnFindings: ColumnDef<FindingProps>[] = [
);
},
},
{
accessorKey: "resourceName",
header: "Resource name",
cell: ({ row }) => {
const resourceName = getResourceData(row, "name");
return (
<SnippetChip
value={resourceName as string}
formatter={(value: string) => `...${value.slice(-10)}`}
icon={<Database size={16} />}
/>
);
},
},
{
accessorKey: "severity",
header: ({ column }) => (
@@ -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<FindingProps>[] = [
return (
<div className="relative flex max-w-[410px] flex-row items-center gap-2 3xl:max-w-[660px]">
<div className="flex flex-row items-center gap-4">
{(delta === "new" || delta === "changed") && (
{delta === "new" || delta === "changed" ? (
<DeltaIndicator delta={delta} />
) : (
<div className="w-2" />
)}
<p className="mr-7 whitespace-normal break-words text-sm">
{checktitle}
@@ -95,6 +101,21 @@ export const ColumnNewFindingsToDate: ColumnDef<FindingProps>[] = [
);
},
},
{
accessorKey: "resourceName",
header: "Resource name",
cell: ({ row }) => {
const resourceName = getResourceData(row, "name");
return (
<SnippetChip
value={resourceName as string}
formatter={(value) => `...${value.slice(-10)}`}
icon={<Database size={16} />}
/>
);
},
},
{
accessorKey: "severity",
header: "Severity",
@@ -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<ProviderProps>[] = [
const {
attributes: { uid },
} = getProviderData(row);
return <SnippetId className="h-7" entityId={uid} />;
return <SnippetChip value={uid} className="h-7" />;
},
},
{
@@ -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<EntityInfoProps> = ({
{entityAlias && (
<span className="text-xs text-default-500">{entityAlias}</span>
)}
<SnippetId
entityId={entityId ?? ""}
<SnippetChip
value={entityId ?? ""}
hideCopyButton={hideCopyButton}
icon={<IdIcon size={16} />}
/>
</div>
</div>
+1 -2
View File
@@ -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";
@@ -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 (
<Snippet
className={cn("h-6", className)}
color="default"
size="sm"
variant="flat"
radius="lg"
hideSymbol
copyIcon={<CopyIcon size={16} />}
checkIcon={<DoneIcon size={16} />}
hideCopyButton={hideCopyButton}
codeString={value}
{...props}
>
<div className="flex items-center space-x-2" aria-label={ariaLabel}>
{icon}
<Tooltip content={value} placement="top" size="sm">
<span className="no-scrollbar max-w-24 overflow-hidden overflow-x-scroll text-ellipsis whitespace-nowrap text-xs">
{formatter ? formatter(value) : value}
</span>
</Tooltip>
</div>
</Snippet>
);
};
-39
View File
@@ -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<SnippetIdProps> = ({
entityId,
hideCopyButton = false,
...props
}) => {
return (
<Snippet
className="flex h-6 items-center py-0"
color="default"
size="sm"
variant="flat"
radius="lg"
hideSymbol
copyIcon={<CopyIcon size={16} />}
checkIcon={<DoneIcon size={16} />}
hideCopyButton={hideCopyButton}
{...props}
>
<p className="flex items-center space-x-2">
<IdIcon size={18} />
<Tooltip content={entityId} placement="top">
<span className="no-scrollbar w-24 overflow-hidden overflow-x-scroll text-ellipsis whitespace-nowrap text-xs">
{entityId}
</span>
</Tooltip>
</p>
</Snippet>
);
};
@@ -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<SnippetLabelProps> = ({
label,
...props
}) => {
return (
label !== "" && (
<Snippet
className="m-0 flex items-center bg-transparent py-0"
color="default"
size="sm"
radius="lg"
hideSymbol
copyIcon={<CopyIcon size={16} />}
checkIcon={<DoneIcon size={16} />}
{...props}
>
<p className="no-scrollbar text-md mb-1 w-32 overflow-hidden overflow-x-scroll text-ellipsis whitespace-nowrap text-sm font-semibold">
{label}
</p>
</Snippet>
)
);
};
@@ -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 (
<div className="flex items-center justify-between">
<p className="text-sm font-semibold text-default-600">
Active organization ID:
</p>
<div className="flex items-center">
<Tooltip content={copied ? "Copied!" : "Copy ID"}>
<CustomButton
ariaLabel="Copy Tenant ID"
onPress={handleCopyTenantId}
variant="light"
color="primary"
size="sm"
>
<span className="mr-2 max-w-[120px] overflow-hidden overflow-ellipsis whitespace-nowrap">
{id}
</span>
{copied ? (
<DoneIcon size={16} className="text-success" />
) : (
<CopyIcon size={16} />
)}
</CustomButton>
</Tooltip>
</div>
<SnippetChip value={id} />
</div>
);
};