1
+ import { zodResolver } from "@hookform/resolvers/zod" ;
2
+ import { PlusIcon } from "lucide-react" ;
3
+ import { useState } from "react" ;
4
+ import { useForm } from "react-hook-form" ;
5
+ import { toast } from "sonner" ;
6
+ import { z } from "zod" ;
7
+ import { Button } from "@/components/ui/button" ;
8
+ import {
9
+ Dialog ,
10
+ DialogContent ,
11
+ DialogDescription ,
12
+ DialogHeader ,
13
+ DialogTitle ,
14
+ DialogTrigger ,
15
+ } from "@/components/ui/dialog" ;
1
16
import {
2
- Flex ,
17
+ Form ,
3
18
FormControl ,
4
- Input ,
5
- Modal ,
6
- ModalBody ,
7
- ModalCloseButton ,
8
- ModalContent ,
9
- ModalFooter ,
10
- ModalHeader ,
11
- ModalOverlay ,
12
- Select ,
13
- useDisclosure ,
14
- } from "@chakra-ui/react" ;
15
- import { Button } from "chakra/button" ;
16
- import { FormLabel } from "chakra/form" ;
17
- import { CirclePlusIcon } from "lucide-react" ;
18
- import { useForm } from "react-hook-form" ;
19
+ FormField ,
20
+ FormItem ,
21
+ FormLabel ,
22
+ FormMessage ,
23
+ } from "@/components/ui/form" ;
24
+ import { Input } from "@/components/ui/input" ;
25
+ import { Spinner } from "@/components/ui/Spinner/Spinner" ;
19
26
import {
20
- type CreateWebhookInput ,
21
- useEngineCreateWebhook ,
22
- } from "@/hooks/useEngine" ;
23
- import { useTxNotifications } from "@/hooks/useTxNotifications" ;
27
+ Select ,
28
+ SelectContent ,
29
+ SelectItem ,
30
+ SelectTrigger ,
31
+ SelectValue ,
32
+ } from "@/components/ui/select" ;
33
+ import { useEngineCreateWebhook } from "@/hooks/useEngine" ;
24
34
import { beautifyString } from "./webhooks-table" ;
25
35
26
- interface AddWebhookButtonProps {
27
- instanceUrl : string ;
28
- authToken : string ;
29
- }
30
-
31
36
const WEBHOOK_EVENT_TYPES = [
32
37
"all_transactions" ,
33
38
"sent_transaction" ,
@@ -38,97 +43,156 @@ const WEBHOOK_EVENT_TYPES = [
38
43
"auth" ,
39
44
] ;
40
45
41
- export const AddWebhookButton : React . FC < AddWebhookButtonProps > = ( {
46
+ const webhookFormSchema = z . object ( {
47
+ eventType : z . string ( ) . min ( 1 , "Event type is required" ) ,
48
+ name : z . string ( ) . min ( 1 , "Name is required" ) ,
49
+ url : z . string ( ) . url ( "Please enter a valid URL" ) ,
50
+ } ) ;
51
+
52
+ type WebhookFormValues = z . infer < typeof webhookFormSchema > ;
53
+
54
+ export function AddWebhookButton ( {
42
55
instanceUrl,
43
56
authToken,
44
- } ) => {
45
- const { isOpen, onOpen, onClose } = useDisclosure ( ) ;
46
- const { mutate : createWebhook } = useEngineCreateWebhook ( {
57
+ } : {
58
+ instanceUrl : string ;
59
+ authToken : string ;
60
+ } ) {
61
+ const [ open , setOpen ] = useState ( false ) ;
62
+ const createWebhook = useEngineCreateWebhook ( {
47
63
authToken,
48
64
instanceUrl,
49
65
} ) ;
50
66
51
- const form = useForm < CreateWebhookInput > ( ) ;
67
+ const form = useForm < WebhookFormValues > ( {
68
+ resolver : zodResolver ( webhookFormSchema ) ,
69
+ defaultValues : {
70
+ eventType : "" ,
71
+ name : "" ,
72
+ url : "" ,
73
+ } ,
74
+ mode : "onChange" ,
75
+ } ) ;
52
76
53
- const { onSuccess, onError } = useTxNotifications (
54
- "Webhook created successfully." ,
55
- "Failed to create webhook." ,
56
- ) ;
77
+ const onSubmit = ( data : WebhookFormValues ) => {
78
+ createWebhook . mutate ( data , {
79
+ onError : ( error ) => {
80
+ toast . error ( "Failed to create webhook" , {
81
+ description : error . message ,
82
+ } ) ;
83
+ console . error ( error ) ;
84
+ } ,
85
+ onSuccess : ( ) => {
86
+ toast . success ( "Webhook created successfully" ) ;
87
+ setOpen ( false ) ;
88
+ form . reset ( ) ;
89
+ } ,
90
+ } ) ;
91
+ } ;
57
92
58
93
return (
59
- < >
60
- < Button
61
- colorScheme = "primary"
62
- leftIcon = { < CirclePlusIcon className = "size-6" /> }
63
- onClick = { onOpen }
64
- size = "sm"
65
- variant = "ghost"
66
- w = "fit-content"
67
- >
68
- Create Webhook
69
- </ Button >
94
+ < Dialog open = { open } onOpenChange = { setOpen } >
95
+ < DialogTrigger asChild >
96
+ < Button className = "w-fit gap-2" onClick = { ( ) => setOpen ( true ) } >
97
+ < PlusIcon className = "size-4" />
98
+ Create Webhook
99
+ </ Button >
100
+ </ DialogTrigger >
70
101
71
- < Modal isCentered isOpen = { isOpen } onClose = { onClose } >
72
- < ModalOverlay />
73
- < ModalContent
74
- as = "form"
75
- className = "!bg-background rounded-lg border border-border"
76
- onSubmit = { form . handleSubmit ( ( data ) => {
77
- createWebhook ( data , {
78
- onError : ( error ) => {
79
- onError ( error ) ;
80
- console . error ( error ) ;
81
- } ,
82
- onSuccess : ( ) => {
83
- onSuccess ( ) ;
84
- onClose ( ) ;
85
- } ,
86
- } ) ;
87
- } ) }
88
- >
89
- < ModalHeader > Create Webhook</ ModalHeader >
90
- < ModalCloseButton />
91
- < ModalBody >
92
- < Flex flexDir = "column" gap = { 4 } >
93
- < FormControl isRequired >
94
- < FormLabel > Event Type</ FormLabel >
95
- < Select { ...form . register ( "eventType" , { required : true } ) } >
96
- { WEBHOOK_EVENT_TYPES . map ( ( eventType ) => (
97
- < option key = { eventType } value = { eventType } >
98
- { beautifyString ( eventType ) }
99
- </ option >
100
- ) ) }
101
- </ Select >
102
- </ FormControl >
103
- < FormControl isRequired >
104
- < FormLabel > Name</ FormLabel >
105
- < Input
106
- placeholder = "My webhook"
107
- type = "text"
108
- { ...form . register ( "name" , { required : true } ) }
109
- />
110
- </ FormControl >
111
- < FormControl isRequired >
112
- < FormLabel > URL</ FormLabel >
113
- < Input
114
- placeholder = "https://"
115
- type = "url"
116
- { ...form . register ( "url" , { required : true } ) }
117
- />
118
- </ FormControl >
119
- </ Flex >
120
- </ ModalBody >
102
+ < DialogContent className = "p-0 gap-0 overflow-hidden" >
103
+ < DialogHeader className = "p-4 lg:p-6" >
104
+ < DialogTitle > Create Webhook</ DialogTitle >
105
+ < DialogDescription >
106
+ Create a new webhook to receive notifications for engine events.
107
+ </ DialogDescription >
108
+ </ DialogHeader >
121
109
122
- < ModalFooter as = { Flex } gap = { 3 } >
123
- < Button onClick = { onClose } type = "button" variant = "ghost" >
124
- Cancel
125
- </ Button >
126
- < Button colorScheme = "primary" type = "submit" >
127
- Create
128
- </ Button >
129
- </ ModalFooter >
130
- </ ModalContent >
131
- </ Modal >
132
- </ >
110
+ < Form { ...form } >
111
+ < form onSubmit = { form . handleSubmit ( onSubmit ) } className = "space-y-4" >
112
+ < div className = "space-y-4 px-4 lg:px-6 pb-4" >
113
+ < FormField
114
+ control = { form . control }
115
+ name = "eventType"
116
+ render = { ( { field } ) => (
117
+ < FormItem >
118
+ < FormLabel > Event Type</ FormLabel >
119
+ < Select onValueChange = { field . onChange } value = { field . value } >
120
+ < FormControl >
121
+ < SelectTrigger className = "bg-card" >
122
+ < SelectValue placeholder = "Select event type" />
123
+ </ SelectTrigger >
124
+ </ FormControl >
125
+ < SelectContent >
126
+ { WEBHOOK_EVENT_TYPES . map ( ( eventType ) => (
127
+ < SelectItem key = { eventType } value = { eventType } >
128
+ { beautifyString ( eventType ) }
129
+ </ SelectItem >
130
+ ) ) }
131
+ </ SelectContent >
132
+ </ Select >
133
+ < FormMessage />
134
+ </ FormItem >
135
+ ) }
136
+ />
137
+
138
+ < FormField
139
+ control = { form . control }
140
+ name = "name"
141
+ render = { ( { field } ) => (
142
+ < FormItem >
143
+ < FormLabel > Name</ FormLabel >
144
+ < FormControl >
145
+ < Input
146
+ className = "bg-card"
147
+ placeholder = "My webhook"
148
+ { ...field }
149
+ />
150
+ </ FormControl >
151
+ < FormMessage />
152
+ </ FormItem >
153
+ ) }
154
+ />
155
+
156
+ < FormField
157
+ control = { form . control }
158
+ name = "url"
159
+ render = { ( { field } ) => (
160
+ < FormItem >
161
+ < FormLabel > URL</ FormLabel >
162
+ < FormControl >
163
+ < Input
164
+ className = "bg-card"
165
+ placeholder = "https://"
166
+ type = "url"
167
+ { ...field }
168
+ />
169
+ </ FormControl >
170
+ < FormMessage />
171
+ </ FormItem >
172
+ ) }
173
+ />
174
+ </ div >
175
+
176
+ < div className = "flex justify-end gap-3 p-4 lg:p-6 bg-card border-t border-border" >
177
+ < Button
178
+ type = "button"
179
+ variant = "outline"
180
+ onClick = { ( ) => setOpen ( false ) }
181
+ >
182
+ Cancel
183
+ </ Button >
184
+ < Button
185
+ type = "submit"
186
+ disabled = { createWebhook . isPending }
187
+ className = "gap-2"
188
+ >
189
+ { createWebhook . isPending && < Spinner className = "size-4" /> }
190
+ Create
191
+ </ Button >
192
+ </ div >
193
+ </ form >
194
+ </ Form >
195
+ </ DialogContent >
196
+ </ Dialog >
133
197
) ;
134
- } ;
198
+ }
0 commit comments