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