AddNewSecretModal.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import { zodResolver } from '@hookform/resolvers/zod'
  2. import { parseAsBoolean, useQueryState } from 'nuqs'
  3. import { SubmitHandler, useForm } from 'react-hook-form'
  4. import { toast } from 'sonner'
  5. import {
  6. Button,
  7. Dialog,
  8. DialogContent,
  9. DialogFooter,
  10. DialogHeader,
  11. DialogSection,
  12. DialogSectionSeparator,
  13. DialogTitle,
  14. Form,
  15. FormControl,
  16. FormField,
  17. Input,
  18. } from 'ui'
  19. import { Input as PasswordInput } from 'ui-patterns/DataInputs/Input'
  20. import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
  21. import * as z from 'zod'
  22. import { useVaultSecretCreateMutation } from '@/data/vault/vault-secret-create-mutation'
  23. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  24. const formSchema = z.object({
  25. name: z.string().min(1, 'Please provide a name for your secret'),
  26. description: z.string().optional(),
  27. secret: z.string().min(1, 'Please enter your secret value'),
  28. })
  29. type FormSchema = z.infer<typeof formSchema>
  30. const formId = 'add-new-secret-form'
  31. export const AddNewSecretModal = () => {
  32. const { data: project } = useSelectedProjectQuery()
  33. const { mutateAsync: addSecret } = useVaultSecretCreateMutation()
  34. const [showAddSecretModal, setShowAddSecretModal] = useQueryState(
  35. 'new',
  36. parseAsBoolean.withDefault(false)
  37. )
  38. const handleClose = () => {
  39. setShowAddSecretModal(null)
  40. form.reset()
  41. }
  42. const onAddNewSecret: SubmitHandler<FormSchema> = async (values) => {
  43. if (!project) return console.error('Project is required')
  44. try {
  45. await addSecret({
  46. projectRef: project.ref,
  47. connectionString: project?.connectionString,
  48. name: values.name,
  49. description: values.description,
  50. secret: values.secret,
  51. })
  52. toast.success(`Successfully added new secret ${values.name}`)
  53. handleClose()
  54. } catch (error: any) {
  55. // [Joshen] No error handler required as they are all handled within the mutations already
  56. } finally {
  57. }
  58. }
  59. const form = useForm<FormSchema>({
  60. resolver: zodResolver(formSchema as any),
  61. defaultValues: { name: '', description: '', secret: '' },
  62. })
  63. const { isDirty, isSubmitting } = form.formState
  64. return (
  65. <Dialog open={showAddSecretModal} onOpenChange={handleClose}>
  66. <DialogContent className="sm:max-w-[425px]">
  67. <DialogHeader>
  68. <DialogTitle>Add new secret</DialogTitle>
  69. </DialogHeader>
  70. <DialogSectionSeparator />
  71. <DialogSection className="space-y-4">
  72. <Form {...form}>
  73. <form
  74. id={formId}
  75. noValidate
  76. onSubmit={form.handleSubmit(onAddNewSecret)}
  77. className="space-y-4"
  78. >
  79. <FormField
  80. control={form.control}
  81. name="name"
  82. render={({ field }) => (
  83. <FormItemLayout layout="vertical" label="Name">
  84. <FormControl className="col-span-6">
  85. <Input {...field} />
  86. </FormControl>
  87. </FormItemLayout>
  88. )}
  89. />
  90. <FormField
  91. control={form.control}
  92. name="description"
  93. render={({ field }) => (
  94. <FormItemLayout layout="vertical" label="Description" labelOptional="Optional">
  95. <FormControl className="col-span-6">
  96. <Input {...field} />
  97. </FormControl>
  98. </FormItemLayout>
  99. )}
  100. />
  101. <FormField
  102. control={form.control}
  103. name="secret"
  104. render={({ field }) => (
  105. <FormItemLayout layout="vertical" label="Secret value">
  106. <FormControl className="col-span-6">
  107. <PasswordInput reveal copy {...field} />
  108. </FormControl>
  109. </FormItemLayout>
  110. )}
  111. />
  112. </form>
  113. </Form>
  114. </DialogSection>
  115. <DialogFooter>
  116. <Button type="default" disabled={isSubmitting} onClick={handleClose}>
  117. Cancel
  118. </Button>
  119. <Button
  120. form={formId}
  121. htmlType="submit"
  122. disabled={!isDirty || isSubmitting}
  123. loading={isSubmitting}
  124. >
  125. Add secret
  126. </Button>
  127. </DialogFooter>
  128. </DialogContent>
  129. </Dialog>
  130. )
  131. }