Skip to content

Commit 905e1d1

Browse files
feat: Add Forgot Password and Reset Password pages with functionality
1 parent 93cbd76 commit 905e1d1

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed

Diff for: app/auth/forgot-password/page.tsx

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { Button } from '@/components/ui/button'
5+
import { Alert, AlertDescription } from '@/components/ui/alert'
6+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
7+
import { supabase } from '@/lib/supabaseClient'
8+
9+
export default function ForgotPasswordPage() {
10+
const [email, setEmail] = useState('')
11+
const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle')
12+
13+
const handleSubmit = async (e: React.FormEvent) => {
14+
e.preventDefault()
15+
setStatus('sending')
16+
17+
const { error } = await supabase.auth.resetPasswordForEmail(email, {
18+
redirectTo: `${window.location.origin}/auth/reset-password`
19+
})
20+
21+
if (error) {
22+
setStatus('error')
23+
console.error('Error sending reset email:', error.message)
24+
} else {
25+
setStatus('sent')
26+
}
27+
}
28+
29+
return (
30+
<main className="flex min-h-screen items-center justify-center px-4">
31+
<Card className="w-full max-w-md rounded-2xl">
32+
<CardHeader>
33+
<CardTitle className="text-center text-2xl">Forgot Password</CardTitle>
34+
</CardHeader>
35+
<CardContent className='space-y-4 align-center'>
36+
<form onSubmit={handleSubmit} className="space-y-4">
37+
<input
38+
type="email"
39+
placeholder="[email protected]"
40+
value={email}
41+
onChange={(e) => setEmail(e.target.value)}
42+
required
43+
className="w-full p-2 border rounded"
44+
/>
45+
<div className="flex justify-center">
46+
<Button type="submit" disabled={status === 'sending'}>
47+
{status === 'sending' ? 'Sending...' : 'Send Reset Link'}
48+
</Button>
49+
</div>
50+
</form>
51+
{status === 'sent' && (
52+
<Alert variant="default" className="mt-4">
53+
<AlertDescription>Check your email for the reset link.</AlertDescription>
54+
</Alert>
55+
)}
56+
{status === 'error' && (
57+
<Alert variant="destructive" className="mt-4">
58+
<AlertDescription>Failed to send reset link. Please try again.</AlertDescription>
59+
</Alert>
60+
)}
61+
</CardContent>
62+
</Card>
63+
</main>
64+
)
65+
}

Diff for: app/auth/reset-password/page.tsx

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { useRouter } from 'next/navigation'
5+
import { Button } from '@/components/ui/button'
6+
import { Alert, AlertDescription } from '@/components/ui/alert'
7+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
8+
import { supabase } from '@/lib/supabaseClient'
9+
10+
export default function ResetPasswordPage() {
11+
const router = useRouter()
12+
const [newPassword, setNewPassword] = useState('')
13+
const [status, setStatus] = useState<'idle' | 'updating' | 'updated' | 'error'>('idle')
14+
15+
const handleSubmit = async (e: React.FormEvent) => {
16+
e.preventDefault()
17+
setStatus('updating')
18+
19+
// Use the access token to update the user's password
20+
const { data, error } = await supabase.auth.updateUser({ password: newPassword })
21+
22+
if (error) {
23+
setStatus('error')
24+
console.error('Error updating password:', error.message)
25+
} else {
26+
setStatus('updated')
27+
// Redirect to login or another page after successful password update
28+
setTimeout(() => {
29+
router.push('/auth/signin')
30+
}, 2000)
31+
}
32+
}
33+
34+
return (
35+
<main className="flex min-h-screen items-center justify-center px-4">
36+
<Card className="w-full max-w-md rounded-2xl">
37+
<CardHeader>
38+
<CardTitle className="text-center text-2xl">Set New Password</CardTitle>
39+
</CardHeader>
40+
<CardContent>
41+
<form onSubmit={handleSubmit} className="space-y-4">
42+
<input
43+
type="password"
44+
placeholder="New Password"
45+
value={newPassword}
46+
onChange={(e) => setNewPassword(e.target.value)}
47+
required
48+
className="w-full p-2 border rounded"
49+
/>
50+
<Button type="submit" disabled={status === 'updating'}>
51+
{status === 'updating' ? 'Updating...' : 'Set New Password'}
52+
</Button>
53+
</form>
54+
{status === 'updated' && (
55+
<Alert variant="default" className="mt-4">
56+
<AlertDescription>Password updated successfully! Redirecting to sign in...</AlertDescription>
57+
</Alert>
58+
)}
59+
{status === 'error' && (
60+
<Alert variant="destructive" className="mt-4">
61+
<AlertDescription>Failed to update password. Please try again.</AlertDescription>
62+
</Alert>
63+
)}
64+
</CardContent>
65+
</Card>
66+
</main>
67+
)
68+
}

Diff for: components/AuthComponent/SigninForm.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ export default function SigninForm() {
128128
title="Sign in"
129129
type="submit"
130130
/>
131+
<div className="flex justify-center mt-4">
132+
<a href="/auth/forgot-password" className="text-blue-600 hover:underline">
133+
Forgot Password?
134+
</a>
135+
</div>
131136
</form>
132137
</Form>
133138
</CardContent>

0 commit comments

Comments
 (0)