Skip to content

Commit a252ceb

Browse files
committed
Dashboard: Migrate engine/admins page from chakra to tailwind
1 parent 0d5cf96 commit a252ceb

File tree

3 files changed

+350
-260
lines changed

3 files changed

+350
-260
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,155 @@
1-
import {
2-
Flex,
3-
FormControl,
4-
Input,
5-
Modal,
6-
ModalBody,
7-
ModalCloseButton,
8-
ModalContent,
9-
ModalFooter,
10-
ModalHeader,
11-
ModalOverlay,
12-
useDisclosure,
13-
} from "@chakra-ui/react";
14-
import { Button } from "chakra/button";
15-
import { FormLabel } from "chakra/form";
16-
import { CirclePlusIcon } from "lucide-react";
1+
import { zodResolver } from "@hookform/resolvers/zod";
2+
import { PlusIcon } from "lucide-react";
3+
import { useState } from "react";
174
import { useForm } from "react-hook-form";
5+
import { toast } from "sonner";
186
import { isAddress } from "thirdweb";
19-
import { type EngineAdmin, useEngineGrantPermissions } from "@/hooks/useEngine";
20-
import { useTxNotifications } from "@/hooks/useTxNotifications";
7+
import { z } from "zod";
8+
import { Button } from "@/components/ui/button";
9+
import {
10+
Dialog,
11+
DialogContent,
12+
DialogDescription,
13+
DialogHeader,
14+
DialogTitle,
15+
DialogTrigger,
16+
} from "@/components/ui/dialog";
17+
import {
18+
Form,
19+
FormControl,
20+
FormField,
21+
FormItem,
22+
FormLabel,
23+
FormMessage,
24+
} from "@/components/ui/form";
25+
import { Input } from "@/components/ui/input";
26+
import { Spinner } from "@/components/ui/Spinner/Spinner";
27+
import { useEngineGrantPermissions } from "@/hooks/useEngine";
28+
import { parseError } from "@/utils/errorParser";
2129

22-
interface AddAdminButtonProps {
23-
instanceUrl: string;
24-
authToken: string;
25-
}
30+
const addAdminSchema = z.object({
31+
walletAddress: z
32+
.string()
33+
.refine((address) => isAddress(address), "Invalid address"),
34+
label: z.string().optional(),
35+
});
2636

