1
- import React from "react" ;
1
+ import React , { useRef } from "react" ;
2
2
import Panel from "@component/Panel" ;
3
3
import Head from "@layout/Head" ;
4
4
import Link from "@component/Link" ;
5
5
import { useRouter } from "next/router" ;
6
6
import { withSessionSsr } from "@lib/session" ;
7
- import { Text , Container , VStack , Wrap } from "@chakra-ui/react" ;
7
+ import { Text , Container , VStack , Wrap , HStack } from "@chakra-ui/react" ;
8
8
import { trpc } from "@util/trpc" ;
9
9
import { useFormFeedback } from "@hook/useFormFeedback" ;
10
10
import TextInput from "@component/TextInput" ;
@@ -13,6 +13,8 @@ import { FormField } from "@component/FormField";
13
13
import { useForm , SubmitHandler } from "react-hook-form" ;
14
14
import { z } from "zod" ;
15
15
import { zodResolver } from "@hookform/resolvers/zod" ;
16
+ import { Captcha } from "@component/Captcha" ;
17
+ import ReCAPTCHA from "react-google-recaptcha" ;
16
18
17
19
const fields = [
18
20
{ type : "input" , name : "name" , label : "Account Name" } ,
@@ -30,24 +32,28 @@ const fields = [
30
32
const schema = z . object ( {
31
33
name : z . string ( ) . min ( 5 , { message : "Account name must be at least 5 characters long" } ) ,
32
34
password : z . string ( ) . min ( 6 , { message : "Password must be at least 6 characters long" } ) ,
35
+ captcha : z . string ( { message : "Captcha is required" } ) ,
33
36
} ) ;
34
37
35
38
export default function Login ( ) {
36
39
const {
37
40
register,
38
41
handleSubmit,
39
42
reset,
43
+ setValue,
44
+ trigger,
40
45
formState : { errors, isValid, isSubmitting } ,
41
46
} = useForm < z . infer < typeof schema > > ( {
42
47
resolver : zodResolver ( schema ) ,
43
48
} ) ;
49
+ const captchaRef = useRef < ReCAPTCHA > ( null ) ;
44
50
const router = useRouter ( ) ;
45
51
const login = trpc . account . login . useMutation ( ) ;
46
52
const { handleResponse, showResponse } = useFormFeedback ( ) ;
47
53
48
- const onSubmit : SubmitHandler < z . infer < typeof schema > > = async ( { name, password } ) => {
54
+ const onSubmit : SubmitHandler < z . infer < typeof schema > > = async ( { name, password, captcha } ) => {
49
55
handleResponse ( async ( ) => {
50
- const account = await login . mutateAsync ( { name, password } ) ;
56
+ const account = await login . mutateAsync ( { name, password, captchaToken : captcha } ) ;
51
57
if ( account ) {
52
58
const redirectUrl = ( router . query . redirect as string ) || "/account" ;
53
59
router . push ( redirectUrl ) ;
@@ -57,35 +63,56 @@ export default function Login() {
57
63
} ) ;
58
64
59
65
reset ( ) ;
66
+
67
+ if ( captchaRef . current ) {
68
+ captchaRef . current . reset ( ) ;
69
+ }
60
70
} ;
61
71
62
72
return (
63
73
< >
64
- < Head title = "Login" />
65
- < Panel header = "Login" >
66
- < Text align = "center" margin = "10px" >
67
- Please enter your account name and your password.
68
- </ Text >
69
- < Text align = "center" margin = "10px" >
70
- < Link href = "/account/register" text = "Create an account " />
71
- if you do not have one yet.
72
- </ Text >
74
+ < Head title = "Log In" />
75
+ < Panel header = "Log In" >
76
+ < VStack >
77
+ < form onSubmit = { handleSubmit ( onSubmit ) } >
78
+ < Container alignContent = { "center" } padding = { 2 } >
79
+ < VStack spacing = { 5 } >
80
+ { fields . map ( ( field ) => (
81
+ < FormField key = { field . name } error = { ( errors as any ) [ field . name ] ?. message } name = { field . name } label = { field . label } >
82
+ < TextInput type = { field . type } { ...register ( field . name as any ) } />
83
+ </ FormField >
84
+ ) ) }
73
85
74
- < form onSubmit = { handleSubmit ( onSubmit ) } >
75
- < Container alignContent = { "center" } padding = { 2 } >
76
- < VStack spacing = { 5 } >
77
- { fields . map ( ( field ) => (
78
- < FormField key = { field . name } error = { ( errors as any ) [ field . name ] ?. message } name = { field . name } label = { field . label } >
79
- < TextInput type = { field . type } { ...register ( field . name as any ) } />
86
+ < FormField error = { errors . captcha ?. message } name = "Captcha" justifyItems = "center" >
87
+ < Captcha
88
+ { ...register ( "captcha" ) }
89
+ onChange = { ( token ) => {
90
+ setValue ( "captcha" , token ?? "" ) ;
91
+ trigger ( "captcha" ) ;
92
+ } }
93
+ ref = { captchaRef }
94
+ />
80
95
</ FormField >
81
- ) ) }
82
- < Wrap spacing = { 2 } padding = "10px" >
83
- < Button isLoading = { isSubmitting } isActive = { ! isValid } loadingText = "Submitting" type = "submit" value = "Submit" btnColorType = "primary" />
84
- < Button value = "Lost Account?" btnColorType = "danger" href = "/account/lost" />
85
- </ Wrap >
86
- </ VStack >
87
- </ Container >
88
- </ form >
96
+
97
+ < Button
98
+ isLoading = { isSubmitting }
99
+ isActive = { ! isValid }
100
+ width = "100%"
101
+ loadingText = "Submitting"
102
+ type = "submit"
103
+ value = "Log In"
104
+ btnColorType = "primary"
105
+ />
106
+
107
+ < Text align = "center" >
108
+ Don't have an account? < Link href = "/account/register" > Register</ Link >
109
+ </ Text >
110
+
111
+ < Link href = "/account/lost" > Forgot password?</ Link >
112
+ </ VStack >
113
+ </ Container >
114
+ </ form >
115
+ </ VStack >
89
116
</ Panel >
90
117
</ >
91
118
) ;
0 commit comments