1- import { Container , Heading , Input , Text } from "@chakra-ui/react "
1+ import { zodResolver } from "@hookform/resolvers/zod "
22import { useMutation } from "@tanstack/react-query"
3- import { createFileRoute , redirect } from "@tanstack/react-router"
4- import { type SubmitHandler , useForm } from "react-hook-form"
5- import { FiMail } from "react-icons/fi "
3+ import { createFileRoute , Link as RouterLink , redirect } from "@tanstack/react-router"
4+ import { useForm } from "react-hook-form"
5+ import { z } from "zod "
66
7- import { type ApiError , LoginService } from "@/client"
8- import { Button } from "@/components/ui/button"
9- import { Field } from "@/components/ui/field"
10- import { InputGroup } from "@/components/ui/input-group"
7+ import { LoginService } from "@/client"
118import { isLoggedIn } from "@/hooks/useAuth"
129import useCustomToast from "@/hooks/useCustomToast"
13- import { emailPattern , handleError } from "@/utils"
10+ import { handleError } from "@/utils"
11+ import { AuthLayout } from "@/components/Common/AuthLayout"
12+ import { LoadingButton } from "@/components/ui/loading-button"
13+ import { Input } from "@/components/ui/input"
14+ import {
15+ Form ,
16+ FormControl ,
17+ FormField ,
18+ FormItem ,
19+ FormLabel ,
20+ FormMessage ,
21+ } from "@/components/ui/form"
1422
15- interface FormData {
16- email : string
17- }
23+ const formSchema = z . object ( {
24+ email : z . email ( ) ,
25+ } )
26+
27+ type FormData = z . infer < typeof formSchema >
1828
1929export const Route = createFileRoute ( "/recover-password" ) ( {
2030 component : RecoverPassword ,
@@ -28,13 +38,13 @@ export const Route = createFileRoute("/recover-password")({
2838} )
2939
3040function RecoverPassword ( ) {
31- const {
32- register ,
33- handleSubmit ,
34- reset ,
35- formState : { errors , isSubmitting } ,
36- } = useForm < FormData > ( )
37- const { showSuccessToast } = useCustomToast ( )
41+ const form = useForm < FormData > ( {
42+ resolver : zodResolver ( formSchema ) ,
43+ defaultValues : {
44+ email : "" ,
45+ } ,
46+ } )
47+ const { showSuccessToast, showErrorToast } = useCustomToast ( )
3848
3949 const recoverPassword = async ( data : FormData ) => {
4050 await LoginService . recoverPassword ( {
@@ -46,49 +56,63 @@ function RecoverPassword() {
4656 mutationFn : recoverPassword ,
4757 onSuccess : ( ) => {
4858 showSuccessToast ( "Password recovery email sent successfully." )
49- reset ( )
50- } ,
51- onError : ( err : ApiError ) => {
52- handleError ( err )
59+ form . reset ( )
5360 } ,
61+ onError : handleError . bind ( showErrorToast ) ,
5462 } )
5563
56- const onSubmit : SubmitHandler < FormData > = async ( data ) => {
64+ const onSubmit = async ( data : FormData ) => {
65+ if ( mutation . isPending ) return
5766 mutation . mutate ( data )
5867 }
5968
6069 return (
61- < Container
62- as = "form"
63- onSubmit = { handleSubmit ( onSubmit ) }
64- h = "100vh"
65- maxW = "sm"
66- alignItems = "stretch"
67- justifyContent = "center"
68- gap = { 4 }
69- centerContent
70- >
71- < Heading size = "xl" color = "ui.main" textAlign = "center" mb = { 2 } >
72- Password Recovery
73- </ Heading >
74- < Text textAlign = "center" >
75- A password recovery email will be sent to the registered account.
76- </ Text >
77- < Field invalid = { ! ! errors . email } errorText = { errors . email ?. message } >
78- < InputGroup w = "100%" startElement = { < FiMail /> } >
79- < Input
80- { ...register ( "email" , {
81- required : "Email is required" ,
82- pattern : emailPattern ,
83- } ) }
84- placeholder = "Email"
85- type = "email"
86- />
87- </ InputGroup >
88- </ Field >
89- < Button variant = "solid" type = "submit" loading = { isSubmitting } >
90- Continue
91- </ Button >
92- </ Container >
70+ < AuthLayout >
71+ < Form { ...form } >
72+ < form
73+ onSubmit = { form . handleSubmit ( onSubmit ) }
74+ className = "flex flex-col gap-6"
75+ >
76+ < div className = "flex flex-col items-center gap-2 text-center" >
77+ < h1 className = "text-2xl font-bold" > Password Recovery</ h1 >
78+ </ div >
79+
80+ < div className = "grid gap-4" >
81+ < FormField
82+ control = { form . control }
83+ name = "email"
84+ render = { ( { field } ) => (
85+ < FormItem >
86+ < FormLabel > Email</ FormLabel >
87+ < FormControl >
88+ < Input
89+ placeholder = "user@example.com"
90+ type = "email"
91+ { ...field }
92+ />
93+ </ FormControl >
94+ < FormMessage />
95+ </ FormItem >
96+ ) }
97+ />
98+
99+ < LoadingButton
100+ type = "submit"
101+ className = "w-full"
102+ loading = { mutation . isPending }
103+ >
104+ Continue
105+ </ LoadingButton >
106+ </ div >
107+
108+ < div className = "text-center text-sm" >
109+ Remember your password?{ " " }
110+ < RouterLink to = "/login" className = "underline underline-offset-4" >
111+ Log in
112+ </ RouterLink >
113+ </ div >
114+ </ form >
115+ </ Form >
116+ </ AuthLayout >
93117 )
94118}
0 commit comments