diff --git a/frontend/src/components/v2/Checkbox/Checkbox.tsx b/frontend/src/components/v2/Checkbox/Checkbox.tsx index 87e278119..dc0e1a491 100644 --- a/frontend/src/components/v2/Checkbox/Checkbox.tsx +++ b/frontend/src/components/v2/Checkbox/Checkbox.tsx @@ -30,7 +30,7 @@ export const Checkbox = ({
-
- handlePopUpToggle("addSecretsInAllEnvs", isOpen)} onClose={() => handlePopUpClose("addSecretsInAllEnvs")} decryptFileKey={latestFileKey!} diff --git a/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx b/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx new file mode 100644 index 000000000..52eebed9e --- /dev/null +++ b/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx @@ -0,0 +1,222 @@ +import { Controller, useForm } from "react-hook-form"; +import { faWarning } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; +import { + Button, + Checkbox, + FormControl, + FormLabel, + Input, + Modal, + ModalContent, + SecretInput, + Tooltip +} from "@app/components/v2"; +import { useWorkspace } from "@app/context"; +import { useCreateFolder, useCreateSecretV3, useUpdateSecretV3 } from "@app/hooks/api"; +import { DecryptedSecret, UserWsKeyPair } from "@app/hooks/api/types"; + +const typeSchema = z + .object({ + key: z.string().min(1, "Key is required"), + value: z.string().optional(), + environments: z.record(z.boolean().optional()) + }) + .refine((data) => data.key !== undefined, { + message: "Please enter secret name" + }); + +type TFormSchema = z.infer; + +type Props = { + secretPath?: string; + decryptFileKey: UserWsKeyPair; + getSecretByKey: (slug: string, key: string) => DecryptedSecret | undefined; + // modal props + isOpen?: boolean; + onClose: () => void; + onTogglePopUp: (isOpen: boolean) => void; +}; + +export const CreateSecretForm = ({ + secretPath = "/", + decryptFileKey, + isOpen, + getSecretByKey, + onClose, + onTogglePopUp +}: Props) => { + const { + register, + handleSubmit, + control, + reset, + watch, + formState: { isSubmitting, errors } + } = useForm({ resolver: zodResolver(typeSchema) }); + const newSecretKey = watch("key"); + + const { currentWorkspace } = useWorkspace(); + const workspaceId = currentWorkspace?.id || ""; + const environments = currentWorkspace?.environments || []; + + const { createNotification } = useNotificationContext(); + + const { mutateAsync: createSecretV3 } = useCreateSecretV3(); + const { mutateAsync: updateSecretV3 } = useUpdateSecretV3(); + const { mutateAsync: createFolder } = useCreateFolder(); + + const handleFormSubmit = async ({ key, value, environments: selectedEnv }: TFormSchema) => { + const environmentsSelected = environments.filter(({ slug }) => selectedEnv[slug]); + const isEnvironmentsSelected = environmentsSelected.length; + + if (!isEnvironmentsSelected) { + createNotification({ type: "error", text: "Select atleast one environment" }); + return; + } + + const promises = environmentsSelected.map(async (env) => { + const environment = env.slug; + // create folder if not existing + if (secretPath !== "/") { + // /hello/world -> [hello","world"] + const pathSegment = secretPath.split("/").filter(Boolean); + const parentPath = `/${pathSegment.slice(0, -1).join("/")}`; + const folderName = pathSegment.at(-1); + if (folderName && parentPath) { + await createFolder({ + projectId: workspaceId, + path: parentPath, + environment, + name: folderName + }); + } + } + + const isEdit = getSecretByKey(environment, key) !== undefined; + if (isEdit) { + return updateSecretV3({ + environment, + workspaceId, + secretPath, + secretName: key, + secretValue: value || "", + type: "shared", + latestFileKey: decryptFileKey + }); + } + + return createSecretV3({ + environment, + workspaceId, + secretPath, + secretName: key, + secretValue: value || "", + secretComment: "", + type: "shared", + latestFileKey: decryptFileKey + }); + }); + + const results = await Promise.allSettled(promises); + const isSecretsAdded = results.some((result) => result.status === "fulfilled"); + + if (isSecretsAdded) { + createNotification({ + type: "success", + text: "Secrets created successfully" + }); + onClose(); + reset(); + } else { + createNotification({ + type: "error", + text: "Failed to create secrets" + }); + } + }; + return ( + + +
+ + + + ( + + + + )} + /> + +
+ {environments.map((env) => { + return ( + ( + + {env.name} + {getSecretByKey(env.slug, newSecretKey) && ( + + + + )} + + )} + /> + ); + })} +
+
+ + +
+ +
+
+ ); +}; diff --git a/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/index.tsx b/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/index.tsx new file mode 100644 index 000000000..ffbe98f57 --- /dev/null +++ b/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/index.tsx @@ -0,0 +1 @@ +export { CreateSecretForm } from "./CreateSecretForm" diff --git a/frontend/src/views/SecretOverviewPage/components/CreateSecretsSection/CreateSecretInAllEnvsForm.tsx b/frontend/src/views/SecretOverviewPage/components/CreateSecretsSection/CreateSecretInAllEnvsForm.tsx deleted file mode 100644 index bc716edff..000000000 --- a/frontend/src/views/SecretOverviewPage/components/CreateSecretsSection/CreateSecretInAllEnvsForm.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { Controller, useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; - -import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; -import { Button, FormControl, Input, Modal, ModalContent, SecretInput } from "@app/components/v2"; -import { useWorkspace } from "@app/context"; -import { useCreateSecretV3 } from "@app/hooks/api"; -import { UserWsKeyPair } from "@app/hooks/api/types"; - -const typeSchema = z - .object({ - key: z.string(), - values: z.record(z.string().optional()) - }) - .refine((data) => data.key !== undefined, { - message: "Please enter secret name" - }); - -type TFormSchema = z.infer; - -type Props = { - workspaceId: string; - secretPath?: string; - decryptFileKey: UserWsKeyPair; - - // modal props - isOpen?: boolean; - onClose: () => void; - onTogglePopUp: (isOpen: boolean) => void; -}; - -export const CreateSecretInAllEnvsForm = ({ - workspaceId, - secretPath = "/", - decryptFileKey, - isOpen, - onClose, - onTogglePopUp -}: Props) => { - const { - register, - handleSubmit, - control, - reset, - setError, - formState: { isSubmitting, errors } - } = useForm({ resolver: zodResolver(typeSchema) }); - const { currentWorkspace } = useWorkspace(); - const environments = currentWorkspace?.environments || []; - - const { createNotification } = useNotificationContext(); - - const { mutateAsync: createSecretV3 } = useCreateSecretV3(); - - const handleFormSubmit = async ({ key, values }: TFormSchema) => { - if (!key) { - setError("key", { message: "Please enter secret name" }); - return; - } - - const promises = environments.map((env) => { - const environment = env.slug; - const value = values[environment] || ""; - - return createSecretV3({ - environment, - workspaceId, - secretPath, - secretName: key, - secretValue: value, - secretComment: "", - type: "shared", - latestFileKey: decryptFileKey - }); - }); - - const results = await Promise.allSettled(promises); - const isSecretsAdded = results.some((result) => result.status === "fulfilled"); - - if (isSecretsAdded) { - createNotification({ - type: "success", - text: "Secrets created successfully" - }); - onClose(); - reset(); - } else { - createNotification({ - type: "error", - text: "Failed to create secrets" - }); - } - }; - return ( - - -
- - - - - {environments.map((env) => { - return ( - ( - - - - )} - /> - ); - })} - -
- - -
- -
-
- ); -};