mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-09 21:14:22 +00:00
fix(ui): replace HeroUI dropdowns with shadcn selects (#10097)
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
export * from "../providers/enhanced-provider-selector";
|
||||
export * from "./api-key/api-key-link-card";
|
||||
export * from "./jira/jira-integration-card";
|
||||
export * from "./jira/jira-integration-form";
|
||||
|
||||
@@ -8,12 +8,18 @@ import { useState } from "react";
|
||||
import { Control, useForm } from "react-hook-form";
|
||||
|
||||
import { createIntegration, updateIntegration } from "@/actions/integrations";
|
||||
import { EnhancedProviderSelector } from "@/components/providers/enhanced-provider-selector";
|
||||
import { PROVIDER_ICONS } from "@/components/findings/table/provider-icon-cell";
|
||||
import { AWSRoleCredentialsForm } from "@/components/providers/workflow/forms/select-credentials-type/aws/credentials-type/aws-role-credentials-form";
|
||||
import { EnhancedMultiSelect } from "@/components/shadcn/select/enhanced-multi-select";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomInput } from "@/components/ui/custom";
|
||||
import { CustomLink } from "@/components/ui/custom/custom-link";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { FormButtons } from "@/components/ui/form/form-buttons";
|
||||
import { getAWSCredentialsTemplateLinks } from "@/lib";
|
||||
import { AWSCredentialsRole } from "@/types";
|
||||
@@ -272,18 +278,40 @@ export const S3IntegrationForm = ({
|
||||
|
||||
// Show configuration step (step 0 or editing configuration)
|
||||
if (isEditingConfig || currentStep === 0) {
|
||||
const providerOptions = providers.map((provider) => {
|
||||
const Icon = PROVIDER_ICONS[provider.attributes.provider];
|
||||
return {
|
||||
value: provider.id,
|
||||
label: provider.attributes.alias || provider.attributes.uid,
|
||||
icon: Icon ? <Icon width={20} height={20} /> : undefined,
|
||||
description: provider.attributes.connection.connected
|
||||
? "Connected"
|
||||
: "Disconnected",
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Provider Selection */}
|
||||
<div className="flex flex-col gap-4">
|
||||
<EnhancedProviderSelector
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="providers"
|
||||
providers={providers}
|
||||
label="Cloud Providers"
|
||||
placeholder="Select providers to integrate with"
|
||||
selectionMode="multiple"
|
||||
enableSearch={true}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<FormControl>
|
||||
<EnhancedMultiSelect
|
||||
options={providerOptions}
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value || []}
|
||||
placeholder="Select providers to integrate with"
|
||||
searchable={true}
|
||||
maxCount={1}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage className="text-text-error max-w-full text-xs" />
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,15 +6,21 @@ import { Radio, RadioGroup } from "@heroui/radio";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Control, useForm } from "react-hook-form";
|
||||
|
||||
import { createIntegration, updateIntegration } from "@/actions/integrations";
|
||||
import { EnhancedProviderSelector } from "@/components/providers/enhanced-provider-selector";
|
||||
import { PROVIDER_ICONS } from "@/components/findings/table/provider-icon-cell";
|
||||
import { AWSRoleCredentialsForm } from "@/components/providers/workflow/forms/select-credentials-type/aws/credentials-type/aws-role-credentials-form";
|
||||
import { EnhancedMultiSelect } from "@/components/shadcn/select/enhanced-multi-select";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomLink } from "@/components/ui/custom/custom-link";
|
||||
import { Form, FormControl, FormField } from "@/components/ui/form";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { FormButtons } from "@/components/ui/form/form-buttons";
|
||||
import { getAWSCredentialsTemplateLinks } from "@/lib";
|
||||
import { AWSCredentialsRole } from "@/types";
|
||||
@@ -52,7 +58,7 @@ export const SecurityHubIntegrationForm = ({
|
||||
const isEditingConfig = editMode === "configuration";
|
||||
const isEditingCredentials = editMode === "credentials";
|
||||
|
||||
const disabledProviderIds = useMemo(() => {
|
||||
const disabledProviderIds = (() => {
|
||||
// When editing, no providers should be disabled since we're not changing it
|
||||
if (isEditing) {
|
||||
return [];
|
||||
@@ -69,7 +75,7 @@ export const SecurityHubIntegrationForm = ({
|
||||
});
|
||||
|
||||
return usedProviderIds;
|
||||
}, [isEditing, existingIntegrations]);
|
||||
})();
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(
|
||||
@@ -107,6 +113,26 @@ export const SecurityHubIntegrationForm = ({
|
||||
const providerIdValue = form.watch("provider_id");
|
||||
const hasErrors = !!form.formState.errors.provider_id || !providerIdValue;
|
||||
|
||||
const providerOptions = providers
|
||||
.filter((provider) => provider.attributes.provider === "aws")
|
||||
.map((provider) => {
|
||||
const isDisabled = disabledProviderIds.includes(provider.id);
|
||||
const connectionLabel = provider.attributes.connection.connected
|
||||
? "Connected"
|
||||
: "Disconnected";
|
||||
|
||||
const Icon = PROVIDER_ICONS[provider.attributes.provider];
|
||||
return {
|
||||
value: provider.id,
|
||||
label: provider.attributes.alias || provider.attributes.uid,
|
||||
icon: Icon ? <Icon width={20} height={20} /> : undefined,
|
||||
description: isDisabled
|
||||
? `${connectionLabel} (Already in use)`
|
||||
: connectionLabel,
|
||||
disabled: isDisabled,
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!useCustomCredentials && isCreating) {
|
||||
setCurrentStep(0);
|
||||
@@ -325,17 +351,29 @@ export const SecurityHubIntegrationForm = ({
|
||||
{!isEditingConfig && (
|
||||
<>
|
||||
<div className="flex flex-col gap-4">
|
||||
<EnhancedProviderSelector
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="provider_id"
|
||||
providers={providers}
|
||||
label="AWS Provider"
|
||||
placeholder="Search and select an AWS provider"
|
||||
isInvalid={!!form.formState.errors.provider_id}
|
||||
selectionMode="single"
|
||||
providerType="aws"
|
||||
enableSearch={true}
|
||||
disabledProviderIds={disabledProviderIds}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<FormControl>
|
||||
<EnhancedMultiSelect
|
||||
options={providerOptions}
|
||||
onValueChange={(values) => {
|
||||
field.onChange(values.at(-1) ?? "");
|
||||
}}
|
||||
defaultValue={field.value ? [field.value] : []}
|
||||
placeholder="Search and select an AWS provider"
|
||||
searchable={true}
|
||||
hideSelectAll={true}
|
||||
maxCount={1}
|
||||
closeOnSelect={true}
|
||||
resetOnDefaultValueChange={true}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage className="text-text-error max-w-full text-xs" />
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { format } from "date-fns";
|
||||
import { PlusIcon, Trash2Icon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
@@ -16,7 +15,7 @@ import {
|
||||
IntegrationCardHeader,
|
||||
IntegrationSkeleton,
|
||||
} from "@/components/integrations/shared";
|
||||
import { Button } from "@/components/shadcn";
|
||||
import { Badge, Button } from "@/components/shadcn";
|
||||
import { Modal } from "@/components/shadcn/modal";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { DataTablePagination } from "@/components/ui/table/data-table-pagination";
|
||||
@@ -385,14 +384,13 @@ export const SecurityHubIntegrationsManager = ({
|
||||
{enabledRegions.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{enabledRegions.map((region) => (
|
||||
<Chip
|
||||
<Badge
|
||||
key={region}
|
||||
size="sm"
|
||||
variant="flat"
|
||||
className="bg-bg-neutral-secondary"
|
||||
variant="outline"
|
||||
className="border-border-neutral-secondary bg-bg-neutral-secondary text-text-neutral-primary text-xs font-normal"
|
||||
>
|
||||
{region}
|
||||
</Chip>
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
import { Badge } from "@/components/shadcn";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface IntegrationCardHeaderProps {
|
||||
icon: ReactNode;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
chips?: Array<{
|
||||
label: string;
|
||||
color?:
|
||||
| "default"
|
||||
| "primary"
|
||||
| "secondary"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "danger";
|
||||
variant?: "solid" | "bordered" | "light" | "flat" | "faded" | "shadow";
|
||||
className?: string;
|
||||
}>;
|
||||
connectionStatus?: {
|
||||
connected: boolean;
|
||||
@@ -63,25 +58,30 @@ export const IntegrationCardHeader = ({
|
||||
{(chips.length > 0 || connectionStatus) && (
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{chips.map((chip, index) => (
|
||||
<Chip
|
||||
<Badge
|
||||
key={index}
|
||||
size="sm"
|
||||
variant={chip.variant || "flat"}
|
||||
color={chip.color || "default"}
|
||||
className="text-xs"
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"border-border-neutral-secondary bg-bg-neutral-secondary text-text-neutral-primary text-xs font-normal",
|
||||
chip.className,
|
||||
)}
|
||||
>
|
||||
{chip.label}
|
||||
</Chip>
|
||||
</Badge>
|
||||
))}
|
||||
{connectionStatus && (
|
||||
<Chip
|
||||
size="sm"
|
||||
color={connectionStatus.connected ? "success" : "danger"}
|
||||
variant="flat"
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"text-xs font-normal",
|
||||
connectionStatus.connected
|
||||
? "bg-bg-pass-secondary text-text-success-primary border-transparent"
|
||||
: "bg-bg-danger-secondary text-text-danger border-transparent",
|
||||
)}
|
||||
>
|
||||
{connectionStatus.label ||
|
||||
(connectionStatus.connected ? "Connected" : "Disconnected")}
|
||||
</Chip>
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user