27-
export const AddAdminButton: React.FC<AddAdminButtonProps> = ({
37+
type AddAdminFormData = z.infer<typeof addAdminSchema>;
38+
39+
export function AddAdminButton({
2840
instanceUrl,
2941
authToken,
30-
}) => {
31-
const { isOpen, onOpen, onClose } = useDisclosure();
32-
const { mutate: grantPermissions } = useEngineGrantPermissions({
42+
}: {
43+
instanceUrl: string;
44+
authToken: string;
45+
}) {
46+
const [open, setOpen] = useState(false);
47+
const grantPermissionsMutation = useEngineGrantPermissions({
3348
authToken,
3449
instanceUrl,
3550
});
3651

37-
const form = useForm<EngineAdmin>({
38-
defaultValues: {
39-
permissions: "ADMIN",
40-
},
52+
const form = useForm<AddAdminFormData>({
53+
resolver: zodResolver(addAdminSchema),
54+
defaultValues: {},
4155
});
4256

43-
const { onSuccess, onError } = useTxNotifications(
44-
"Successfully added admin.",
45-
"Failed to add admin.",
46-
);
57+
const onSubmit = (data: AddAdminFormData) => {
58+
grantPermissionsMutation.mutate(
59+
{
60+
...data,
61+
permissions: "ADMIN",
62+
},
63+
{
64+
onError: (error) => {
65+
toast.error("Failed to add admin.", {
66+
description: parseError(error),
67+
});
68+
console.error(error);
69+
},
70+
onSuccess: () => {
71+
toast.success("Admin added successfully.");
72+
setOpen(false);
73+
form.reset();
74+
},
75+
},
76+
);
77+
};
4778

4879
return (
49-
<>
50-
<Button
51-
colorScheme="primary"
52-
leftIcon={<CirclePlusIcon className="size-6" />}
53-
onClick={onOpen}
54-
size="sm"
55-
variant="ghost"
56-
w="fit-content"
57-
>
58-
Add Admin
59-
</Button>
60-
<Modal isCentered isOpen={isOpen} onClose={onClose}>
61-
<ModalOverlay />
62-
<ModalContent
63-
as="form"
64-
className="!bg-background rounded-lg border border-border"
65-
onSubmit={form.handleSubmit((data) => {
66-
if (!isAddress(data.walletAddress)) {
67-
onError(new Error("Invalid wallet address"));
68-
}
69-
grantPermissions(data, {
70-
onError: (error) => {
71-
onError(error);
72-
console.error(error);
73-
},
74-
onSuccess: () => {
75-
onSuccess();
76-
onClose();
77-
},
78-
});
79-
})}
80-
>
81-
<ModalHeader>Add Admin</ModalHeader>
82-
<ModalCloseButton />
83-
<ModalBody>
84-
<div className="flex flex-col gap-4">
85-
<FormControl isRequired>
86-
<FormLabel>Wallet Address</FormLabel>
87-
<Input
88-
placeholder="The wallet address for this admin"
89-
type="text"
90-
{...form.register("walletAddress", { required: true })}
91-
/>
92-
</FormControl>
93-
<FormControl>
94-
<FormLabel>Label</FormLabel>
95-
<Input
96-
placeholder="Enter a description for this admin"
97-
type="text"
98-
{...form.register("label")}
99-
/>
100-
</FormControl>
101-
</div>
102-
</ModalBody>
80+
<Dialog open={open} onOpenChange={setOpen}>
81+
<DialogTrigger asChild>
82+
<Button className="w-fit gap-2">
83+
<PlusIcon className="size-4" />
84+
Add Admin
85+
</Button>
86+
</DialogTrigger>
87+
<DialogContent className="p-0 overflow-hidden gap-0">
88+
<DialogHeader className="p-4 lg:p-6">
89+
<DialogTitle>Add Admin</DialogTitle>
90+
<DialogDescription>
91+
Add a new admin to your engine instance.
92+
</DialogDescription>
93+
</DialogHeader>
10394

104-
<ModalFooter as={Flex} gap={3}>
105-
<Button onClick={onClose} type="button" variant="ghost">
106-
Cancel
107-
</Button>
108-
<Button colorScheme="primary" type="submit">
109-
Add
110-
</Button>
111-
</ModalFooter>
112-
</ModalContent>
113-
</Modal>
114-
</>
95+
<Form {...form}>
96+
<form onSubmit={form.handleSubmit(onSubmit)}>
97+
<div className="space-y-4 px-4 lg:px-6 pb-8">
98+
{/* wallet address */}
99+
<FormField
100+
control={form.control}
101+
name="walletAddress"
102+
render={({ field }) => (
103+
<FormItem>
104+
<FormLabel>Wallet Address</FormLabel>
105+
<FormControl>
106+
<Input
107+
className="bg-card"
108+
placeholder="0x..."
109+
{...field}
110+
/>
111+
</FormControl>
112+
<FormMessage />
113+
</FormItem>
114+
)}
115+
/>
116+
{/* label */}
117+
<FormField
118+
control={form.control}
119+
name="label"
120+
render={({ field }) => (
121+
<FormItem>
122+
<FormLabel>Label</FormLabel>
123+
<FormControl>
124+
<Input
125+
className="bg-card"
126+
placeholder="label for this admin"
127+
{...field}
128+
/>
129+
</FormControl>
130+
<FormMessage />
131+
</FormItem>
132+
)}
133+
/>
134+
</div>
135+
<div className="flex justify-end gap-3 p-4 lg:p-6 bg-card border-t">
136+
<Button
137+
type="button"
138+
variant="outline"
139+
onClick={() => setOpen(false)}
140+
>
141+
Cancel
142+
</Button>
143+
<Button type="submit" className="gap-2">
144+
{grantPermissionsMutation.isPending && (
145+
<Spinner className="size-4" />
146+
)}
147+
Add Admin
148+
</Button>
149+
</div>
150+
</form>
151+
</Form>
152+
</DialogContent>
153+
</Dialog>
115154
);
116-
};
155+
}

0 commit comments

Comments
 (0)