mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-04-15 00:57:55 +00:00
feat(ui): S3 integrations pagination added (#8450)
This commit is contained in:
@@ -1,42 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { Card, CardBody, CardHeader, Chip } from "@nextui-org/react";
|
||||
import { Card, CardBody, CardHeader } from "@nextui-org/react";
|
||||
import { SettingsIcon } from "lucide-react";
|
||||
|
||||
import { AmazonS3Icon } from "@/components/icons/services/IconServices";
|
||||
import { CustomButton } from "@/components/ui/custom";
|
||||
import { CustomLink } from "@/components/ui/custom/custom-link";
|
||||
import { IntegrationProps } from "@/types/integrations";
|
||||
|
||||
import { S3IntegrationCardSkeleton } from "./skeleton-s3-integration-card";
|
||||
|
||||
interface S3IntegrationCardProps {
|
||||
integrations?: IntegrationProps[];
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
export const S3IntegrationCard = ({
|
||||
integrations = [],
|
||||
isLoading = false,
|
||||
}: S3IntegrationCardProps) => {
|
||||
const s3Integrations = integrations.filter(
|
||||
(integration) => integration.attributes.integration_type === "amazon_s3",
|
||||
);
|
||||
|
||||
const isConfigured = s3Integrations.length > 0;
|
||||
const connectedCount = s3Integrations.filter(
|
||||
(integration) => integration.attributes.connected,
|
||||
).length;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<S3IntegrationCardSkeleton
|
||||
variant="main"
|
||||
count={s3Integrations.length || 1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const S3IntegrationCard = () => {
|
||||
return (
|
||||
<Card className="dark:bg-gray-800">
|
||||
<CardHeader className="gap-2">
|
||||
@@ -62,84 +33,24 @@ export const S3IntegrationCard = ({
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 self-end sm:self-center">
|
||||
{isConfigured && (
|
||||
<Chip
|
||||
size="sm"
|
||||
color={connectedCount > 0 ? "success" : "warning"}
|
||||
variant="flat"
|
||||
>
|
||||
{connectedCount} / {s3Integrations.length} connected
|
||||
</Chip>
|
||||
)}
|
||||
<CustomButton
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
startContent={<SettingsIcon size={14} />}
|
||||
asLink="/integrations/s3"
|
||||
ariaLabel={
|
||||
isConfigured
|
||||
? "Manage S3 integrations"
|
||||
: "Configure S3 integration"
|
||||
}
|
||||
ariaLabel="Manage S3 integrations"
|
||||
>
|
||||
{isConfigured ? "Manage" : "Configure"}
|
||||
Manage
|
||||
</CustomButton>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="flex flex-col gap-4">
|
||||
{isConfigured ? (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
{s3Integrations.map((integration) => (
|
||||
<div
|
||||
key={integration.id}
|
||||
className="flex items-center justify-between rounded-lg border border-gray-200 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-800"
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium">
|
||||
{integration.attributes.configuration.bucket_name ||
|
||||
"Unknown Bucket"}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-300">
|
||||
Output directory:{" "}
|
||||
{integration.attributes.configuration
|
||||
.output_directory ||
|
||||
integration.attributes.configuration.path ||
|
||||
"/"}
|
||||
</span>
|
||||
</div>
|
||||
<Chip
|
||||
size="sm"
|
||||
color={
|
||||
integration.attributes.connected ? "success" : "danger"
|
||||
}
|
||||
variant="dot"
|
||||
>
|
||||
{integration.attributes.connected
|
||||
? "Connected"
|
||||
: "Disconnected"}
|
||||
</Chip>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-sm">
|
||||
<span className="font-medium">Status: </span>
|
||||
<span className="text-gray-500">Not configured</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
Export your security findings to Amazon S3 buckets
|
||||
automatically.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
Configure and manage your Amazon S3 integrations to automatically
|
||||
export security findings to your S3 buckets.
|
||||
</p>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
@@ -49,7 +49,6 @@ export const S3IntegrationForm = ({
|
||||
const isEditingConfig = editMode === "configuration";
|
||||
const isEditingCredentials = editMode === "credentials";
|
||||
|
||||
// Create the form with updated schema and default values
|
||||
const form = useForm({
|
||||
resolver: zodResolver(
|
||||
// For credentials editing, use creation schema (all fields required)
|
||||
@@ -141,13 +140,30 @@ export const S3IntegrationForm = ({
|
||||
return credentials;
|
||||
};
|
||||
|
||||
const buildConfiguration = (values: any) => {
|
||||
const buildConfiguration = (values: any, isPartial = false) => {
|
||||
const configuration: any = {};
|
||||
|
||||
// Always include all fields for both creation and edit modes
|
||||
// Backend expects complete configuration object
|
||||
configuration.bucket_name = values.bucket_name;
|
||||
configuration.output_directory = values.output_directory || "output";
|
||||
// For creation mode, include all fields
|
||||
if (!isPartial) {
|
||||
configuration.bucket_name = values.bucket_name;
|
||||
configuration.output_directory = values.output_directory || "output";
|
||||
} else {
|
||||
// For edit mode, bucket_name and output_directory are treated as a pair
|
||||
const originalBucketName =
|
||||
integration?.attributes.configuration.bucket_name || "";
|
||||
const originalOutputDirectory =
|
||||
integration?.attributes.configuration.output_directory || "";
|
||||
|
||||
const bucketNameChanged = values.bucket_name !== originalBucketName;
|
||||
const outputDirectoryChanged =
|
||||
values.output_directory !== originalOutputDirectory;
|
||||
|
||||
// If either field changed, send both (as a pair)
|
||||
if (bucketNameChanged || outputDirectoryChanged) {
|
||||
configuration.bucket_name = values.bucket_name;
|
||||
configuration.output_directory = values.output_directory || "output";
|
||||
}
|
||||
}
|
||||
|
||||
return configuration;
|
||||
};
|
||||
@@ -158,8 +174,10 @@ export const S3IntegrationForm = ({
|
||||
formData.append("integration_type", values.integration_type);
|
||||
|
||||
if (isEditingConfig) {
|
||||
const configuration = buildConfiguration(values);
|
||||
formData.append("configuration", JSON.stringify(configuration));
|
||||
const configuration = buildConfiguration(values, true);
|
||||
if (Object.keys(configuration).length > 0) {
|
||||
formData.append("configuration", JSON.stringify(configuration));
|
||||
}
|
||||
// Always send providers array, even if empty, to update relationships
|
||||
formData.append("providers", JSON.stringify(values.providers || []));
|
||||
} else if (isEditingCredentials) {
|
||||
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
import { AmazonS3Icon } from "@/components/icons/services/IconServices";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomAlertModal, CustomButton } from "@/components/ui/custom";
|
||||
import { DataTablePagination } from "@/components/ui/table/data-table-pagination";
|
||||
import { MetaDataProps } from "@/types";
|
||||
import { IntegrationProps } from "@/types/integrations";
|
||||
import { ProviderProps } from "@/types/providers";
|
||||
|
||||
@@ -28,11 +30,13 @@ import { S3IntegrationCardSkeleton } from "./skeleton-s3-integration-card";
|
||||
interface S3IntegrationsManagerProps {
|
||||
integrations: IntegrationProps[];
|
||||
providers: ProviderProps[];
|
||||
metadata?: MetaDataProps;
|
||||
}
|
||||
|
||||
export const S3IntegrationsManager = ({
|
||||
integrations,
|
||||
providers,
|
||||
metadata,
|
||||
}: S3IntegrationsManagerProps) => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [editingIntegration, setEditingIntegration] =
|
||||
@@ -392,6 +396,12 @@ export const S3IntegrationsManager = ({
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{metadata && integrations.length > 0 && (
|
||||
<div className="mt-6">
|
||||
<DataTablePagination metadata={metadata} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user