Skip to content

Commit 03cdbc1

Browse files
committed
♻️ Refactor RecoverPassword component and form handling
1 parent acddfbd commit 03cdbc1

1 file changed

Lines changed: 80 additions & 56 deletions

File tree

Lines changed: 80 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
import { Container, Heading, Input, Text } from "@chakra-ui/react"
1+
import { zodResolver } from "@hookform/resolvers/zod"
22
import { 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"
118
import { isLoggedIn } from "@/hooks/useAuth"
129
import 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

1929
export const Route = createFileRoute("/recover-password")({
2030
component: RecoverPassword,
@@ -28,13 +38,13 @@ export const Route = createFileRoute("/recover-password")({
2838
})
2939

3040
function 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

Comments
 (0)