feat(ui): add RadioCard and AWS method selector components

Add reusable RadioCard component for selectable card options. Add
AwsMethodSelector that presents single-account vs Organizations bulk
connect, with Organizations gated behind NEXT_PUBLIC_IS_CLOUD_ENV.
This commit is contained in:
alejandrobailo
2026-02-17 09:46:16 +01:00
parent de81f6384e
commit d3b0c3c393
3 changed files with 116 additions and 0 deletions
+1
View File
@@ -5,4 +5,5 @@ export * from "./forms/delete-form";
export * from "./link-to-scans";
export * from "./muted-findings-config-button";
export * from "./provider-info";
export * from "./radio-card";
export * from "./radio-group-provider";
@@ -0,0 +1,62 @@
"use client";
import { ArrowRightCircle, Ban, Box } from "lucide-react";
import { RadioCard } from "@/components/providers/radio-card";
interface AwsMethodSelectorProps {
onSelectSingle: () => void;
onSelectOrganizations: () => void;
}
export function AwsMethodSelector({
onSelectSingle,
onSelectOrganizations,
}: AwsMethodSelectorProps) {
const isCloudEnv = process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true";
return (
<div className="flex flex-col gap-3">
<p className="text-muted-foreground text-sm">
Select a method to add your accounts to Prowler.
</p>
<RadioCard
icon={Box}
title="Add A Single AWS Cloud Account"
onClick={onSelectSingle}
/>
<RadioCard
icon={Ban}
title="Add Multiple Accounts With AWS Organizations"
onClick={onSelectOrganizations}
disabled={!isCloudEnv}
>
{!isCloudEnv && <CtaBadge />}
</RadioCard>
</div>
);
}
function CtaBadge() {
return (
<a
href="https://prowler.com/pricing"
target="_blank"
rel="noopener noreferrer"
className="flex h-[52px] shrink-0 items-center justify-center rounded-lg px-4 py-3 transition-opacity hover:opacity-90"
style={{
backgroundImage:
"linear-gradient(112deg, rgb(46, 229, 155) 3.5%, rgb(98, 223, 240) 98.8%)",
}}
>
<div className="flex items-center gap-1.5">
<ArrowRightCircle className="text-primary-foreground size-5" />
<span className="text-primary-foreground text-sm leading-6 font-bold">
Get Prowler Cloud
</span>
</div>
</a>
);
}
+53
View File
@@ -0,0 +1,53 @@
import { cn } from "@/lib/utils";
interface RadioCardProps {
icon: React.ComponentType<{ className?: string }>;
title: string;
onClick: () => void;
disabled?: boolean;
/** Optional trailing content (e.g. a CTA badge). */
children?: React.ReactNode;
}
export function RadioCard({
icon: Icon,
title,
onClick,
disabled = false,
children,
}: RadioCardProps) {
return (
<button
type="button"
onClick={onClick}
disabled={disabled}
className={cn(
"flex min-h-[72px] w-full items-center gap-4 rounded-lg border px-3 py-2.5 text-left transition-colors",
disabled
? "cursor-not-allowed border-border-neutral-primary bg-bg-neutral-tertiary"
: "hover:border-primary cursor-pointer border-border-neutral-primary bg-bg-neutral-tertiary",
)}
>
<div className="size-[18px] shrink-0 rounded-full border border-border-neutral-primary bg-bg-input-primary shadow-xs" />
<div className="flex min-w-0 flex-1 items-center gap-1.5">
<Icon
className={cn(
"size-[18px] shrink-0",
disabled ? "text-text-neutral-tertiary" : "text-muted-foreground",
)}
/>
<span
className={cn(
"truncate text-sm leading-6",
disabled ? "text-text-neutral-tertiary" : "text-foreground",
)}
>
{title}
</span>
</div>
{children}
</button>
);
}