Compare commits

...

1 Commits

Author SHA1 Message Date
Nitpicks Agent
2828313f71 feat: improve dashboard layout consistency and spacing 2025-07-31 09:35:55 +00:00
7 changed files with 104 additions and 94 deletions

View File

@@ -36,19 +36,19 @@ export default function Home({
<FilterControls providers mutedFindings showClearButton={false} />
<div className="grid grid-cols-12 gap-12 lg:gap-6">
<div className="col-span-12 lg:col-span-4">
<div className="col-span-12 lg:col-span-4 min-h-[400px]">
<Suspense fallback={<SkeletonProvidersOverview />}>
<SSRProvidersOverview />
</Suspense>
</div>
<div className="col-span-12 lg:col-span-4">
<div className="col-span-12 lg:col-span-4 min-h-[400px]">
<Suspense fallback={<SkeletonFindingsBySeverityChart />}>
<SSRFindingsBySeverity searchParams={searchParams} />
</Suspense>
</div>
<div className="col-span-12 lg:col-span-4">
<div className="col-span-12 lg:col-span-4 min-h-[400px]">
<Suspense fallback={<SkeletonFindingsByStatusChart />}>
<SSRFindingsByStatus searchParams={searchParams} />
</Suspense>
@@ -72,10 +72,12 @@ const SSRProvidersOverview = async () => {
const providersOverview = await getProvidersOverview({});
return (
<>
<div className="flex h-full flex-col">
<h3 className="mb-4 text-sm font-bold uppercase">Providers Overview</h3>
<ProvidersOverview providersOverview={providersOverview} />
</>
<div className="flex-1">
<ProvidersOverview providersOverview={providersOverview} />
</div>
</div>
);
};
@@ -95,10 +97,12 @@ const SSRFindingsByStatus = async ({
const findingsByStatus = await getFindingsByStatus({ filters });
return (
<>
<div className="flex h-full flex-col">
<h3 className="mb-4 text-sm font-bold uppercase">Findings by Status</h3>
<FindingsByStatusChart findingsByStatus={findingsByStatus} />
</>
<div className="flex-1">
<FindingsByStatusChart findingsByStatus={findingsByStatus} />
</div>
</div>
);
};
@@ -118,10 +122,12 @@ const SSRFindingsBySeverity = async ({
const findingsBySeverity = await getFindingsBySeverity({ filters });
return (
<>
<div className="flex h-full flex-col">
<h3 className="mb-4 text-sm font-bold uppercase">Findings by Severity</h3>
<FindingsBySeverityChart findingsBySeverity={findingsBySeverity} />
</>
<div className="flex-1">
<FindingsBySeverityChart findingsBySeverity={findingsBySeverity} />
</div>
</div>
);
};

View File

@@ -71,59 +71,57 @@ export const FindingsBySeverityChart = ({
return (
<Card className="h-full dark:bg-prowler-blue-400">
<CardBody>
<div className="my-auto">
<ChartContainer config={chartConfig}>
<BarChart
accessibilityLayer
data={chartData}
<CardBody className="flex h-full items-center justify-center p-6">
<ChartContainer config={chartConfig} className="h-full w-full">
<BarChart
accessibilityLayer
data={chartData}
layout="vertical"
barGap={2}
height={250}
margin={{ left: 50 }}
width={500}
>
<YAxis
dataKey="severity"
type="category"
tickLine={false}
tickMargin={20}
axisLine={false}
tickFormatter={(value) =>
chartConfig[value as keyof typeof chartConfig]?.label
}
/>
<XAxis dataKey="findings" type="number" hide>
<LabelList position="insideTop" offset={1} fontSize={12} />
</XAxis>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent indicator="line" />}
/>
<Bar
dataKey="findings"
layout="vertical"
barGap={2}
height={200}
margin={{ left: 50 }}
width={500}
>
<YAxis
dataKey="severity"
type="category"
tickLine={false}
tickMargin={20}
axisLine={false}
tickFormatter={(value) =>
chartConfig[value as keyof typeof chartConfig]?.label
radius={12}
barSize={20}
onClick={(data) => {
const severity = data.severity as keyof typeof chartConfig;
const link = chartConfig[severity]?.link;
if (link) {
window.location.href = link;
}
}}
style={{ cursor: "pointer" }}
>
<LabelList
position="insideRight"
offset={5}
className="fill-foreground font-bold"
fontSize={11}
/>
<XAxis dataKey="findings" type="number" hide>
<LabelList position="insideTop" offset={1} fontSize={12} />
</XAxis>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent indicator="line" />}
/>
<Bar
dataKey="findings"
layout="vertical"
radius={12}
barSize={20}
onClick={(data) => {
const severity = data.severity as keyof typeof chartConfig;
const link = chartConfig[severity]?.link;
if (link) {
window.location.href = link;
}
}}
style={{ cursor: "pointer" }}
>
<LabelList
position="insideRight"
offset={5}
className="fill-foreground font-bold"
fontSize={11}
/>
</Bar>
</BarChart>
</ChartContainer>
</div>
</Bar>
</BarChart>
</ChartContainer>
</CardBody>
</Card>
);

View File

@@ -2,14 +2,15 @@ import { Card, CardBody, CardHeader, Skeleton } from "@nextui-org/react";
export const SkeletonFindingsBySeverityChart = () => {
return (
<Card>
<CardHeader>
<Skeleton className="h-6 w-1/3 rounded-lg">
<div className="h-6 bg-default-200"></div>
<div className="flex h-full flex-col">
<div className="mb-4">
<Skeleton className="h-4 w-1/3 rounded-lg">
<div className="h-4 bg-default-200"></div>
</Skeleton>
</CardHeader>
<CardBody>
<div className="flex flex-col gap-4">
</div>
<Card className="flex-1 h-full">
<CardBody className="flex h-full items-center justify-center p-6">
<div className="flex flex-col gap-4 w-full">
{/* Critical */}
<div className="flex items-center gap-2">
<Skeleton className="h-4 w-1/4 rounded-lg">
@@ -58,5 +59,6 @@ export const SkeletonFindingsBySeverityChart = () => {
</div>
</CardBody>
</Card>
</div>
);
};

View File

@@ -120,11 +120,11 @@ export const FindingsByStatusChart: React.FC<FindingsByStatusChartProps> = ({
return (
<Card className="h-full dark:bg-prowler-blue-400">
<CardBody>
<CardBody className="flex h-full flex-col items-center justify-between p-6">
<div className="flex h-full flex-col items-center justify-between">
<ChartContainer
config={chartConfig}
className="aspect-square w-[250px] min-w-[250px]"
className="aspect-square w-[200px] min-w-[200px] flex-shrink-0"
>
<PieChart>
<ChartTooltip cursor={false} content={<ChartTooltipContent />} />
@@ -170,7 +170,7 @@ export const FindingsByStatusChart: React.FC<FindingsByStatusChartProps> = ({
</PieChart>
</ChartContainer>
<div className="flex min-h-[156px] flex-col justify-start gap-4">
<div className="flex flex-1 flex-col justify-center gap-3 pt-4">
<div className="flex flex-col gap-2">
<div className="flex items-center space-x-2">
<Link

View File

@@ -2,21 +2,22 @@ import { Card, CardBody, CardHeader, Skeleton } from "@nextui-org/react";
export const SkeletonFindingsByStatusChart = () => {
return (
<Card>
<CardHeader>
<Skeleton className="h-6 w-1/4 rounded-lg">
<div className="h-6 bg-default-200"></div>
<div className="flex h-full flex-col">
<div className="mb-4">
<Skeleton className="h-4 w-1/4 rounded-lg">
<div className="h-4 bg-default-200"></div>
</Skeleton>
</CardHeader>
<CardBody>
<div className="flex items-center gap-6">
</div>
<Card className="flex-1 h-full">
<CardBody className="flex h-full flex-col items-center justify-between p-6">
<div className="flex h-full flex-col items-center justify-between">
{/* Circle Chart Skeleton */}
<Skeleton className="aspect-square h-[150px] w-[150px] rounded-full">
<div className="h-[150px] w-[150px] bg-default-200"></div>
<Skeleton className="aspect-square h-[200px] w-[200px] rounded-full flex-shrink-0">
<div className="h-[200px] w-[200px] bg-default-200"></div>
</Skeleton>
{/* Text Details Skeleton */}
<div className="flex flex-col gap-4">
<div className="flex flex-1 flex-col justify-center gap-3 pt-4">
{/* Pass Findings */}
<div className="flex flex-col gap-2">
<div className="flex items-center space-x-2">
@@ -50,5 +51,6 @@ export const SkeletonFindingsByStatusChart = () => {
</div>
</CardBody>
</Card>
</div>
);
};

View File

@@ -49,8 +49,8 @@ export const ProvidersOverview = ({
if (!providersOverview || !Array.isArray(providersOverview.data)) {
return (
<Card className="h-full dark:bg-prowler-blue-400">
<CardBody>
<div className="my-auto grid grid-cols-1 gap-3">
<CardBody className="flex h-full flex-col justify-between p-6">
<div className="flex-1 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">
@@ -97,8 +97,8 @@ export const ProvidersOverview = ({
return (
<Card className="h-full dark:bg-prowler-blue-400">
<CardBody>
<div className="my-auto grid grid-cols-1 gap-3">
<CardBody className="flex h-full flex-col justify-between p-6">
<div className="flex-1 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">
@@ -177,7 +177,7 @@ export const ProvidersOverview = ({
</span>
</div>
</div>
<div className="mt-4 flex w-full items-center justify-end">
<div className="mt-4 flex w-full items-center justify-end flex-shrink-0">
<CustomButton
asLink="/providers"
ariaLabel="Go to Providers page"

View File

@@ -4,14 +4,15 @@ export const SkeletonProvidersOverview = () => {
const rows = 4;
return (
<Card>
<CardHeader>
<Skeleton className="h-6 w-1/3 rounded-lg">
<div className="h-6 bg-default-200"></div>
<div className="flex h-full flex-col">
<div className="mb-4">
<Skeleton className="h-4 w-1/3 rounded-lg">
<div className="h-4 bg-default-200"></div>
</Skeleton>
</CardHeader>
<CardBody>
<div className="grid grid-cols-1 gap-4">
</div>
<Card className="flex-1 h-full">
<CardBody className="flex h-full flex-col justify-between p-6">
<div className="flex-1 grid grid-cols-1 gap-4">
{/* Header Skeleton */}
<div className="grid grid-cols-4 border-b pb-2 text-sm font-semibold">
<Skeleton className="h-5 w-full rounded-lg">
@@ -60,5 +61,6 @@ export const SkeletonProvidersOverview = () => {
</div>
</CardBody>
</Card>
</div>
);
};