Skip to content

Commit

Permalink
forgotPasswordForm added and forget password feature enabled on login…
Browse files Browse the repository at this point in the history
… page
  • Loading branch information
aayank13 committed Jan 7, 2025
1 parent 765f385 commit f7165ba
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 46 deletions.
68 changes: 33 additions & 35 deletions app/(root)/auth/page.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,43 @@
"use client";

import { useState } from 'react';
import { LoginForm } from '@/components/auth/loginForm';
import { SignupForm } from '@/components/auth/signupForm';
import { useState } from "react";
import { LoginForm } from "@/components/auth/loginForm";
import { SignupForm } from "@/components/auth/signupForm";

export default function AuthPage() {
const [isLogin, setIsLogin] = useState(true);

return (
<div className="min-h-screen w-full flex items-center justify-center p-8">
<div className="w-full max-w-md space-y-6">
<div className="space-y-2">
<h1 className="text-5xl font-bold tracking-tight">
Get started
</h1>
{!isLogin && (
<p className="text-muted-foreground">
Already have an account?{' '}
<button
onClick={() => setIsLogin(true)}
className="text-primary hover:underline font-medium"
>
Sign in
</button>
</p>
)}
{isLogin && (
<p className="text-muted-foreground">
Don&apos;t have an account?{' '}
<button
onClick={() => setIsLogin(false)}
className="text-primary hover:underline font-medium"
>
Sign up
</button>
</p>
)}
</div>

{isLogin ? <LoginForm /> : <SignupForm />}
<div className="min-h-screen w-full flex items-center justify-center p-8">
<div className="w-full max-w-md space-y-6">
<div className="space-y-2">
<h1 className="text-5xl font-bold tracking-tight">Get started</h1>
{!isLogin && (
<p className="text-muted-foreground">
Already have an account?{" "}
<button
onClick={() => setIsLogin(true)}
className="text-primary text-indigo-600 hover:underline font-bold"
>
Sign in
</button>
</p>
)}
{isLogin && (
<p className="text-muted-foreground">
Don&apos;t have an account?{" "}
<button
onClick={() => setIsLogin(false)}
className="text-primary text-indigo-600 hover:underline font-bold"
>
Sign up
</button>
</p>
)}
</div>

{isLogin ? <LoginForm /> : <SignupForm />}
</div>
</div>
);
}
}
131 changes: 131 additions & 0 deletions components/auth/ForgotPasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { useState } from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { Loader2, ArrowLeft, CheckCircle2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { createClient } from "@/lib/supabase/client";
import { useToast } from "@/hooks/use-toast";
import { Alert, AlertDescription } from "@/components/ui/alert";

const formSchema = z.object({
email: z.string().email("Invalid email address"),
});

export function ForgotPasswordForm({ onBack }: { onBack: () => void }) {
const [isLoading, setIsLoading] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const supabase = createClient();
const { toast } = useToast();

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: { email: "" },
});

async function onSubmit(values: z.infer<typeof formSchema>) {
setIsLoading(true);
try {
const { error } = await supabase.auth.resetPasswordForEmail(
values.email,
{
redirectTo: `${window.location.origin}/reset-password`,
}
);

if (error) {
toast({
variant: "destructive",
title: "Error",
description: error.message,
});
return;
}

setIsSuccess(true);
} catch (error) {
console.error("Password reset error:", error);
toast({
variant: "destructive",
title: "Error",
description: (error as Error).message,
});
} finally {
setIsLoading(false);
}
}

if (isSuccess) {
return (
<div className="space-y-6">
<Alert>
<CheckCircle2 className="h-4 w-4" />
<AlertDescription>
Password reset instructions have been sent to your email address.
</AlertDescription>
</Alert>
<Button variant="outline" className="w-full" onClick={onBack}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Login
</Button>
</div>
);
}

return (
<div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="[email protected]" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<div className="text-center">
<p className="text-sm text-muted-foreground">
Enter your email address and we&apos;ll send you a link to reset your
password.
</p>
</div>
<div className="space-y-2">
<Button
type="submit"
className="w-full bg-indigo-600 hover:bg-indigo-800"
disabled={isLoading}
>
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Send Reset Instructions
</Button>
<Button
type="button"
variant="outline"
className="w-full"
onClick={onBack}
>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Login
</Button>
</div>
</form>
</Form>
</div>
);
}
36 changes: 25 additions & 11 deletions components/auth/loginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"use client";

import { useState } from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
Expand All @@ -16,9 +14,10 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { createClient } from "@/lib/supabase/client";
import { useToast } from "@/hooks/use-toast"
import { ToastAction } from "@/components/ui/toast"
import { useToast } from "@/hooks/use-toast";
import { ToastAction } from "@/components/ui/toast";
import { OAuthButtons } from "./oAuthButton";
import { ForgotPasswordForm } from "./ForgotPasswordForm";

const formSchema = z.object({
email: z.string().email("Invalid email address"),
Expand All @@ -27,6 +26,7 @@ const formSchema = z.object({

export function LoginForm() {
const [isLoading, setIsLoading] = useState(false);
const [showForgotPassword, setShowForgotPassword] = useState(false);
const supabase = createClient();
const { toast } = useToast();

Expand Down Expand Up @@ -72,11 +72,8 @@ export function LoginForm() {
if (data?.user) {
toast({
description: "Successfully signed in! Redirecting...",
})
});
window.location.href = "/dashboard";
// setTimeout(() => {
// window.location.reload()
// }, 1000);
}
} catch (error) {
console.error("Login error:", error);
Expand All @@ -91,9 +88,12 @@ export function LoginForm() {
}
}

if (showForgotPassword) {
return <ForgotPasswordForm onBack={() => setShowForgotPassword(false)} />;
}

return (
<div className="w-full space-y-6">

<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
Expand All @@ -114,15 +114,29 @@ export function LoginForm() {
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<div className="flex items-center justify-between">
<FormLabel>Password</FormLabel>
<Button
variant="link"
className="px-0 font-normal text-indigo-600 hover:text-indigo-800"
type="button"
onClick={() => setShowForgotPassword(true)}
>
Forgot password?
</Button>
</div>
<FormControl>
<Input type="password" placeholder="••••••••" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full bg-indigo-600 hover:bg-indigo-800" disabled={isLoading}>
<Button
type="submit"
className="w-full bg-indigo-600 hover:bg-indigo-800"
disabled={isLoading}
>
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Sign In
</Button>
Expand Down
59 changes: 59 additions & 0 deletions components/ui/alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
}
)

const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
))
Alert.displayName = "Alert"

const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
))
AlertTitle.displayName = "AlertTitle"

const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
))
AlertDescription.displayName = "AlertDescription"

export { Alert, AlertTitle, AlertDescription }

0 comments on commit f7165ba

Please sign in to comment.