mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
feat(ui): reused custom banner component
This commit is contained in:
@@ -11,21 +11,10 @@ const NO_PROVIDERS_ADDED_ACTION = {
|
||||
LINK: "link",
|
||||
} as const;
|
||||
|
||||
// "page" is the full-screen empty state (Providers page); "hint" is a compact
|
||||
// horizontal banner that sits above other content (Scans page).
|
||||
const NO_PROVIDERS_ADDED_VARIANT = {
|
||||
PAGE: "page",
|
||||
HINT: "hint",
|
||||
} as const;
|
||||
|
||||
type NoProvidersAddedVariant =
|
||||
(typeof NO_PROVIDERS_ADDED_VARIANT)[keyof typeof NO_PROVIDERS_ADDED_VARIANT];
|
||||
|
||||
interface NoProvidersAddedBaseProps {
|
||||
containerClassName?: string;
|
||||
// Tour anchor for the CTA; needed because this empty state replaces the table's AddProviderButton.
|
||||
ctaTourId?: string;
|
||||
variant?: NoProvidersAddedVariant;
|
||||
}
|
||||
|
||||
interface NoProvidersAddedButtonProps extends NoProvidersAddedBaseProps {
|
||||
@@ -44,23 +33,14 @@ type NoProvidersAddedProps =
|
||||
| NoProvidersAddedButtonProps
|
||||
| NoProvidersAddedLinkProps;
|
||||
|
||||
const renderCta = (
|
||||
props: NoProvidersAddedProps,
|
||||
variant: NoProvidersAddedVariant,
|
||||
) => {
|
||||
const isHint = variant === NO_PROVIDERS_ADDED_VARIANT.HINT;
|
||||
const className = isHint
|
||||
? "w-full justify-center md:w-fit"
|
||||
: "w-full max-w-xs justify-center";
|
||||
const size = isHint ? undefined : "lg";
|
||||
|
||||
const renderCta = (props: NoProvidersAddedProps) => {
|
||||
if (props.action === NO_PROVIDERS_ADDED_ACTION.LINK) {
|
||||
return (
|
||||
<Button
|
||||
asChild
|
||||
aria-label="Open Add Provider modal"
|
||||
className={className}
|
||||
size={size}
|
||||
className="w-full max-w-xs justify-center"
|
||||
size="lg"
|
||||
>
|
||||
<Link href={props.href}>Add a Provider</Link>
|
||||
</Button>
|
||||
@@ -70,9 +50,9 @@ const renderCta = (
|
||||
return (
|
||||
<Button
|
||||
aria-label="Open Add Provider modal"
|
||||
className={className}
|
||||
className="w-full max-w-xs justify-center"
|
||||
data-tour-id={props.ctaTourId}
|
||||
size={size}
|
||||
size="lg"
|
||||
onClick={props.onOpenWizard}
|
||||
>
|
||||
Add a Provider
|
||||
@@ -81,33 +61,6 @@ const renderCta = (
|
||||
};
|
||||
|
||||
export const NoProvidersAdded = (props: NoProvidersAddedProps) => {
|
||||
const variant = props.variant ?? NO_PROVIDERS_ADDED_VARIANT.PAGE;
|
||||
|
||||
// Compact horizontal hint, matching NoProvidersConnected so both provider
|
||||
// hints on the Scans page share one composition.
|
||||
if (variant === NO_PROVIDERS_ADDED_VARIANT.HINT) {
|
||||
return (
|
||||
<Card variant="base">
|
||||
<CardContent className="flex w-full flex-col items-start gap-6 md:flex-row md:items-center md:justify-between md:gap-8">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center justify-start gap-3">
|
||||
<InfoIcon className="h-6 w-6 text-gray-800 dark:text-white" />
|
||||
<h2 className="text-lg font-bold text-gray-800 dark:text-white">
|
||||
No Providers Configured
|
||||
</h2>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
No providers have been configured. Start by setting up a provider.
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-full md:w-auto md:shrink-0">
|
||||
{renderCta(props, variant)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
role="region"
|
||||
@@ -134,7 +87,7 @@ export const NoProvidersAdded = (props: NoProvidersAddedProps) => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{renderCta(props, variant)}
|
||||
{renderCta(props)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
|
||||
import { Button, Card, CardContent } from "@/components/shadcn";
|
||||
|
||||
import { InfoIcon } from "../icons/Icons";
|
||||
|
||||
export const NoProvidersConnected = () => {
|
||||
return (
|
||||
<Card variant="base">
|
||||
<CardContent className="flex w-full flex-col items-start gap-6 md:flex-row md:items-center md:justify-between md:gap-8">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center justify-start gap-3">
|
||||
<InfoIcon className="h-6 w-6 text-gray-800 dark:text-white" />
|
||||
<h2 className="text-lg font-bold text-gray-800 dark:text-white">
|
||||
No Connected Providers
|
||||
</h2>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
None of your providers are connected yet. Connect one to launch
|
||||
on-demand scans — imported scans still appear below.
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-full md:w-auto md:shrink-0">
|
||||
<Button
|
||||
asChild
|
||||
className="w-full justify-center md:w-fit"
|
||||
aria-label="Go to Providers page"
|
||||
>
|
||||
<Link href="/providers">Review Providers</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
@@ -1,44 +1,32 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { ADD_PROVIDER_HREF } from "@/lib/providers-navigation";
|
||||
|
||||
import { ScansProvidersEmptyState } from "./scans-providers-empty-state";
|
||||
|
||||
vi.mock("./no-providers-connected", () => ({
|
||||
NoProvidersConnected: () => <div>No Connected Providers</div>,
|
||||
}));
|
||||
|
||||
describe("ScansProvidersEmptyState", () => {
|
||||
it("shows the add provider message with a providers page CTA", () => {
|
||||
// Given/When
|
||||
it("shows the add-provider hint with a providers page CTA when there are no providers", () => {
|
||||
render(<ScansProvidersEmptyState thereIsNoProviders />);
|
||||
|
||||
// Then
|
||||
expect(screen.getByText("No Providers Configured")).toBeInTheDocument();
|
||||
const cta = screen.getByRole("link", {
|
||||
name: /open add provider modal/i,
|
||||
});
|
||||
|
||||
const cta = screen.getByRole("link", { name: /add a provider/i });
|
||||
expect(cta).toHaveAttribute("href", ADD_PROVIDER_HREF);
|
||||
expect(cta.tagName).toBe("A");
|
||||
});
|
||||
|
||||
it("does not render the provider wizard in Scans", () => {
|
||||
// Given/When
|
||||
render(<ScansProvidersEmptyState thereIsNoProviders />);
|
||||
|
||||
// Then
|
||||
expect(screen.getByText("No Providers Configured")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows the no connected providers message", () => {
|
||||
// Given/When
|
||||
it("shows the no-connected-providers hint with a providers page CTA", () => {
|
||||
render(<ScansProvidersEmptyState thereIsNoProviders={false} />);
|
||||
|
||||
// Then
|
||||
expect(screen.getByText("No Connected Providers")).toBeInTheDocument();
|
||||
const cta = screen.getByRole("link", { name: /review providers/i });
|
||||
expect(cta).toHaveAttribute("href", "/providers");
|
||||
expect(cta.tagName).toBe("A");
|
||||
});
|
||||
|
||||
it("does not render the provider wizard dialog in Scans", () => {
|
||||
render(<ScansProvidersEmptyState thereIsNoProviders />);
|
||||
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { NoProvidersAdded } from "@/components/providers/no-providers-added";
|
||||
import { CustomBanner } from "@/components/shadcn/custom/custom-banner";
|
||||
import { ADD_PROVIDER_HREF } from "@/lib/providers-navigation";
|
||||
|
||||
import { NoProvidersConnected } from "./no-providers-connected";
|
||||
|
||||
interface ScansProvidersEmptyStateProps {
|
||||
thereIsNoProviders: boolean;
|
||||
}
|
||||
@@ -11,8 +9,18 @@ export function ScansProvidersEmptyState({
|
||||
thereIsNoProviders,
|
||||
}: ScansProvidersEmptyStateProps) {
|
||||
return thereIsNoProviders ? (
|
||||
<NoProvidersAdded action="link" href={ADD_PROVIDER_HREF} variant="hint" />
|
||||
<CustomBanner
|
||||
title="No Providers Configured"
|
||||
message="No providers have been configured. Start by setting up a provider."
|
||||
buttonLabel="Add a Provider"
|
||||
buttonLink={ADD_PROVIDER_HREF}
|
||||
/>
|
||||
) : (
|
||||
<NoProvidersConnected />
|
||||
<CustomBanner
|
||||
title="No Connected Providers"
|
||||
message="None of your providers are connected yet. Connect one to launch on-demand scans — imported scans still appear below."
|
||||
buttonLabel="Review Providers"
|
||||
buttonLink="/providers"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user