mirror of
https://github.com/prowler-cloud/prowler.git
synced 2025-12-19 05:17:47 +00:00
Compare commits
2 Commits
0d0dabe166
...
feat/PRWLR
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e40c2ceb1 | ||
|
|
71aec6aede |
@@ -365,6 +365,7 @@ export const S3IntegrationForm = ({
|
||||
<FormButtons
|
||||
setIsOpen={() => {}}
|
||||
onCancel={handleBack}
|
||||
submitColor="danger"
|
||||
submitText="Create Integration"
|
||||
cancelText="Back"
|
||||
loadingText="Creating..."
|
||||
|
||||
@@ -10,7 +10,7 @@ const alertVariants = cva(
|
||||
variant: {
|
||||
default: "bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50",
|
||||
destructive:
|
||||
"bg-danger-50 border-red-500/50 text-red-200 dark:border-red-500 dark:border-red-900/50 dark:text-red-200 dark:dark:border-red-900",
|
||||
"bg-danger-50 border-red-500/50 text-red-700 dark:border-red-500 dark:border-red-900/50 dark:text-red-700 dark:dark:border-red-900",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ReactNode } from "react";
|
||||
|
||||
import { CustomButton } from "@/components/ui/custom/custom-button";
|
||||
|
||||
interface ModalButtonsProps {
|
||||
@@ -7,6 +9,7 @@ interface ModalButtonsProps {
|
||||
isDisabled?: boolean;
|
||||
submitText?: string;
|
||||
submitColor?: "action" | "danger";
|
||||
submitIcon?: ReactNode;
|
||||
}
|
||||
|
||||
export const ModalButtons = ({
|
||||
@@ -16,24 +19,32 @@ export const ModalButtons = ({
|
||||
isDisabled = false,
|
||||
submitText = "Save",
|
||||
submitColor = "action",
|
||||
submitIcon,
|
||||
}: ModalButtonsProps) => {
|
||||
return (
|
||||
<div className="flex w-full justify-end gap-3 pt-4">
|
||||
<div className="flex w-full justify-center gap-6">
|
||||
<CustomButton
|
||||
size="lg"
|
||||
radius="lg"
|
||||
variant="faded"
|
||||
type="button"
|
||||
ariaLabel="Cancel"
|
||||
color="transparent"
|
||||
variant="light"
|
||||
className="w-full bg-transparent"
|
||||
onPress={onCancel}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
Cancel
|
||||
</CustomButton>
|
||||
<CustomButton
|
||||
size="lg"
|
||||
radius="lg"
|
||||
className="w-full"
|
||||
ariaLabel={submitText}
|
||||
color={submitColor}
|
||||
onPress={onSubmit}
|
||||
isLoading={isLoading}
|
||||
isDisabled={isDisabled || isLoading}
|
||||
startContent={submitIcon}
|
||||
>
|
||||
{submitText}
|
||||
</CustomButton>
|
||||
@@ -8,7 +8,7 @@ import { SaveIcon } from "@/components/icons";
|
||||
import { CustomButton } from "../custom";
|
||||
|
||||
interface FormCancelButtonProps {
|
||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
setIsOpen?: Dispatch<SetStateAction<boolean>>;
|
||||
onCancel?: () => void;
|
||||
children?: React.ReactNode;
|
||||
leftIcon?: React.ReactNode;
|
||||
@@ -19,10 +19,11 @@ interface FormSubmitButtonProps {
|
||||
loadingText?: string;
|
||||
isDisabled?: boolean;
|
||||
rightIcon?: React.ReactNode;
|
||||
color?: SubmitColorsType;
|
||||
}
|
||||
|
||||
interface FormButtonsProps {
|
||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
setIsOpen?: Dispatch<SetStateAction<boolean>>;
|
||||
onCancel?: () => void;
|
||||
submitText?: string;
|
||||
cancelText?: string;
|
||||
@@ -30,8 +31,16 @@ interface FormButtonsProps {
|
||||
isDisabled?: boolean;
|
||||
rightIcon?: React.ReactNode;
|
||||
leftIcon?: React.ReactNode;
|
||||
submitColor?: SubmitColorsType;
|
||||
}
|
||||
|
||||
const SubmitColors = {
|
||||
action: "action",
|
||||
danger: "danger",
|
||||
} as const;
|
||||
|
||||
export type SubmitColorsType = (typeof SubmitColors)[keyof typeof SubmitColors];
|
||||
|
||||
const FormCancelButton = ({
|
||||
setIsOpen,
|
||||
onCancel,
|
||||
@@ -41,7 +50,7 @@ const FormCancelButton = ({
|
||||
const handleCancel = () => {
|
||||
if (onCancel) {
|
||||
onCancel();
|
||||
} else {
|
||||
} else if (setIsOpen) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
@@ -53,6 +62,7 @@ const FormCancelButton = ({
|
||||
className="w-full bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={handleCancel}
|
||||
startContent={leftIcon}
|
||||
>
|
||||
@@ -65,6 +75,7 @@ const FormSubmitButton = ({
|
||||
children = "Save",
|
||||
loadingText = "Loading",
|
||||
isDisabled = false,
|
||||
color = "action",
|
||||
rightIcon,
|
||||
}: FormSubmitButtonProps) => {
|
||||
const { pending } = useFormStatus();
|
||||
@@ -75,8 +86,9 @@ const FormSubmitButton = ({
|
||||
ariaLabel="Save"
|
||||
className="w-full"
|
||||
variant="solid"
|
||||
color="action"
|
||||
color={color}
|
||||
size="lg"
|
||||
radius="lg"
|
||||
isLoading={pending}
|
||||
isDisabled={isDisabled}
|
||||
startContent={!pending && rightIcon}
|
||||
@@ -88,6 +100,7 @@ const FormSubmitButton = ({
|
||||
|
||||
export const FormButtons = ({
|
||||
setIsOpen,
|
||||
submitColor,
|
||||
onCancel,
|
||||
submitText = "Save",
|
||||
cancelText = "Cancel",
|
||||
@@ -110,6 +123,7 @@ export const FormButtons = ({
|
||||
loadingText={loadingText}
|
||||
isDisabled={isDisabled}
|
||||
rightIcon={rightIcon}
|
||||
color={submitColor}
|
||||
>
|
||||
{submitText}
|
||||
</FormSubmitButton>
|
||||
|
||||
@@ -30,7 +30,7 @@ export const ApiKeySuccessModal = ({
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<Alert variant="destructive">
|
||||
<AlertTitle>⚠️ Important</AlertTitle>
|
||||
<AlertTitle>⚠️ Warning</AlertTitle>
|
||||
<AlertDescription>
|
||||
This is the only time you will see this API key. Please copy it now
|
||||
and store it securely. Once you close this dialog, the key cannot be
|
||||
|
||||
@@ -22,20 +22,20 @@ export const createApiKeyColumns = (
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="NAME" param="name" />
|
||||
<DataTableColumnHeader column={column} title="Name" param="name" />
|
||||
),
|
||||
cell: ({ row }) => <NameCell apiKey={row.original} />,
|
||||
},
|
||||
{
|
||||
accessorKey: "prefix",
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="PREFIX" param="prefix" />
|
||||
<DataTableColumnHeader column={column} title="Prefix" param="prefix" />
|
||||
),
|
||||
cell: ({ row }) => <PrefixCell apiKey={row.original} />,
|
||||
},
|
||||
{
|
||||
id: "email",
|
||||
header: "EMAIL",
|
||||
header: "Email",
|
||||
cell: ({ row }) => <EmailCell apiKey={row.original} />,
|
||||
enableSorting: false,
|
||||
},
|
||||
@@ -44,7 +44,7 @@ export const createApiKeyColumns = (
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader
|
||||
column={column}
|
||||
title="CREATED"
|
||||
title="Created"
|
||||
param="inserted_at"
|
||||
/>
|
||||
),
|
||||
@@ -52,7 +52,7 @@ export const createApiKeyColumns = (
|
||||
},
|
||||
{
|
||||
accessorKey: "last_used_at",
|
||||
header: "LAST USED",
|
||||
header: "Last Used",
|
||||
cell: ({ row }) => <LastUsedCell apiKey={row.original} />,
|
||||
enableSorting: false,
|
||||
},
|
||||
@@ -61,7 +61,7 @@ export const createApiKeyColumns = (
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader
|
||||
column={column}
|
||||
title="EXPIRES"
|
||||
title="Expires"
|
||||
param="expires_at"
|
||||
/>
|
||||
),
|
||||
@@ -70,7 +70,7 @@ export const createApiKeyColumns = (
|
||||
{
|
||||
accessorKey: "revoked",
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="STATUS" param="revoked" />
|
||||
<DataTableColumnHeader column={column} title="Status" param="revoked" />
|
||||
),
|
||||
cell: ({ row }) => <StatusCell apiKey={row.original} />,
|
||||
},
|
||||
|
||||
@@ -33,6 +33,9 @@ export function DataTableRowActions({
|
||||
onRevoke,
|
||||
}: DataTableRowActionsProps) {
|
||||
const apiKey = row.original;
|
||||
const isRevoked = apiKey.attributes.revoked;
|
||||
const isExpired = new Date(apiKey.attributes.expires_at) < new Date();
|
||||
const canRevoke = !isRevoked && !isExpired;
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center justify-end gap-2">
|
||||
@@ -62,23 +65,25 @@ export function DataTableRowActions({
|
||||
Edit name
|
||||
</DropdownItem>
|
||||
</DropdownSection>
|
||||
<DropdownSection title="Danger zone">
|
||||
<DropdownItem
|
||||
key="revoke"
|
||||
className="text-danger"
|
||||
color="danger"
|
||||
description="Revoke this API key permanently"
|
||||
textValue="Revoke"
|
||||
startContent={
|
||||
<DeleteDocumentBulkIcon
|
||||
className={clsx(iconClasses, "!text-danger")}
|
||||
/>
|
||||
}
|
||||
onPress={() => onRevoke(apiKey)}
|
||||
>
|
||||
Revoke
|
||||
</DropdownItem>
|
||||
</DropdownSection>
|
||||
{canRevoke ? (
|
||||
<DropdownSection title="Danger zone">
|
||||
<DropdownItem
|
||||
key="revoke"
|
||||
className="text-danger"
|
||||
color="danger"
|
||||
description="Revoke this API key permanently"
|
||||
textValue="Revoke"
|
||||
startContent={
|
||||
<DeleteDocumentBulkIcon
|
||||
className={clsx(iconClasses, "!text-danger")}
|
||||
/>
|
||||
}
|
||||
onPress={() => onRevoke(apiKey)}
|
||||
>
|
||||
Revoke
|
||||
</DropdownItem>
|
||||
</DropdownSection>
|
||||
) : null}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
@@ -8,10 +8,9 @@ import { createApiKey } from "@/actions/api-keys/api-keys";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomInput } from "@/components/ui/custom";
|
||||
import { CustomAlertModal } from "@/components/ui/custom/custom-alert-modal";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { Form, FormButtons } from "@/components/ui/form";
|
||||
|
||||
import { DEFAULT_EXPIRY_DAYS } from "./api-keys/constants";
|
||||
import { ModalButtons } from "./api-keys/modal-buttons";
|
||||
import { calculateExpiryDate } from "./api-keys/utils";
|
||||
|
||||
interface CreateApiKeyModalProps {
|
||||
@@ -45,8 +44,6 @@ export const CreateApiKeyModal = ({
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
const onSubmitClient = async (values: FormValues) => {
|
||||
try {
|
||||
const result = await createApiKey({
|
||||
@@ -99,7 +96,7 @@ export const CreateApiKeyModal = ({
|
||||
onSubmit={form.handleSubmit(onSubmitClient)}
|
||||
className="flex flex-col gap-4"
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex w-full justify-center gap-6">
|
||||
<CustomInput
|
||||
control={form.control}
|
||||
name="name"
|
||||
@@ -127,12 +124,12 @@ export const CreateApiKeyModal = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ModalButtons
|
||||
<FormButtons
|
||||
onCancel={handleClose}
|
||||
onSubmit={form.handleSubmit(onSubmitClient)}
|
||||
isLoading={isLoading}
|
||||
isDisabled={!form.formState.isValid}
|
||||
submitText="Create API Key"
|
||||
cancelText="Cancel"
|
||||
loadingText="Processing..."
|
||||
isDisabled={!form.formState.isValid}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -9,9 +9,8 @@ import { updateApiKey } from "@/actions/api-keys/api-keys";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomInput } from "@/components/ui/custom";
|
||||
import { CustomAlertModal } from "@/components/ui/custom/custom-alert-modal";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { Form, FormButtons } from "@/components/ui/form";
|
||||
|
||||
import { ModalButtons } from "./api-keys/modal-buttons";
|
||||
import { EnrichedApiKey } from "./api-keys/types";
|
||||
import { isApiKeyNameDuplicate } from "./api-keys/utils";
|
||||
|
||||
@@ -45,8 +44,6 @@ export const EditApiKeyNameModal = ({
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
// Sync form data when apiKey changes or modal opens
|
||||
useEffect(() => {
|
||||
if (isOpen && apiKey) {
|
||||
@@ -124,12 +121,12 @@ export const EditApiKeyNameModal = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ModalButtons
|
||||
<FormButtons
|
||||
onCancel={handleClose}
|
||||
onSubmit={form.handleSubmit(onSubmitClient)}
|
||||
isLoading={isLoading}
|
||||
isDisabled={!form.formState.isValid}
|
||||
submitText="Save Changes"
|
||||
cancelText="Cancel"
|
||||
loadingText="Processing..."
|
||||
isDisabled={!form.formState.isValid}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { Snippet } from "@heroui/snippet";
|
||||
import { Trash2Icon } from "lucide-react";
|
||||
|
||||
import { revokeApiKey } from "@/actions/api-keys/api-keys";
|
||||
import {
|
||||
Alert,
|
||||
@@ -7,9 +10,9 @@ import {
|
||||
AlertTitle,
|
||||
} from "@/components/ui/alert/Alert";
|
||||
import { CustomAlertModal } from "@/components/ui/custom/custom-alert-modal";
|
||||
import { ModalButtons } from "@/components/ui/custom/custom-modal-buttons";
|
||||
|
||||
import { FALLBACK_VALUES } from "./api-keys/constants";
|
||||
import { ModalButtons } from "./api-keys/modal-buttons";
|
||||
import { EnrichedApiKey } from "./api-keys/types";
|
||||
import { useModalForm } from "./api-keys/use-modal-form";
|
||||
|
||||
@@ -54,23 +57,26 @@ export const RevokeApiKeyModal = ({
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<Alert variant="destructive">
|
||||
<AlertTitle className="text-danger-700">⚠️ Warning</AlertTitle>
|
||||
<AlertDescription className="text-danger-600">
|
||||
<AlertTitle>⚠️ Warning</AlertTitle>
|
||||
<AlertDescription>
|
||||
This action cannot be undone. This API key will be revoked and will
|
||||
no longer work.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<div className="text-sm">
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>Are you sure you want to revoke this API key?</p>
|
||||
<div className="mt-2 rounded-lg bg-slate-800 p-3">
|
||||
<p className="font-medium text-white">
|
||||
{apiKey?.attributes.name || FALLBACK_VALUES.UNNAMED_KEY}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-slate-400">
|
||||
Prefix: {apiKey?.attributes.prefix}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Snippet
|
||||
hideSymbol
|
||||
hideCopyButton={true}
|
||||
classNames={{
|
||||
pre: "font-mono text-sm break-all whitespace-pre-wrap",
|
||||
}}
|
||||
>
|
||||
<p>{apiKey?.attributes.name || FALLBACK_VALUES.UNNAMED_KEY}</p>
|
||||
<p className="mt-1 text-xs">Prefix: {apiKey?.attributes.prefix}</p>
|
||||
</Snippet>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
@@ -87,6 +93,7 @@ export const RevokeApiKeyModal = ({
|
||||
isDisabled={!apiKey}
|
||||
submitText="Revoke API Key"
|
||||
submitColor="danger"
|
||||
submitIcon={<Trash2Icon size={24} />}
|
||||
/>
|
||||
</CustomAlertModal>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user