feat(v5): tweaks UI for v5 release - 2 (#5979)

This commit is contained in:
Pablo Lara
2024-12-01 15:28:11 +01:00
committed by GitHub
parent 2a13301d35
commit 3c2b0a58a1
25 changed files with 354 additions and 220 deletions
+1
View File
@@ -45,6 +45,7 @@ export const getFindings = async ({
revalidatePath("/findings");
return parsedData;
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error fetching findings:", error);
return undefined;
}
+19 -10
View File
@@ -13,7 +13,12 @@ import {
import { Header } from "@/components/ui";
import { DataTable, DataTableFilterCustom } from "@/components/ui/table";
import { createDict } from "@/lib";
import { FindingProps, SearchParamsProps } from "@/types/components";
import {
FindingProps,
ProviderProps,
ScanProps,
SearchParamsProps,
} from "@/types/components";
export default async function Findings({
searchParams,
@@ -56,24 +61,28 @@ export default async function Findings({
const scansData = await getScans({});
// Extract provider UIDs
const providerUIDs = providersData?.data
?.map((provider: any) => provider.attributes.uid)
.filter(Boolean);
const providerUIDs = Array.from(
new Set(
providersData?.data
?.map((provider: ProviderProps) => provider.attributes.uid)
.filter(Boolean),
),
);
// Extract scan UUIDs with "completed" state and more than one resource
const completedScans = scansData?.data
?.filter(
(scan: any) =>
scan.attributes.state === "completed" &&
scan.attributes.unique_resource_count > 1 &&
scan.attributes.name, // Ensure it has a name
scan.attributes.unique_resource_count > 1,
)
.map((scan: any) => ({
.map((scan: ScanProps) => ({
id: scan.id,
name: scan.attributes.name,
}));
const completedScanIds = completedScans?.map((scan: any) => scan.id) || [];
const completedScanIds =
completedScans?.map((scan: ScanProps) => scan.id) || [];
return (
<>
@@ -97,12 +106,12 @@ export default async function Findings({
},
{
key: "provider_uid__in",
labelCheckboxGroup: "Account",
labelCheckboxGroup: "Provider UID",
values: providerUIDs,
},
{
key: "scan__in",
labelCheckboxGroup: "Scans",
labelCheckboxGroup: "Scan ID",
values: completedScanIds,
},
]}
+1
View File
@@ -56,6 +56,7 @@ export default function Home({
</div>
<div className="col-span-12">
<Spacer y={16} />
<Suspense
key={searchParamsKey}
fallback={<SkeletonTableNewFindings />}
+93 -47
View File
@@ -1,10 +1,14 @@
import { Spacer } from "@nextui-org/react";
import { Suspense } from "react";
import { getProviders } from "@/actions/providers";
import { getProvider, getProviders } from "@/actions/providers";
import { getScans } from "@/actions/scans";
import { FilterControls, filterScans } from "@/components/filters";
import { ButtonRefreshData } from "@/components/scans";
import { filterScans } from "@/components/filters";
import {
ButtonRefreshData,
NoProvidersAdded,
NoProvidersConnected,
} from "@/components/scans";
import { LaunchScanWorkflow } from "@/components/scans/launch-workflow";
import { SkeletonTableScans } from "@/components/scans/table";
import { ColumnGetScans } from "@/components/scans/table/scans";
@@ -21,48 +25,72 @@ export default async function Scans({
delete filteredParams.scanId;
const searchParamsKey = JSON.stringify(filteredParams);
const providersData = await getProviders({});
const providersData = await getProviders({
filters: {
"filter[connected]": true,
},
});
const providerInfo = providersData?.data?.length
? providersData.data.map((provider: ProviderProps) => ({
providerId: provider.id,
alias: provider.attributes.alias,
providerType: provider.attributes.provider,
uid: provider.attributes.uid,
connected: provider.attributes.connection.connected,
}))
: [];
const providerInfo = providersData.data.map((provider: ProviderProps) => ({
providerId: provider.id,
alias: provider.attributes.alias,
providerType: provider.attributes.provider,
uid: provider.attributes.uid,
connected: provider.attributes.connection.connected,
}));
// const executingScans = await getExecutingScans();
const providersCountConnected = await getProviders({});
const thereIsNoProviders =
!providersCountConnected?.data || providersCountConnected.data.length === 0;
const thereIsNoProvidersConnected = providersCountConnected?.data?.every(
(provider: ProviderProps) => !provider.attributes.connection.connected,
);
return (
<>
<Header title="Scans" icon="lucide:scan-search" />
<Spacer y={4} />
<FilterControls search providers />
<Spacer y={8} />
<LaunchScanWorkflow providers={providerInfo} />
<Spacer y={8} />
<div className="flex flex-row justify-between">
<DataTableFilterCustom filters={filterScans || []} />
<ButtonRefreshData
onPress={async () => {
"use server";
await getScans({});
}}
/>
</div>
{thereIsNoProviders && (
<>
<Spacer y={4} />
<NoProvidersAdded />
</>
)}
<Spacer y={8} />
<div className="grid grid-cols-12 items-start gap-4">
<div className="col-span-12">
<Suspense key={searchParamsKey} fallback={<SkeletonTableScans />}>
<SSRDataTableScans searchParams={searchParams} />
</Suspense>
</div>
</div>
{!thereIsNoProviders && (
<>
{thereIsNoProvidersConnected ? (
<>
<Spacer y={8} />
<NoProvidersConnected />
</>
) : (
<>
<LaunchScanWorkflow providers={providerInfo} />
<Spacer y={8} />
</>
)}
<Spacer y={8} />
<div className="grid grid-cols-12 items-start gap-4">
<div className="col-span-12">
<div className="flex flex-row items-center justify-between">
<DataTableFilterCustom filters={filterScans || []} />
<ButtonRefreshData
onPress={async () => {
"use server";
await getScans({});
}}
/>
</div>
<Spacer y={8} />
<Suspense key={searchParamsKey} fallback={<SkeletonTableScans />}>
<SSRDataTableScans searchParams={searchParams} />
</Suspense>
</div>
</div>
</>
)}
</>
);
}
@@ -85,22 +113,40 @@ const SSRDataTableScans = async ({
// Extract query from filters
const query = (filters["filter[search]"] as string) || "";
// Fetch scans data
const scansData = await getScans({ query, page, sort, filters });
// Handle expanded scans data
const expandedScansData = await Promise.all(
scansData?.data?.map(async (scan: any) => {
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);
if (providerData?.data) {
const { provider, uid, alias } = providerData.data.attributes;
return {
...scan,
providerInfo: { provider, uid, alias },
};
}
return { ...scan, providerInfo: null };
}) || [],
);
return (
<DataTable
columns={ColumnGetScans}
data={scansData?.data || []}
data={expandedScansData || []}
metadata={scansData?.meta}
/>
);
};
// const getExecutingScans = async () => {
// const scansData = await getScans({});
// return scansData?.data?.some(
// (scan: ScanProps) =>
// scan.attributes.state === "executing" && scan.attributes.progress < 100,
// );
// };
+6 -6
View File
@@ -8,11 +8,11 @@ export const filterProviders = [
];
export const filterScans = [
// {
// key: "provider_type__in",
// labelCheckboxGroup: "Provider",
// values: ["aws", "azure", "gcp", "kubernetes"],
// },
{
key: "provider_type__in",
labelCheckboxGroup: "Cloud Provider",
values: ["aws", "azure", "gcp", "kubernetes"],
},
{
key: "state",
labelCheckboxGroup: "State",
@@ -51,7 +51,7 @@ export const filterFindings = [
},
{
key: "provider_type__in",
labelCheckboxGroup: "Provider",
labelCheckboxGroup: "Cloud Provider",
values: ["aws", "azure", "gcp", "kubernetes"],
},
// Add more filter categories as needed
@@ -5,7 +5,7 @@ import { useSearchParams } from "next/navigation";
import { DataTableRowDetails } from "@/components/findings/table";
import { InfoIcon } from "@/components/icons";
import { DateWithTime } from "@/components/ui/entities";
import { DateWithTime, EntityInfoShort } from "@/components/ui/entities";
import { TriggerSheet } from "@/components/ui/sheet";
import {
DataTableColumnHeader,
@@ -181,16 +181,20 @@ export const ColumnFindings: ColumnDef<FindingProps>[] = [
},
},
{
accessorKey: "account",
header: "Account",
accessorKey: "cloudProvider",
header: "Cloud provider",
cell: ({ row }) => {
const account = getProviderData(row, "uid");
const provider = getProviderData(row, "provider");
const alias = getProviderData(row, "alias");
const uid = getProviderData(row, "uid");
return (
<>
<p className="max-w-96 truncate text-small">
{typeof account === "string" ? account : "Invalid account"}
</p>
<EntityInfoShort
cloudProvider={provider as "aws" | "azure" | "gcp" | "kubernetes"}
entityAlias={alias as string}
entityId={uid as string}
/>
</>
);
},
@@ -56,7 +56,7 @@ export const FindingsBySeverityChart = ({
}));
return (
<Card className="dark:bg-prowler-blue-400">
<Card className="h-full dark:bg-prowler-blue-400">
<CardBody>
<div className="my-auto">
<ChartContainer config={chartConfig}>
@@ -91,7 +91,7 @@ export const FindingsByStatusChart: React.FC<FindingsByStatusChartProps> = ({
];
return (
<Card className="dark:bg-prowler-blue-400">
<Card className="h-full dark:bg-prowler-blue-400">
<CardBody>
<div className="flex flex-col items-center gap-6">
<ChartContainer
@@ -5,7 +5,7 @@ import { useSearchParams } from "next/navigation";
import { DataTableRowDetails } from "@/components/findings/table";
import { InfoIcon } from "@/components/icons";
import { DateWithTime } from "@/components/ui/entities";
import { DateWithTime, EntityInfoShort } from "@/components/ui/entities";
import { TriggerSheet } from "@/components/ui/sheet";
import { SeverityBadge, StatusFindingBadge } from "@/components/ui/table";
import { FindingProps } from "@/types";
@@ -132,16 +132,20 @@ export const ColumnNewFindingsToDate: ColumnDef<FindingProps>[] = [
},
},
{
accessorKey: "account",
header: "Account",
accessorKey: "cloudProvider",
header: "Cloud provider",
cell: ({ row }) => {
const account = getProviderData(row, "uid");
const provider = getProviderData(row, "provider");
const alias = getProviderData(row, "alias");
const uid = getProviderData(row, "uid");
return (
<>
<p className="max-w-96 truncate text-small">
{typeof account === "string" ? account : "Invalid account"}
</p>
<EntityInfoShort
cloudProvider={provider as "aws" | "azure" | "gcp" | "kubernetes"}
entityAlias={alias as string}
entityId={uid as string}
/>
</>
);
},
@@ -44,9 +44,9 @@ export const ProvidersOverview = ({
if (!providersOverview || !Array.isArray(providersOverview.data)) {
return (
<Card className="dark:bg-prowler-blue-400">
<Card className="h-full dark:bg-prowler-blue-400">
<CardBody>
<div className="grid grid-cols-1 gap-3">
<div className="my-auto grid grid-cols-1 gap-3">
<div className="grid grid-cols-4 border-b pb-2 text-xs font-semibold">
<span className="text-center">Provider</span>
<span className="flex flex-col items-center text-center">
@@ -92,9 +92,9 @@ export const ProvidersOverview = ({
}
return (
<Card className="dark:bg-prowler-blue-400">
<Card className="h-full dark:bg-prowler-blue-400">
<CardBody>
<div className="grid grid-cols-1 gap-3">
<div className="my-auto grid grid-cols-1 gap-3">
<div className="grid grid-cols-4 border-b pb-2 text-xs font-semibold">
<span className="text-center">Provider</span>
<span className="flex flex-col items-center text-center">
@@ -175,7 +175,7 @@ export const ProvidersOverview = ({
</div>
<div className="mt-4 flex w-full items-center justify-end">
<CustomButton
asLink="/providers/connect-account"
asLink="/providers"
ariaLabel="Go to Providers page"
variant="solid"
color="action"
@@ -42,8 +42,7 @@ export function DataTableRowActions<ProviderProps>({
<CustomAlertModal
isOpen={isEditOpen}
onOpenChange={setIsEditOpen}
title="Edit Provider"
description={"Edit the provider details"}
title="Edit Provider Alias"
>
<EditForm
providerId={providerId}
@@ -93,7 +92,7 @@ export function DataTableRowActions<ProviderProps>({
startContent={<EditDocumentBulkIcon className={iconClasses} />}
onClick={() => setIsEditOpen(true)}
>
Edit Provider
Edit Provider Alias
</DropdownItem>
</DropdownSection>
<DropdownSection title="Danger zone">
+2
View File
@@ -1,2 +1,4 @@
export * from "./button-refresh-data";
export * from "./link-to-findings-from-scan";
export * from "./no-providers-added";
export * from "./no-providers-connected";
@@ -5,7 +5,7 @@ import { useForm } from "react-hook-form";
import * as z from "zod";
import { scanOnDemand } from "@/actions/scans";
import { RocketIcon, ScheduleIcon } from "@/components/icons";
import { RocketIcon } from "@/components/icons";
import { CustomButton, CustomInput } from "@/components/ui/custom";
import { Form } from "@/components/ui/form";
import { toast } from "@/components/ui/toast";
@@ -79,26 +79,26 @@ export const LaunchScanWorkflow = ({
onSubmit={form.handleSubmit(onSubmitClient)}
className="flex flex-col space-y-4"
>
<div className="grid grid-cols-2 gap-6">
<div className="flex flex-col gap-6">
<div className="w-full">
<p className="pb-1 text-sm font-medium text-default-700">
Launch Scan
</p>
<SelectScanProvider
providers={providers}
control={form.control}
name="providerId"
/>
</div>
<AnimatePresence>
{form.watch("providerId") && (
<div className="grid grid-cols-12 gap-6">
<div className="col-span-4">
<p className="pb-1 text-sm font-medium text-default-700">
Launch Scan
</p>
<SelectScanProvider
providers={providers}
control={form.control}
name="providerId"
/>
</div>
<AnimatePresence>
{form.watch("providerId") && (
<>
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.2 }}
initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -50 }}
transition={{ duration: 0.3 }}
className="col-span-2"
>
<CustomInput
control={form.control}
@@ -112,10 +112,41 @@ export const LaunchScanWorkflow = ({
isInvalid={!!form.formState.errors.scanName}
/>
</motion.div>
)}
</AnimatePresence>
</div>
<motion.div
initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -50 }}
transition={{ duration: 0.3 }}
className="col-span-4 flex items-end gap-4"
>
<div className="flex flex-row items-center gap-4">
<CustomButton
type="submit"
ariaLabel="Start scan now"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <RocketIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Start now</span>}
</CustomButton>
<CustomButton
onPress={() => form.reset()}
className="w-fit border-gray-200 bg-transparent"
ariaLabel="Clear form"
variant="bordered"
size="md"
radius="lg"
>
Cancel
</CustomButton>
</div>
</motion.div>
</>
)}
</AnimatePresence>
{/*
<div className="flex flex-col justify-start">
<AnimatePresence>
{form.watch("providerId") && (
@@ -139,54 +170,7 @@ export const LaunchScanWorkflow = ({
</motion.div>
)}
</AnimatePresence>
</div>
<AnimatePresence>
{form.watch("providerId") && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.2 }}
className="col-span-2 flex justify-start gap-4"
>
<CustomButton
onPress={() => form.reset()}
className="w-fit border-gray-200 bg-transparent"
ariaLabel="Clear form"
variant="bordered"
size="lg"
radius="lg"
>
Cancel
</CustomButton>
<CustomButton
type="submit"
ariaLabel="Schedule scan"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <ScheduleIcon size={24} />}
isDisabled={true}
>
{isLoading ? <>Loading</> : <span>Schedule</span>}
</CustomButton>
<CustomButton
type="submit"
ariaLabel="Start scan now"
variant="solid"
color="action"
size="lg"
isLoading={isLoading}
startContent={!isLoading && <RocketIcon size={24} />}
>
{isLoading ? <>Loading</> : <span>Start now</span>}
</CustomButton>
</motion.div>
)}
</AnimatePresence>
</div> */}
</div>
</form>
</Form>
@@ -55,8 +55,8 @@ export const SelectScanProvider = <
<>
<FormControl>
<Select
aria-label="Select a Provider"
placeholder="Choose a provider"
aria-label="Select a scan job"
placeholder="Choose a scan job"
labelPlacement="outside"
size="md"
selectedKeys={field.value ? new Set([field.value]) : new Set()}
@@ -74,7 +74,7 @@ export const SelectScanProvider = <
{selectedItem.alias}
</div>
) : (
"Choose a provider"
"Choose a scan job"
);
}}
>
@@ -0,0 +1,30 @@
"use client";
import { Card, CardBody } from "@nextui-org/react";
import React from "react";
import { CustomButton } from "../ui/custom";
export const NoProvidersAdded = () => {
return (
<div className="flex h-screen items-center justify-center">
<Card shadow="sm" className="w-full max-w-md">
<CardBody className="space-y-6 p-6 text-center">
<h2 className="text-xl font-bold">No Cloud Accounts Configured</h2>
<p className="text-md text-gray-600">
You don't have any cloud accounts configured yet. This is the first
step to get started.
</p>
<CustomButton
asLink="/providers/connect-account"
ariaLabel="Go to Add Cloud Account page"
variant="solid"
color="action"
size="lg"
>
Add Cloud Account
</CustomButton>
</CardBody>
</Card>
</div>
);
};
@@ -0,0 +1,31 @@
"use client";
import { Card, CardBody } from "@nextui-org/react";
import React from "react";
import { CustomButton } from "../ui/custom";
export const NoProvidersConnected = () => {
return (
<div className="flex items-center justify-center">
<Card shadow="sm" className="w-full max-w-md">
<CardBody className="space-y-6 p-6 text-center">
<h2 className="text-xl font-bold">No Cloud Accounts Connected</h2>
<p className="text-md text-gray-600">
All your cloud accounts are currently disconnected. Please review
their configuration to proceed.
</p>
<CustomButton
asLink="/providers"
ariaLabel="Go to Cloud accounts page"
variant="solid"
color="action"
size="lg"
>
Review Cloud Accounts
</CustomButton>
</CardBody>
</Card>
</div>
);
};
@@ -4,7 +4,7 @@ import { ColumnDef } from "@tanstack/react-table";
import { useSearchParams } from "next/navigation";
import { InfoIcon } from "@/components/icons";
import { DateWithTime } from "@/components/ui/entities";
import { DateWithTime, EntityInfoShort } from "@/components/ui/entities";
import { TriggerSheet } from "@/components/ui/sheet";
import { DataTableColumnHeader, StatusBadge } from "@/components/ui/table";
import { ScanProps } from "@/types";
@@ -19,14 +19,49 @@ const getScanData = (row: { original: ScanProps }) => {
export const ColumnGetScans: ColumnDef<ScanProps>[] = [
{
accessorKey: "accountName",
header: () => <p className="pr-8">Account name</p>,
id: "moreInfo",
header: "Details",
cell: ({ row }) => {
console.log(row.original);
const searchParams = useSearchParams();
const scanId = searchParams.get("scanId");
const isOpen = scanId === row.original.id;
return <span className="font-medium">providerinfo</span>;
return (
<div className="flex w-9 items-center justify-center">
<TriggerSheet
triggerComponent={<InfoIcon className="text-primary" size={16} />}
title="Scan Details"
description="View the scan details"
defaultOpen={isOpen}
>
<DataTableRowDetails entityId={row.original.id} />
</TriggerSheet>
</div>
);
},
},
{
accessorKey: "cloudProvider",
header: () => <p className="pr-8">Cloud provider</p>,
cell: ({ row }) => {
const providerInfo = row.original.providerInfo;
if (!providerInfo) {
return <span className="font-medium">No provider info</span>;
}
const { provider, uid, alias } = providerInfo;
return (
<EntityInfoShort
cloudProvider={provider as "aws" | "azure" | "gcp" | "kubernetes"}
entityAlias={alias}
entityId={uid}
/>
);
},
},
{
accessorKey: "started_at",
header: () => <p className="pr-8">Started at</p>,
@@ -88,22 +123,21 @@ export const ColumnGetScans: ColumnDef<ScanProps>[] = [
},
},
{
accessorKey: "scheduled_at",
accessorKey: "next_scan_at",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title={"Scheduled at"}
param="scheduled_at"
title={"Next execution"}
param="next_scan_at"
/>
),
cell: ({ row }) => {
const {
attributes: { scheduled_at },
attributes: { next_scan_at },
} = getScanData(row);
return <DateWithTime dateTime={scheduled_at} />;
return <DateWithTime dateTime={next_scan_at} />;
},
},
{
accessorKey: "completed_at",
header: ({ column }) => (
@@ -129,16 +163,9 @@ export const ColumnGetScans: ColumnDef<ScanProps>[] = [
const {
attributes: { trigger },
} = getScanData(row);
return <p>{trigger}</p>;
return <p className="text-tiny font-medium uppercase">{trigger}</p>;
},
},
// {
// accessorKey: "id",
// header: () => <span>ID</span>,
// cell: ({ row }) => {
// return <SnippetId entityId={row.original.id} />;
// },
// },
{
accessorKey: "scanName",
header: ({ column }) => (
@@ -153,30 +180,9 @@ export const ColumnGetScans: ColumnDef<ScanProps>[] = [
return <span className="font-medium">-</span>;
}
return <span className="font-medium">{name}</span>;
return <span className="text-xs font-medium">{name}</span>;
},
},
{
id: "moreInfo",
header: "Details",
cell: ({ row }) => {
const searchParams = useSearchParams();
const scanId = searchParams.get("scanId");
const isOpen = scanId === row.original.id;
return (
<TriggerSheet
triggerComponent={<InfoIcon className="text-primary" size={16} />}
title="Scan Details"
description="View the scan details"
defaultOpen={isOpen}
>
<DataTableRowDetails entityId={row.original.id} />
</TriggerSheet>
);
},
},
{
id: "actions",
cell: ({ row }) => {
+3 -3
View File
@@ -18,13 +18,13 @@ export const DateWithTime: React.FC<DateWithTimeProps> = ({
const formattedTime = format(date, "p 'UTC'");
return (
<div className="max-w-fit">
<div className="mw-fit">
<div
className={`flex ${inline ? "flex-row items-center gap-2" : "flex-col"}`}
>
<span className="text-sm font-semibold">{formattedDate}</span>
<span className="text-xs font-semibold">{formattedDate}</span>
{showTime && (
<span className="text-xs text-gray-500">{formattedTime}</span>
<span className="text-tiny text-gray-500">{formattedTime}</span>
)}
</div>
</div>
@@ -2,7 +2,6 @@ import React from "react";
import { getProviderLogo } from "./get-provider-logo";
import { SnippetId } from "./snippet-id";
import { SnippetLabel } from "./snippet-label";
interface EntityInfoProps {
cloudProvider: "aws" | "azure" | "gcp" | "kubernetes";
@@ -16,11 +15,13 @@ export const EntityInfoShort: React.FC<EntityInfoProps> = ({
entityId,
}) => {
return (
<div className="flex w-full items-center justify-between space-x-4">
<div className="flex items-center gap-x-4">
<div className="flex w-full items-center justify-between space-x-2">
<div className="flex items-center gap-x-2">
<div className="flex-shrink-0">{getProviderLogo(cloudProvider)}</div>
<div className="flex flex-col">
<SnippetLabel label={entityAlias ?? ""} />
<div className="flex flex-col space-y-1">
{entityAlias && (
<span className="text-tiny text-default-500">{entityAlias}</span>
)}
<SnippetId entityId={entityId ?? ""} />
</div>
</div>
+2 -2
View File
@@ -10,7 +10,7 @@ interface SnippetIdProps {
export const SnippetId: React.FC<SnippetIdProps> = ({ entityId, ...props }) => {
return (
<Snippet
className="flex h-6 items-center py-0"
className="flex h-4 items-center py-0"
color="default"
size="sm"
variant="flat"
@@ -22,7 +22,7 @@ export const SnippetId: React.FC<SnippetIdProps> = ({ entityId, ...props }) => {
>
<p className="flex items-center space-x-2">
<IdIcon size={18} />
<span className="no-scrollbar text-md w-28 overflow-hidden overflow-x-scroll text-ellipsis whitespace-nowrap text-sm">
<span className="no-scrollbar w-14 overflow-hidden overflow-x-scroll text-ellipsis whitespace-nowrap text-xs">
{entityId}
</span>
</p>
+2 -2
View File
@@ -131,7 +131,7 @@ export const SidebarWrap = () => {
ariaLabel="Documentation"
variant="flat"
className={clsx(
"justify-start truncate bg-transparent text-default-500 data-[hover=true]:text-foreground",
"justify-start truncate bg-transparent text-default-500 data-[hover=true]:text-foreground dark:bg-transparent",
{
"justify-center": isCompact,
},
@@ -168,7 +168,7 @@ export const SidebarWrap = () => {
ariaLabel="Support"
variant="flat"
className={clsx(
"justify-start truncate bg-transparent text-default-500 data-[hover=true]:text-foreground",
"justify-start truncate bg-transparent text-default-500 data-[hover=true]:text-foreground dark:bg-transparent",
{
"justify-center": isCompact,
},
@@ -80,10 +80,20 @@ export const DataTableColumnHeader = <TData, TValue>({
return (
<Button
className="h-10 w-full justify-between whitespace-nowrap bg-transparent px-0 text-left align-middle text-tiny font-semibold text-foreground-500 outline-none dark:text-slate-400"
className="h-10 w-fit max-w-[110px] whitespace-nowrap bg-transparent px-0 text-left align-middle text-tiny font-semibold text-foreground-500 outline-none dark:text-slate-400"
onClick={getToggleSortingHandler}
>
<span>{title}</span>
<span
className="block whitespace-normal break-normal"
style={{
display: "-webkit-box",
WebkitBoxOrient: "vertical",
WebkitLineClamp: 2,
width: "90px",
}}
>
{title}
</span>
{renderSortIcon()}
</Button>
);
+1 -1
View File
@@ -35,7 +35,7 @@ export const StatusBadge = ({
return (
<Chip
className="gap-1 border-none px-2 py-4 capitalize text-default-600"
className="gap-1 border-none px-2 py-2 capitalize text-default-600"
size={size}
variant="flat"
color={color}
+2 -2
View File
@@ -75,7 +75,7 @@ const TableHead = React.forwardRef<
<th
ref={ref}
className={cn(
"h-10 whitespace-nowrap bg-default-100 px-4 text-left align-middle text-tiny font-semibold text-foreground-500 outline-none first:rounded-l-lg last:rounded-r-lg data-[focus-visible=true]:z-10 data-[hover=true]:text-foreground-400 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-offset-2 data-[focus-visible=true]:outline-focus dark:bg-prowler-blue-800 dark:text-slate-400 rtl:text-right rtl:first:rounded-l-[unset] rtl:first:rounded-r-lg rtl:last:rounded-l-lg rtl:last:rounded-r-[unset] [&:has([role=checkbox])]:pr-0",
"h-10 whitespace-nowrap bg-default-100 px-2 text-left align-middle text-tiny font-semibold text-foreground-500 outline-none first:rounded-l-lg last:rounded-r-lg data-[focus-visible=true]:z-10 data-[hover=true]:text-foreground-400 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-offset-2 data-[focus-visible=true]:outline-focus dark:bg-prowler-blue-800 dark:text-slate-400 rtl:text-right rtl:first:rounded-l-[unset] rtl:first:rounded-r-lg rtl:last:rounded-l-lg rtl:last:rounded-r-[unset] [&:has([role=checkbox])]:pr-0",
className,
)}
{...props}
@@ -90,7 +90,7 @@ const TableCell = React.forwardRef<
<td
ref={ref}
className={cn(
"px-4 py-2.5 align-middle [&:has([role=checkbox])]:pr-0",
"px-2 py-2.5 align-middle [&:has([role=checkbox])]:pr-0",
className,
)}
{...props}
+6
View File
@@ -327,6 +327,7 @@ export interface ScanProps {
started_at: string;
completed_at: string;
scheduled_at: string;
next_scan_at: string;
};
relationships: {
provider: {
@@ -342,6 +343,11 @@ export interface ScanProps {
};
};
};
providerInfo?: {
provider: "aws" | "azure" | "gcp" | "kubernetes";
uid: string;
alias: string;
};
}
export interface FindingProps {