1
1
"use client" ;
2
2
3
- import { FormControl , Textarea } from "@chakra-ui/react" ;
4
- import { Button } from "chakra/button" ;
5
- import { FormErrorMessage , FormLabel } from "chakra/form" ;
6
3
import { PlusIcon } from "lucide-react" ;
7
4
import { useState } from "react" ;
8
5
import { useForm } from "react-hook-form" ;
@@ -11,102 +8,123 @@ import type { ThirdwebContract } from "thirdweb";
11
8
import * as VoteExt from "thirdweb/extensions/vote" ;
12
9
import { useSendAndConfirmTransaction } from "thirdweb/react" ;
13
10
import { TransactionButton } from "@/components/tx-button" ;
11
+ import { Button } from "@/components/ui/button" ;
12
+ import {
13
+ Form ,
14
+ FormControl ,
15
+ FormField ,
16
+ FormItem ,
17
+ FormLabel ,
18
+ FormMessage ,
19
+ } from "@/components/ui/form" ;
14
20
import {
15
21
Sheet ,
16
22
SheetContent ,
17
23
SheetHeader ,
18
24
SheetTitle ,
19
25
SheetTrigger ,
20
26
} from "@/components/ui/sheet" ;
27
+ import { Textarea } from "@/components/ui/textarea" ;
28
+ import { parseError } from "@/utils/errorParser" ;
21
29
22
- interface VoteButtonProps {
23
- contract : ThirdwebContract ;
24
- isLoggedIn : boolean ;
25
- }
26
-
27
- const PROPOSAL_FORM_ID = "proposal-form-id" ;
28
-
29
- export const ProposalButton : React . FC < VoteButtonProps > = ( {
30
+ export function ProposalButton ( {
30
31
contract,
31
32
isLoggedIn,
32
- } ) => {
33
+ } : {
34
+ contract : ThirdwebContract ;
35
+ isLoggedIn : boolean ;
36
+ } ) {
33
37
const [ open , setOpen ] = useState ( false ) ;
34
38
const sendTx = useSendAndConfirmTransaction ( ) ;
35
- const {
36
- register,
37
- handleSubmit,
38
- formState : { errors } ,
39
- } = useForm < { description : string } > ( ) ;
39
+ const form = useForm < { description : string } > ( {
40
+ defaultValues : {
41
+ description : "" ,
42
+ } ,
43
+ } ) ;
44
+
45
+ async function onSubmit ( data : { description : string } ) {
46
+ const tx = VoteExt . propose ( {
47
+ calldatas : [ "0x" ] ,
48
+ contract,
49
+ description : data . description ,
50
+ targets : [ contract . address ] ,
51
+ values : [ 0n ] ,
52
+ } ) ;
53
+
54
+ await sendTx . mutateAsync ( tx , {
55
+ onError : ( error ) => {
56
+ toast . error ( "Failed to create proposal" , {
57
+ description : parseError ( error ) ,
58
+ } ) ;
59
+ console . error ( error ) ;
60
+ } ,
61
+ onSuccess : ( ) => {
62
+ toast . success ( "Proposal created successfully" ) ;
63
+ setOpen ( false ) ;
64
+ } ,
65
+ } ) ;
66
+ }
40
67
41
68
return (
42
69
< Sheet onOpenChange = { setOpen } open = { open } >
43
70
< SheetTrigger asChild >
44
- < Button
45
- colorScheme = "primary"
46
- leftIcon = { < PlusIcon className = "size-5" /> }
47
- >
71
+ < Button className = "gap-2" size = "sm" >
72
+ < PlusIcon className = "size-3.5" />
48
73
Create Proposal
49
74
</ Button >
50
75
</ SheetTrigger >
51
- < SheetContent className = "w-full sm:w-[540px] sm: max-w-[90%] lg:w-[700px] " >
76
+ < SheetContent className = "! w-full lg:! max-w-lg " >
52
77
< SheetHeader >
53
78
< SheetTitle > Create new proposal</ SheetTitle >
54
79
</ SheetHeader >
55
- < form
56
- className = "mt-10 flex flex-col gap-6"
57
- id = { PROPOSAL_FORM_ID }
58
- onSubmit = { handleSubmit ( ( data ) => {
59
- const tx = VoteExt . propose ( {
60
- calldatas : [ "0x" ] ,
61
- contract,
62
- description : data . description ,
63
- targets : [ contract . address ] ,
64
- values : [ 0n ] ,
65
- } ) ;
66
- toast . promise (
67
- sendTx . mutateAsync ( tx , {
68
- onError : ( error ) => {
69
- console . error ( error ) ;
70
- } ,
71
- onSuccess : ( ) => {
72
- setOpen ( false ) ;
73
- } ,
74
- } ) ,
75
- {
76
- error : "Failed to create proposal" ,
77
- loading : "Creating proposal..." ,
78
- success : "Proposal created successfully" ,
79
- } ,
80
- ) ;
81
- } ) }
82
- >
83
- < FormControl isInvalid = { ! ! errors . description } isRequired >
84
- < FormLabel > Description</ FormLabel >
85
- < Textarea { ...register ( "description" ) } />
86
- < FormErrorMessage > { errors ?. description ?. message } </ FormErrorMessage >
87
- </ FormControl >
88
- </ form >
89
- < div className = "mt-6 flex flex-row justify-end gap-3" >
90
- < Button
91
- isDisabled = { sendTx . isPending }
92
- onClick = { ( ) => setOpen ( false ) }
93
- variant = "outline"
80
+ < Form { ...form } >
81
+ < form
82
+ className = "mt-4 flex flex-col gap-6"
83
+ onSubmit = { form . handleSubmit ( onSubmit ) }
94
84
>
95
- Cancel
96
- </ Button >
97
- < TransactionButton
98
- client = { contract . client }
99
- form = { PROPOSAL_FORM_ID }
100
- isLoggedIn = { isLoggedIn }
101
- isPending = { sendTx . isPending }
102
- transactionCount = { 1 }
103
- txChainID = { contract . chain . id }
104
- type = "submit"
105
- >
106
- Submit
107
- </ TransactionButton >
108
- </ div >
85
+ < FormField
86
+ control = { form . control }
87
+ name = "description"
88
+ render = { ( { field } ) => (
89
+ < FormItem >
90
+ < FormLabel > Description</ FormLabel >
91
+ < FormControl >
92
+ < Textarea
93
+ className = "bg-card"
94
+ placeholder = "Enter proposal description..."
95
+ { ...field }
96
+ />
97
+ </ FormControl >
98
+ < FormMessage />
99
+ </ FormItem >
100
+ ) }
101
+ rules = { {
102
+ required : "Description is required" ,
103
+ } }
104
+ />
105
+
106
+ < div className = "mt-6 flex flex-row justify-end gap-3" >
107
+ < Button
108
+ disabled = { sendTx . isPending }
109
+ onClick = { ( ) => setOpen ( false ) }
110
+ variant = "outline"
111
+ >
112
+ Cancel
113
+ </ Button >
114
+ < TransactionButton
115
+ client = { contract . client }
116
+ isLoggedIn = { isLoggedIn }
117
+ isPending = { sendTx . isPending }
118
+ transactionCount = { 1 }
119
+ txChainID = { contract . chain . id }
120
+ type = "submit"
121
+ >
122
+ Submit
123
+ </ TransactionButton >
124
+ </ div >
125
+ </ form >
126
+ </ Form >
109
127
</ SheetContent >
110
128
</ Sheet >
111
129
) ;
112
- } ;
130
+ }
0 commit comments