Skip to content

Commit e3d9346

Browse files
committed
feat: improved Supabase password reset flow, username check, and UX enhancements
1 parent 5690da4 commit e3d9346

5 files changed

Lines changed: 316 additions & 270 deletions

File tree

Backend/.env-example

Lines changed: 0 additions & 8 deletions
This file was deleted.

Frontend/src/context/AuthContext.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
ReactNode,
66
useEffect,
77
} from "react";
8-
import { useNavigate } from "react-router-dom";
8+
import { useNavigate, useLocation } from "react-router-dom";
99
import { supabase, User } from "../utils/supabase";
1010

1111
interface AuthContextType {
@@ -25,6 +25,7 @@ export const AuthProvider = ({ children }: AuthProviderProps) => {
2525
const [user, setUser] = useState<User | null>(null);
2626
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
2727
const navigate = useNavigate();
28+
const location = useLocation();
2829

2930
useEffect(() => {
3031
supabase.auth.getSession().then(({ data }) => {
@@ -34,14 +35,18 @@ export const AuthProvider = ({ children }: AuthProviderProps) => {
3435
const { data: listener } = supabase.auth.onAuthStateChange(
3536
(event, session) => {
3637
setUser(session?.user || null);
37-
if (session?.user) {
38+
if (
39+
session?.user &&
40+
location.pathname !== "/reset-password" &&
41+
event !== "PASSWORD_RECOVERY"
42+
) {
3843
navigate("/dashboard");
3944
}
4045
}
4146
);
4247

4348
return () => listener.subscription.unsubscribe();
44-
}, []);
49+
}, [location.pathname, navigate]);
4550

4651
const login = () => {
4752
setIsAuthenticated(true);

Frontend/src/pages/ForgotPassword.tsx

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,41 @@
11
import { useState } from "react";
22
import { Link } from "react-router-dom";
33
import { ArrowLeft, Check, Rocket } from "lucide-react";
4+
import { supabase } from "../utils/supabase";
45

56
export default function ForgotPasswordPage() {
67
const [email, setEmail] = useState("");
78
const [isLoading, setIsLoading] = useState(false);
89
const [isSubmitted, setIsSubmitted] = useState(false);
910
const [error, setError] = useState("");
11+
const [showSignupPrompt, setShowSignupPrompt] = useState(false);
1012

1113
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
1214
e.preventDefault();
1315
setIsLoading(true);
1416
setError("");
17+
setShowSignupPrompt(false);
1518

1619
try {
17-
// In a real app, you would call your auth API here
18-
await new Promise((resolve) => setTimeout(resolve, 1500));
20+
// Check if email exists in users table
21+
const { data: users, error: userError } = await supabase
22+
.from("users")
23+
.select("id")
24+
.eq("email", email)
25+
.maybeSingle();
26+
if (userError) throw userError;
27+
if (!users) {
28+
setShowSignupPrompt(true);
29+
setIsLoading(false);
30+
return;
31+
}
32+
const { error } = await supabase.auth.resetPasswordForEmail(email, {
33+
redirectTo: window.location.origin + "/reset-password"
34+
});
35+
if (error) throw error;
1936
setIsSubmitted(true);
20-
} catch (err) {
21-
setError("Something went wrong. Please try again.");
37+
} catch (err: any) {
38+
setError(err.message || "Something went wrong. Please try again.");
2239
} finally {
2340
setIsLoading(false);
2441
}
@@ -88,6 +105,12 @@ export default function ForgotPasswordPage() {
88105
</div>
89106
)}
90107

108+
{showSignupPrompt && (
109+
<div className="mb-6 p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg text-yellow-700 dark:text-yellow-400 text-sm animate-[pulse_1s_ease-in-out]">
110+
No account found with this email. <Link to="/signup" className="underline text-purple-600">Sign up?</Link>
111+
</div>
112+
)}
113+
91114
<form onSubmit={handleSubmit} className="space-y-6">
92115
<div className="space-y-2">
93116
<label
@@ -103,7 +126,7 @@ export default function ForgotPasswordPage() {
103126
onChange={(e) => setEmail(e.target.value)}
104127
required
105128
className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-400 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white transition-all duration-200"
106-
placeholder="you@example.com"
129+
placeholder="you@example.com (CASE sensitive)"
107130
/>
108131
</div>
109132

0 commit comments

Comments
 (0)