feat: refactor password reset flow with new routes and forms

This commit is contained in:
javayhu 2025-02-23 13:26:25 +08:00
parent 940b8e9dfc
commit 0d7e5ba6ae
8 changed files with 43 additions and 60 deletions

View File

@ -0,0 +1,15 @@
import { ForgotPasswordForm } from "@/components/auth/forgot-password-form";
import { siteConfig } from "@/config/site";
import { constructMetadata } from "@/lib/metadata";
export const metadata = constructMetadata({
title: "Forgot Password",
description: "Forgot your password? Reset it.",
canonicalUrl: `${siteConfig.url}/auth/forgot-password`,
});
const ForgotPasswordPage = () => {
return <ForgotPasswordForm />;
};
export default ForgotPasswordPage;

View File

@ -1,5 +1,4 @@
import BackButtonSmall from "@/components/shared/back-button-small";
import Image from "next/image";
/**
* auth layout is different from other public layouts,
@ -13,25 +12,9 @@ export default function AuthLayout({
children: React.ReactNode;
}) {
return (
<div>
<div className="w-full lg:grid lg:min-h-screen lg:grid-cols-2">
{/* auth form */}
<div className="flex items-center justify-center relative w-full h-full min-h-screen">
<BackButtonSmall className="absolute top-6 left-6" />
<div className="w-full max-w-md px-4">{children}</div>
</div>
{/* brand image */}
<div className="hidden bg-muted lg:block">
<Image
src="/placeholder.svg"
alt="Image"
width="1920"
height="1080"
className="h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
/>
</div>
</div>
<div className="flex items-center justify-center relative w-full h-full min-h-screen">
<BackButtonSmall className="absolute top-6 left-6" />
<div className="w-full max-w-md px-4">{children}</div>
</div>
);
}

View File

@ -1,15 +0,0 @@
import { NewPasswordForm } from "@/components/auth/new-password-form";
import { siteConfig } from "@/config/site";
import { constructMetadata } from "@/lib/metadata";
export const metadata = constructMetadata({
title: "New Password",
description: "Set a new password",
canonicalUrl: `${siteConfig.url}/auth/new-password`,
});
const NewPasswordPage = () => {
return <NewPasswordForm />;
};
export default NewPasswordPage;

View File

@ -1,15 +1,15 @@
import { ResetPasswordForm } from "@/components/auth/reset-form";
import { ResetPasswordForm } from "@/components/auth/reset-password-form";
import { siteConfig } from "@/config/site";
import { constructMetadata } from "@/lib/metadata";
export const metadata = constructMetadata({
title: "Reset Password",
description: "Reset your password",
canonicalUrl: `${siteConfig.url}/auth/reset`,
description: "Set a new password",
canonicalUrl: `${siteConfig.url}/auth/reset-password`,
});
const ResetPage = () => {
const ResetPasswordPage = () => {
return <ResetPasswordForm />;
};
export default ResetPage;
export default ResetPasswordPage;

View File

@ -13,7 +13,7 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { ResetPasswordSchema } from "@/lib/schemas";
import { ForgotPasswordSchema } from "@/lib/schemas";
import { zodResolver } from "@hookform/resolvers/zod";
import { useState, useTransition } from "react";
import { useForm } from "react-hook-form";
@ -21,39 +21,39 @@ import type * as z from "zod";
import { Icons } from "@/components/icons/icons";
import { authClient } from "@/lib/auth-client";
export const ResetPasswordForm = () => {
export const ForgotPasswordForm = () => {
const [error, setError] = useState<string | undefined>("");
const [success, setSuccess] = useState<string | undefined>("");
const [isPending, setIsPending] = useState(false);
const form = useForm<z.infer<typeof ResetPasswordSchema>>({
resolver: zodResolver(ResetPasswordSchema),
const form = useForm<z.infer<typeof ForgotPasswordSchema>>({
resolver: zodResolver(ForgotPasswordSchema),
defaultValues: {
email: "",
},
});
const onSubmit = async (values: z.infer<typeof ResetPasswordSchema>) => {
const onSubmit = async (values: z.infer<typeof ForgotPasswordSchema>) => {
const { data, error } = await authClient.forgetPassword({
email: values.email,
redirectTo: "/auth/new-password",
redirectTo: "/auth/reset-password",
}, {
onRequest: (ctx) => {
// console.log("reset, request:", ctx.url);
// console.log("forgotPassword, request:", ctx.url);
setIsPending(true);
setError("");
setSuccess("");
},
onResponse: (ctx) => {
// console.log("reset, response:", ctx.response);
// console.log("forgotPassword, response:", ctx.response);
setIsPending(false);
},
onSuccess: (ctx) => {
// console.log("reset, success:", ctx.data);
setSuccess("Please check your email for the reset password link");
// console.log("forgotPassword, success:", ctx.data);
setSuccess("Please check your email inbox");
},
onError: (ctx) => {
console.log("reset, error:", ctx.error);
console.log("forgotPassword, error:", ctx.error);
setError(ctx.error.message);
},
});
@ -101,7 +101,7 @@ export const ResetPasswordForm = () => {
) : (
""
)}
<span>Send reset email</span>
<span>Send reset password email</span>
</Button>
</form>
</Form>

View File

@ -119,7 +119,7 @@ export const LoginForm = ({ className }: { className?: string }) => {
asChild
className="px-0 font-normal text-muted-foreground"
>
<Link href="/auth/reset-password" className="text-xs underline">
<Link href="/auth/forgot-password" className="text-xs underline">
Forgot password?
</Link>
</Button>

View File

@ -15,14 +15,14 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { authClient } from "@/lib/auth-client";
import { NewPasswordSchema } from "@/lib/schemas";
import { ResetPasswordSchema } from "@/lib/schemas";
import { zodResolver } from "@hookform/resolvers/zod";
import { useRouter, useSearchParams } from "next/navigation";
import { useState, useTransition } from "react";
import { useForm } from "react-hook-form";
import type * as z from "zod";
export const NewPasswordForm = () => {
export const ResetPasswordForm = () => {
const searchParams = useSearchParams();
const token = searchParams.get("token");
if (!token) {
@ -36,14 +36,14 @@ export const NewPasswordForm = () => {
const [success, setSuccess] = useState<string | undefined>("");
const [isPending, setIsPending] = useState(false);
const form = useForm<z.infer<typeof NewPasswordSchema>>({
resolver: zodResolver(NewPasswordSchema),
const form = useForm<z.infer<typeof ResetPasswordSchema>>({
resolver: zodResolver(ResetPasswordSchema),
defaultValues: {
password: "",
},
});
const onSubmit = async (values: z.infer<typeof NewPasswordSchema>) => {
const onSubmit = async (values: z.infer<typeof ResetPasswordSchema>) => {
const { data, error } = await authClient.resetPassword({
newPassword: values.password,
token,

View File

@ -158,13 +158,13 @@ export type UserPasswordData = z.infer<typeof UserPasswordSchema>;
/**
* auth related schemas
*/
export const NewPasswordSchema = z.object({
export const ResetPasswordSchema = z.object({
password: z.string().min(8, {
message: "Minimum 8 characters required",
}),
});
export const ResetPasswordSchema = z.object({
export const ForgotPasswordSchema = z.object({
email: z.string().email({
message: "Email is required",
}),