Component Details
Auth Component 03

ForgotPasswordPage.tsx
Next.js•Tailwind CSS
1// app/auth/forgot-password/page.tsx
2"use client";
3
4import type React from "react";
5import { useState } from "react";
6import Link from "next/link";
7import { Button } from "@/components/ui/button";
8import { Input } from "@/components/ui/input";
9import {
10 Card,
11 CardContent,
12 CardHeader,
13 CardTitle,
14 CardDescription,
15 CardFooter,
16} from "@/components/ui/card";
17import { Alert, AlertDescription } from "@/components/ui/alert";
18import { Label } from "@/components/ui/label";
19import { AlertCircle, Loader2, Mail, CheckCircle } from "lucide-react";
20
21export default function ForgotPasswordPage() {
22 const [email, setEmail] = useState("");
23 const [error, setError] = useState("");
24 const [success, setSuccess] = useState("");
25 const [isLoading, setIsLoading] = useState(false);
26 const [isEmailSent, setIsEmailSent] = useState(false);
27
28 const handleRequestReset = async (e: React.FormEvent) => {
29 e.preventDefault();
30 setError("");
31 setSuccess("");
32 setIsLoading(true);
33
34 // Validate email
35 if (!email.trim()) {
36 setError("Please enter your email address");
37 setIsLoading(false);
38 return;
39 }
40
41 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
42 if (!emailRegex.test(email)) {
43 setError("Please enter a valid email address");
44 setIsLoading(false);
45 return;
46 }
47
48 // Simulate API call to send reset email
49 await new Promise((resolve) => setTimeout(resolve, 1500));
50
51 // In a real app with Supabase, you would:
52 // 1. Call Supabase auth.resetPasswordForEmail(email)
53 // 2. This sends a reset email with a token
54 // 3. User clicks link which redirects to /auth/reset-password
55
56 // For demo purposes, simulate success
57 setSuccess(`Password reset email sent to ${email}`);
58 setIsEmailSent(true);
59
60 // Store email in localStorage to simulate token passing
61 localStorage.setItem("passwordResetEmail", email);
62
63 setIsLoading(false);
64 };
65
66 return (
67 <div className="flex flex-col">
68 <main className="flex-1 flex items-center justify-center px-4 py-12">
69 <div className="w-full max-w-md">
70 <div className="text-center mb-8">
71 <h1 className="text-3xl font-bold mb-2">
72 {isEmailSent ? "Check your email" : "Reset your password"}
73 </h1>
74 <p className="text-muted-foreground">
75 {isEmailSent
76 ? "We sent you a password reset link"
77 : "Enter your email to receive reset instructions"}
78 </p>
79 </div>
80
81 <Card className="border shadow-sm">
82 <CardHeader className="space-y-1">
83 <CardTitle className="text-2xl">
84 {isEmailSent ? "Email sent" : "Forgot Password"}
85 </CardTitle>
86 <CardDescription>
87 {isEmailSent
88 ? "Click the link in your email to reset your password"
89 : "We'll send you a link to reset your password"}
90 </CardDescription>
91 </CardHeader>
92
93 <CardContent>
94 {!isEmailSent ? (
95 <form onSubmit={handleRequestReset} className="space-y-4">
96 <div className="space-y-2">
97 <Label htmlFor="email">Email address</Label>
98 <div className="relative">
99 <Mail className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
100 <Input
101 id="email"
102 type="email"
103 placeholder="name@example.com"
104 className="pl-10"
105 value={email}
106 onChange={(e) => setEmail(e.target.value)}
107 disabled={isLoading}
108 autoComplete="email"
109 />
110 </div>
111 </div>
112
113 {error && (
114 <Alert variant="destructive">
115 <AlertCircle className="h-4 w-4" />
116 <AlertDescription>{error}</AlertDescription>
117 </Alert>
118 )}
119
120 {success && (
121 <Alert
122 variant="default"
123 className="bg-blue-50 border-blue-200"
124 >
125 <CheckCircle className="h-4 w-4 text-blue-600" />
126 <AlertDescription className="text-blue-800">
127 {success}
128 </AlertDescription>
129 </Alert>
130 )}
131
132 <Button type="submit" className="w-full" disabled={isLoading}>
133 {isLoading ? (
134 <>
135 <Loader2 className="mr-2 h-4 w-4 animate-spin" />
136 Sending reset link...
137 </>
138 ) : (
139 "Send Reset Link"
140 )}
141 </Button>
142 </form>
143 ) : (
144 <div className="space-y-6">
145 <div className="text-center p-6 border rounded-lg bg-muted/30">
146 <Mail className="h-12 w-12 mx-auto mb-4 text-primary" />
147 <h3 className="font-semibold mb-2">Check your inbox</h3>
148 <p className="text-sm text-muted-foreground mb-4">
149 We've sent a password reset link to{" "}
150 <strong>{email}</strong>. Click the link in the email to
151 set a new password.
152 </p>
153 </div>
154
155 <div className="space-y-3">
156 <div className="text-sm text-muted-foreground">
157 <p className="font-medium mb-1">
158 Didn't receive the email?
159 </p>
160 <ul className="space-y-1">
161 <li className="flex items-start gap-2">
162 <div className="w-1 h-1 mt-2 rounded-full bg-muted-foreground" />
163 Check your spam or junk folder
164 </li>
165 <li className="flex items-start gap-2">
166 <div className="w-1 h-1 mt-2 rounded-full bg-muted-foreground" />
167 Make sure you entered the correct email
168 </li>
169 <li className="flex items-start gap-2">
170 <div className="w-1 h-1 mt-2 rounded-full bg-muted-foreground" />
171 <Button
172 type="button"
173 variant="link"
174 className="p-0 h-auto text-primary"
175 onClick={handleRequestReset}
176 disabled={isLoading}
177 >
178 Click here to resend
179 </Button>
180 </li>
181 </ul>
182 </div>
183 </div>
184 </div>
185 )}
186 </CardContent>
187
188 <CardFooter className="flex flex-col space-y-3">
189 {!isEmailSent && (
190 <div className="text-center text-sm text-muted-foreground w-full">
191 <div className="space-y-3">
192 <div className="relative">
193 <div className="absolute inset-0 flex items-center">
194 <div className="w-full border-t"></div>
195 </div>
196 <div className="relative flex justify-center text-xs">
197 <span className="bg-background px-2 text-muted-foreground">
198 Remember your password?
199 </span>
200 </div>
201 </div>
202
203 <Link href="/signin">
204 <Button
205 type="button"
206 variant="outline"
207 className="w-full"
208 >
209 Back to Sign in
210 </Button>
211 </Link>
212 </div>
213 </div>
214 )}
215 </CardFooter>
216 </Card>
217 </div>
218 </main>
219 </div>
220 );
221}