diff --git a/apps/studio/components/interfaces/Integrations/Vault/Secrets/EditSecretModal.tsx b/apps/studio/components/interfaces/Integrations/Vault/Secrets/EditSecretModal.tsx index 5c19268657a..25f713ac250 100644 --- a/apps/studio/components/interfaces/Integrations/Vault/Secrets/EditSecretModal.tsx +++ b/apps/studio/components/interfaces/Integrations/Vault/Secrets/EditSecretModal.tsx @@ -1,31 +1,67 @@ import { isEmpty } from 'lodash' -import { useEffect, useState } from 'react' +import { useState, useEffect } from 'react' +import { type SubmitHandler, useForm } from 'react-hook-form' +import { zodResolver } from '@hookform/resolvers/zod' +import { z } from 'zod' import { toast } from 'sonner' - import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { useVaultSecretDecryptedValueQuery } from 'data/vault/vault-secret-decrypted-value-query' import { useVaultSecretUpdateMutation } from 'data/vault/vault-secret-update-mutation' import type { VaultSecret } from 'types' -import { Button, Form, Input, Modal } from 'ui' +import { + Button, + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogSection, + DialogSectionSeparator, + DialogTitle, + Form_Shadcn_, + FormControl_Shadcn_, + FormField_Shadcn_, + Input_Shadcn_, +} from 'ui' import { EyeOff, Eye } from 'lucide-react' +import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' interface EditSecretModalProps { selectedSecret: VaultSecret | undefined onClose: () => void } +const SecretSchema = z.object({ + name: z.string().min(1, 'Please provide a name for your secret'), + description: z.string().optional(), + secret: z.string().min(1, 'Please enter your secret value'), +}) + const EditSecretModal = ({ selectedSecret, onClose }: EditSecretModalProps) => { const [showSecretValue, setShowSecretValue] = useState(false) const { project } = useProjectContext() - - const { mutateAsync: updateSecret } = useVaultSecretUpdateMutation() - - let INITIAL_VALUES = { - name: selectedSecret?.name ?? '', - description: selectedSecret?.description ?? '', - secret: selectedSecret?.decryptedSecret ?? '', - } + const formId = 'edit-vault-secret-form' + const { data, isLoading: isLoadingSecretValue } = useVaultSecretDecryptedValueQuery( + { + projectRef: project?.ref!, + id: selectedSecret?.id!, + connectionString: project?.connectionString, + }, + { enabled: selectedSecret !== undefined && !!(project?.ref && selectedSecret?.id) } + ) + const form = useForm>({ + resolver: zodResolver(SecretSchema), + defaultValues: { + name: selectedSecret?.name ?? '', + description: selectedSecret?.description ?? '', + secret: selectedSecret?.decryptedSecret ?? data ?? '', + }, + values: { + name: selectedSecret?.name ?? '', + description: selectedSecret?.description ?? '', + secret: selectedSecret?.decryptedSecret ?? data ?? '', + }, + }) useEffect(() => { if (selectedSecret !== undefined) { @@ -33,126 +69,146 @@ const EditSecretModal = ({ selectedSecret, onClose }: EditSecretModalProps) => { } }, [selectedSecret]) - const validate = (values: any) => { - const errors: any = {} - if (values.name.length === 0) errors.name = 'Please provide a name for your secret' - if (values.secret.length === 0) errors.secret = 'Please enter your secret value' - return errors - } + const { mutateAsync: updateSecret, isLoading: isSubmitting } = useVaultSecretUpdateMutation() - const onUpdateSecret = async (values: any, { setSubmitting }: any) => { + const onSubmit: SubmitHandler> = async (values) => { if (!project) return console.error('Project is required') - try { - const payload: Partial = {} - if (values.name !== selectedSecret?.name) payload.name = values.name - if (values.description !== selectedSecret?.description) - payload.description = values.description - payload.secret = values.secret + const payload: Partial = { + secret: values.secret, + } + if (values.name !== selectedSecret?.name) payload.name = values.name + if (values.description !== selectedSecret?.description) payload.description = values.description - if (!isEmpty(payload) && selectedSecret) { - setSubmitting(true) - const res = await updateSecret({ + if (!isEmpty(payload) && selectedSecret) { + updateSecret( + { projectRef: project.ref, connectionString: project?.connectionString, id: selectedSecret.id, ...payload, - }) - if (!res.error) { - toast.success('Successfully updated secret') - setSubmitting(false) - onClose() - } else { - toast.error(`Failed to update secret: ${res.error.message}`) - setSubmitting(false) + }, + { + onSuccess: () => { + toast.success('Successfully updated secret') + onClose() + }, + onError: (error) => { + toast.error(`Failed to update secret: ${error.message}`) + }, } - } - } finally { + ) } } return ( - Edit secret} + { + if (!open) { + form.reset() + onClose() + } + }} > -
- {({ isSubmitting, resetForm }: any) => { - const { - data, - isLoading: isLoadingSecretValue, - isSuccess: isSuccessSecretValue, - // [Joshen] JFYI this is breaking rules of hooks, will be fixed once we move to - // using react hook form instead - // eslint-disable-next-line react-hooks/rules-of-hooks - } = useVaultSecretDecryptedValueQuery( - { - projectRef: project?.ref!, - id: selectedSecret?.id!, - connectionString: project?.connectionString, - }, - { enabled: selectedSecret !== undefined && !!(project?.ref && selectedSecret?.id) } - ) - - // [Joshen] JFYI this is breaking rules of hooks, will be fixed once we move to - // using react hook form instead - // eslint-disable-next-line react-hooks/rules-of-hooks - useEffect(() => { - if (selectedSecret !== undefined && isSuccessSecretValue) { - resetForm({ - values: { ...INITIAL_VALUES, secret: data }, - initialValues: { ...INITIAL_VALUES, secret: data }, - }) - } - }, [selectedSecret, isSuccessSecretValue]) - - return isLoadingSecretValue ? ( - - - - ) : ( - <> - - - - - - - - - ) - }} -
-
+ + + Edit secret + + + {isLoadingSecretValue ? ( + + + + ) : ( + <> + + +
+ ( + + + + + + )} + /> + ( + + + + + + )} + /> + ( + + +
+ +
+
+
+ )} + /> + +
+
+ + + + + + )} +
+ ) } diff --git a/apps/studio/components/interfaces/Integrations/Vault/Secrets/SecretRow.tsx b/apps/studio/components/interfaces/Integrations/Vault/Secrets/SecretRow.tsx index f62dac4d194..0a79deb18af 100644 --- a/apps/studio/components/interfaces/Integrations/Vault/Secrets/SecretRow.tsx +++ b/apps/studio/components/interfaces/Integrations/Vault/Secrets/SecretRow.tsx @@ -97,7 +97,7 @@ const SecretRow = ({ secret, onSelectEdit, onSelectRemove }: SecretRowProps) =>

-