Skip to content

Commit 7ecde66

Browse files
committed
Dashboard: Migrate engine/webhooks page from chakra to tailwind
1 parent 2996121 commit 7ecde66

File tree

4 files changed

+398
-275
lines changed

4 files changed

+398
-275
lines changed

apps/dashboard/src/@/hooks/useEngine.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,7 @@ export function useEngineUpdateAccessToken(params: {
12231223
});
12241224
}
12251225

1226-
export type CreateWebhookInput = {
1226+
type CreateWebhookInput = {
12271227
url: string;
12281228
name: string;
12291229
eventType: string;
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,38 @@
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";
116
import {
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";
1926
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";
2434
import { beautifyString } from "./webhooks-table";
2535

26-
interface AddWebhookButtonProps {
27-
instanceUrl: string;
28-
authToken: string;
29-
}
30-
3136
const 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+
}
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,49 @@
11
"use client";
22

3-
import { Heading } from "chakra/heading";
4-
import { Link } from "chakra/link";
5-
import { Text } from "chakra/text";
3+
import { UnderlineLink } from "@/components/ui/UnderlineLink";
64
import { useEngineWebhooks } from "@/hooks/useEngine";
75
import { AddWebhookButton } from "./add-webhook-button";
86
import { WebhooksTable } from "./webhooks-table";
97

10-
interface EngineWebhooksProps {
11-
instanceUrl: string;
12-
authToken: string;
13-
}
14-
15-
export const EngineWebhooks: React.FC<EngineWebhooksProps> = ({
8+
export function EngineWebhooks({
169
instanceUrl,
1710
authToken,
18-
}) => {
11+
}: {
12+
instanceUrl: string;
13+
authToken: string;
14+
}) {
1915
const webhooks = useEngineWebhooks({
2016
authToken,
2117
instanceUrl,
2218
});
2319

2420
return (
25-
<div className="flex flex-col gap-4">
26-
<div className="flex flex-col gap-2">
27-
<Heading size="title.md">Webhooks</Heading>
28-
<Text>
29-
Notify your app backend when transaction and backend wallet events
30-
occur.{" "}
31-
<Link
32-
color="primary.500"
33-
href="https://portal.thirdweb.com/infrastructure/engine/features/webhooks"
34-
isExternal
35-
>
36-
Learn more about webhooks
37-
</Link>
38-
.
39-
</Text>
40-
</div>
21+
<div>
22+
<h2 className="text-2xl tracking-tight font-semibold mb-1">Webhooks</h2>
23+
<p className="text-muted-foreground mb-4 text-sm">
24+
Notify your app backend when transaction and backend wallet events
25+
occur.{" "}
26+
<UnderlineLink
27+
href="https://portal.thirdweb.com/infrastructure/engine/features/webhooks"
28+
rel="noopener noreferrer"
29+
target="_blank"
30+
>
31+
Learn more about webhooks
32+
</UnderlineLink>
33+
.
34+
</p>
35+
4136
<WebhooksTable
4237
authToken={authToken}
4338
instanceUrl={instanceUrl}
4439
isFetched={webhooks.isFetched}
4540
isPending={webhooks.isPending}
4641
webhooks={webhooks.data || []}
4742
/>
48-
<AddWebhookButton authToken={authToken} instanceUrl={instanceUrl} />
43+
44+
<div className="mt-4 flex justify-end">
45+
<AddWebhookButton authToken={authToken} instanceUrl={instanceUrl} />
46+
</div>
4947
</div>
5048
);
51-
};
49+
}

0 commit comments

Comments
 (0)