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" ;
116import {
2- Flex ,
17+ Form ,
318 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" ;
1926import {
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" ;
2434import { beautifyString } from "./webhooks-table" ;
2535
26- interface AddWebhookButtonProps {
27- instanceUrl : string ;
28- authToken : string ;
29- }
30-
3136const WEBHOOK_EVENT_TYPES = [
3237 "all_transactions" ,
3338 "sent_transaction" ,
@@ -38,97 +43,156 @@ const WEBHOOK_EVENT_TYPES = [
3843 "auth" ,
3944] ;
4045
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 ( {
4255 instanceUrl,
4356 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 ( {
4763 authToken,
4864 instanceUrl,
4965 } ) ;
5066
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+ } ) ;
5276
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+ } ;
5792
5893 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 >
70101
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 >
121109
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 >
133197 ) ;
134- } ;
198+ }
0 commit comments