diff --git a/frontend/src/helpers/parseEnvVar.ts b/frontend/src/helpers/parseEnvVar.ts index 27640b515c..8bde050844 100644 --- a/frontend/src/helpers/parseEnvVar.ts +++ b/frontend/src/helpers/parseEnvVar.ts @@ -1,14 +1,31 @@ /** Extracts the key and value from a passed in env string based on the provided delimiters. */ export const getKeyValue = (pastedContent: string, delimiters: string[]) => { - const foundDelimiter = delimiters.find((delimiter) => pastedContent.includes(delimiter)); + if (!pastedContent) { + return { key: "", value: "" }; + } + + let firstDelimiterIndex = -1; + let foundDelimiter = ""; + + delimiters.forEach((delimiter) => { + const index = pastedContent.indexOf(delimiter); + if (index !== -1 && (firstDelimiterIndex === -1 || index < firstDelimiterIndex)) { + firstDelimiterIndex = index; + foundDelimiter = delimiter; + } + }); - if (!foundDelimiter) { + const hasValueAfterDelimiter = pastedContent.length > firstDelimiterIndex + foundDelimiter.length; + + if (firstDelimiterIndex === -1 || !hasValueAfterDelimiter) { return { key: pastedContent.trim(), value: "" }; } - const [key, value] = pastedContent.split(foundDelimiter); + const key = pastedContent.substring(0, firstDelimiterIndex); + const value = pastedContent.substring(firstDelimiterIndex + foundDelimiter.length); + return { key: key.trim(), - value: (value ?? "").trim() + value: value.trim() }; }; diff --git a/frontend/src/views/SecretMainPage/components/CreateSecretForm/CreateSecretForm.tsx b/frontend/src/views/SecretMainPage/components/CreateSecretForm/CreateSecretForm.tsx index 53b00f34a5..db53d26ae3 100644 --- a/frontend/src/views/SecretMainPage/components/CreateSecretForm/CreateSecretForm.tsx +++ b/frontend/src/views/SecretMainPage/components/CreateSecretForm/CreateSecretForm.tsx @@ -1,4 +1,4 @@ -import { ClipboardEvent } from "react"; +import { ClipboardEvent, useRef } from "react"; import { Controller, useForm } from "react-hook-form"; import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -46,6 +46,7 @@ export const CreateSecretForm = ({ control, reset, setValue, + watch, formState: { errors, isSubmitting } } = useForm({ resolver: zodResolver(typeSchema) }); const { closePopUp } = usePopUpAction(); @@ -59,6 +60,11 @@ export const CreateSecretForm = ({ canReadTags ? workspaceId : "" ); + const secretKeyInputRef = useRef(null); + const { ref: setSecretKeyHookRef, ...secretKeyRegisterRest } = register("key"); + + const secretKey = watch("key"); + const slugSchema = z.string().trim().toLowerCase().min(1); const createNewTag = async (slug: string) => { // TODO: Replace with slugSchema generic @@ -108,13 +114,23 @@ export const CreateSecretForm = ({ }; const handlePaste = (e: ClipboardEvent) => { - e.preventDefault(); const delimitters = [":", "="]; const pastedContent = e.clipboardData.getData("text"); const { key, value } = getKeyValue(pastedContent, delimitters); - setValue("key", key); - setValue("value", value); + const isWholeKeyHighlighted = + secretKeyInputRef.current && + secretKeyInputRef.current.selectionStart === 0 && + secretKeyInputRef.current.selectionEnd === secretKeyInputRef.current.value.length; + + if (!secretKey || isWholeKeyHighlighted) { + e.preventDefault(); + + setValue("key", key); + if (value) { + setValue("value", value); + } + } }; return ( @@ -126,7 +142,12 @@ export const CreateSecretForm = ({ errorText={errors?.key?.message} > { + setSecretKeyHookRef(e); + // @ts-expect-error this is for multiple ref single component + secretKeyInputRef.current = e; + }} placeholder="Type your secret name" onPaste={handlePaste} autoCapitalization={autoCapitalize} diff --git a/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx b/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx index 36e430205b..aabd7899e2 100644 --- a/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx +++ b/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx @@ -1,4 +1,4 @@ -import { ClipboardEvent } from "react"; +import { ClipboardEvent, useRef } from "react"; import { Controller, useForm } from "react-hook-form"; import { subject } from "@casl/ability"; import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons"; @@ -46,6 +46,7 @@ export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => { control, reset, setValue, + watch, formState: { isSubmitting, errors } } = useForm({ resolver: zodResolver(typeSchema) }); @@ -61,6 +62,11 @@ export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => { canReadTags ? workspaceId : "" ); + const secretKeyInputRef = useRef(null); + const { ref: setSecretKeyHookRef, ...secretKeyRegisterRest } = register("key"); + + const secretKey = watch("key"); + const handleFormSubmit = async ({ key, value, environments: selectedEnv, tags }: TFormSchema) => { const promises = selectedEnv.map(async (env) => { const environment = env.slug; @@ -152,13 +158,23 @@ export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => { }; const handlePaste = (e: ClipboardEvent) => { - e.preventDefault(); const delimitters = [":", "="]; const pastedContent = e.clipboardData.getData("text"); const { key, value } = getKeyValue(pastedContent, delimitters); - setValue("key", key); - setValue("value", value); + const isWholeKeyHighlighted = + secretKeyInputRef.current && + secretKeyInputRef.current.selectionStart === 0 && + secretKeyInputRef.current.selectionEnd === secretKeyInputRef.current.value.length; + + if (!secretKey || isWholeKeyHighlighted) { + e.preventDefault(); + + setValue("key", key); + if (value) { + setValue("value", value); + } + } }; const createWsTag = useCreateWsTag(); @@ -189,7 +205,12 @@ export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => { errorText={errors?.key?.message} > { + setSecretKeyHookRef(e); + // @ts-expect-error this is for multiple ref single component + secretKeyInputRef.current = e; + }} placeholder="Type your secret name" onPaste={handlePaste} autoCapitalization={currentWorkspace?.autoCapitalization}