SendMessageModal.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import { zodResolver } from '@hookform/resolvers/zod'
  2. import { useParams } from 'common'
  3. import { useEffect } from 'react'
  4. import { SubmitHandler, useForm } from 'react-hook-form'
  5. import { toast } from 'sonner'
  6. import {
  7. Form,
  8. FormControl,
  9. FormField,
  10. InputGroup,
  11. InputGroupAddon,
  12. InputGroupInput,
  13. InputGroupText,
  14. Modal,
  15. } from 'ui'
  16. import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
  17. import z from 'zod'
  18. import CodeEditor from '@/components/ui/CodeEditor/CodeEditor'
  19. import { useDatabaseQueueMessageSendMutation } from '@/data/database-queues/database-queue-messages-send-mutation'
  20. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  21. interface SendMessageModalProps {
  22. visible: boolean
  23. onClose: () => void
  24. }
  25. const FormSchema = z.object({
  26. delay: z.coerce.number().int().gte(0).default(5),
  27. payload: z.string().refine(
  28. (val) => {
  29. try {
  30. JSON.parse(val)
  31. } catch {
  32. return false
  33. }
  34. },
  35. {
  36. message: 'The payload should be a JSON object',
  37. }
  38. ),
  39. })
  40. export type SendMessageForm = z.infer<typeof FormSchema>
  41. const FORM_ID = 'QUEUES_SEND_MESSAGE_FORM'
  42. export const SendMessageModal = ({ visible, onClose }: SendMessageModalProps) => {
  43. const { childId: queueName } = useParams()
  44. const { data: project } = useSelectedProjectQuery()
  45. const form = useForm<SendMessageForm>({
  46. resolver: zodResolver(FormSchema as any),
  47. defaultValues: {
  48. delay: 1,
  49. payload: '{}',
  50. },
  51. })
  52. const { isPending, mutate } = useDatabaseQueueMessageSendMutation({
  53. onSuccess: () => {
  54. toast.success(`Successfully added a message to the queue.`)
  55. onClose()
  56. },
  57. })
  58. const onSubmit: SubmitHandler<SendMessageForm> = (values) => {
  59. mutate({
  60. projectRef: project?.ref!,
  61. connectionString: project?.connectionString,
  62. queueName: queueName!,
  63. payload: values.payload,
  64. delay: values.delay,
  65. })
  66. }
  67. useEffect(() => {
  68. if (visible) {
  69. form.reset({ delay: 1, payload: '{}' })
  70. }
  71. }, [visible])
  72. return (
  73. <Modal
  74. size="medium"
  75. alignFooter="right"
  76. header="Add a message to the queue"
  77. visible={visible}
  78. loading={isPending}
  79. onCancel={onClose}
  80. confirmText="Add"
  81. onConfirm={() => {
  82. const values = form.getValues()
  83. onSubmit(values)
  84. }}
  85. >
  86. <Modal.Content className="flex flex-col gap-y-4">
  87. <Form {...form}>
  88. <form
  89. id={FORM_ID}
  90. className="grow overflow-auto gap-2 flex flex-col"
  91. onSubmit={form.handleSubmit(onSubmit)}
  92. >
  93. <FormField
  94. control={form.control}
  95. name="delay"
  96. render={({ field: { ref, ...rest } }) => (
  97. <FormItemLayout
  98. label="Delay"
  99. layout="vertical"
  100. className="gap-1"
  101. description="Time in seconds before the message becomes available for reading."
  102. >
  103. <FormControl>
  104. <InputGroup>
  105. <InputGroupInput {...rest} type="number" placeholder="1" />
  106. <InputGroupAddon align="inline-end">
  107. <InputGroupText>sec</InputGroupText>
  108. </InputGroupAddon>
  109. </InputGroup>
  110. </FormControl>
  111. </FormItemLayout>
  112. )}
  113. />
  114. <FormField
  115. control={form.control}
  116. name="payload"
  117. render={({ field }) => (
  118. <FormItemLayout label="Message payload" layout="vertical" className="gap-1">
  119. <FormControl>
  120. <CodeEditor
  121. id="message-payload"
  122. language="json"
  123. autofocus={false}
  124. className="mb-0! h-32 overflow-hidden rounded-sm border"
  125. onInputChange={(e: string | undefined) => field.onChange(e)}
  126. options={{ wordWrap: 'off', contextmenu: false }}
  127. value={field.value}
  128. />
  129. </FormControl>
  130. </FormItemLayout>
  131. )}
  132. />
  133. </form>
  134. </Form>
  135. </Modal.Content>
  136. </Modal>
  137. )
  138. }