1
- import React from "react" ;
2
- import Panel from "@component/Panel" ;
1
+ import { useRef } from "react" ;
3
2
import Head from "@layout/Head" ;
4
3
import { withSessionSsr } from "@lib/session" ;
5
4
import { trpc } from "@util/trpc" ;
6
5
import { SubmitHandler , useForm } from "react-hook-form" ;
7
6
import { z } from "zod" ;
8
7
import { zodResolver } from "@hookform/resolvers/zod" ;
9
8
import TextInput from "@component/TextInput" ;
10
- import { Container , VStack , Wrap } from "@chakra-ui/react" ;
9
+ import { Text , VStack } from "@chakra-ui/react" ;
11
10
import Button from "@component/Button" ;
12
11
import { FormField } from "@component/FormField" ;
13
12
import { useFormFeedback } from "@hook/useFormFeedback" ;
14
13
import { useRouter } from "next/router" ;
14
+ import { Captcha } from "@component/Captcha" ;
15
+ import ReCAPTCHA from "react-google-recaptcha" ;
16
+ import { Content } from "@component/Content" ;
17
+ import Link from "@component/Link" ;
15
18
16
19
const fields = [
17
20
{
@@ -36,6 +39,8 @@ const fields = [
36
39
} ,
37
40
] ;
38
41
42
+ const isCaptchaRequired = Boolean ( process . env . NEXT_PUBLIC_CAPTCHA_SITE_KEY ) ;
43
+
39
44
const schema = z
40
45
. object ( {
41
46
name : z . string ( ) . min ( 5 , { message : "Account name must be at least 5 characters long" } ) ,
@@ -46,6 +51,7 @@ const schema = z
46
51
. regex ( / ^ [ a A - z Z 0 - 9 ] + $ / , "Invalid letters, words or format. Use a-Z and spaces." ) ,
47
52
repeatPassword : z . string ( ) ,
48
53
email : z . string ( ) . email ( { message : "Invalid email address" } ) ,
54
+ captcha : isCaptchaRequired ? z . string ( { message : "Captcha is required" } ) : z . string ( ) . optional ( ) ,
49
55
} )
50
56
. refine ( ( data ) => data . password === data . repeatPassword , {
51
57
message : "Passwords don't match" ,
@@ -57,49 +63,89 @@ export default function Register() {
57
63
register,
58
64
handleSubmit,
59
65
reset,
66
+ setValue,
67
+ trigger,
60
68
formState : { errors, isValid, isSubmitting } ,
61
69
} = useForm < z . infer < typeof schema > > ( {
62
70
resolver : zodResolver ( schema ) ,
63
71
} ) ;
72
+ const captchaRef = useRef < ReCAPTCHA > ( null ) ;
64
73
const router = useRouter ( ) ;
65
74
const { handleResponse, showResponse } = useFormFeedback ( ) ;
66
75
const createAccount = trpc . account . create . useMutation ( ) ;
67
76
68
- const onSubmit : SubmitHandler < z . infer < typeof schema > > = async ( values ) => {
77
+ const onSubmit : SubmitHandler < z . infer < typeof schema > > = async ( { name , password , email , captcha } ) => {
69
78
handleResponse ( async ( ) => {
70
79
await createAccount . mutateAsync ( {
71
- name : values . name ,
72
- password : values . password ,
73
- email : values . email ,
80
+ name,
81
+ password,
82
+ email,
83
+ captchaToken : captcha ,
74
84
} ) ;
75
85
76
86
showResponse ( "Account created successfully. You can login now." , "success" ) ;
77
87
router . push ( "/account/login" ) ;
78
88
} ) ;
79
89
80
90
reset ( ) ;
91
+ if ( captchaRef . current ) {
92
+ captchaRef . current . reset ( ) ;
93
+ }
81
94
} ;
82
95
83
96
return (
84
97
< >
85
98
< Head title = "Register" />
86
- < Panel header = "Register" >
87
- < form onSubmit = { handleSubmit ( onSubmit ) } >
88
- < Container alignContent = { "center" } padding = { 2 } >
89
- < VStack spacing = { 5 } >
99
+ < Content >
100
+ < Content . Header > Register</ Content . Header >
101
+ < Content . Body >
102
+ < form onSubmit = { handleSubmit ( onSubmit ) } >
103
+ < VStack spacing = { 10 } w = "25rem" maxW = "25rem" >
90
104
{ fields . map ( ( field ) => (
91
105
< FormField key = { field . name } error = { ( errors as any ) [ field . name ] ?. message } name = { field . name } label = { field . label } >
92
106
< TextInput type = { field . type } { ...register ( field . name as any ) } />
93
107
</ FormField >
94
108
) ) }
95
- < Wrap spacing = { 2 } padding = "10px" >
96
- < Button isLoading = { isSubmitting } isActive = { ! isValid } loadingText = "Submitting" type = "submit" value = "Submit" btnColorType = "primary" />
97
- < Button value = "Reset" btnColorType = "danger" />
98
- </ Wrap >
109
+ { process . env . NEXT_PUBLIC_CAPTCHA_SITE_KEY && (
110
+ < FormField error = { errors . captcha ?. message } name = "Captcha" justifyItems = "center" >
111
+ < Captcha
112
+ { ...register ( "captcha" ) }
113
+ onChange = { ( token ) => {
114
+ setValue ( "captcha" , token ?? "" ) ;
115
+ trigger ( "captcha" ) ;
116
+ } }
117
+ ref = { captchaRef }
118
+ />
119
+ </ FormField >
120
+ ) }
121
+ < Button
122
+ isLoading = { isSubmitting }
123
+ width = "100%"
124
+ isActive = { ! isValid }
125
+ loadingText = "Submitting"
126
+ type = "submit"
127
+ value = "Register"
128
+ btnColorType = "primary"
129
+ />
130
+ < VStack >
131
+ < Text >
132
+ By creating an account you agree to our{ " " }
133
+ < Link textDecoration = "underline" href = "/rules" >
134
+ rules
135
+ </ Link >
136
+ .
137
+ </ Text >
138
+ < Text align = "center" >
139
+ Have an account?{ " " }
140
+ < Link textDecoration = "underline" href = "/account/login" >
141
+ Login
142
+ </ Link >
143
+ </ Text >
144
+ </ VStack >
99
145
</ VStack >
100
- </ Container >
101
- </ form >
102
- </ Panel >
146
+ </ form >
147
+ </ Content . Body >
148
+ </ Content >
103
149
</ >
104
150
) ;
105
151
}
0 commit comments