Skip to content

Commit a5289e5

Browse files
Merge pull request #14 from Shitanshukumar607/use-zustand
Use zustand
2 parents 0c415f5 + 46ae0e2 commit a5289e5

File tree

13 files changed

+293
-58
lines changed

13 files changed

+293
-58
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ jobs:
1919
- run: npm run build
2020
env:
2121
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
22-
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
22+
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY }}

app/auth/callback/page.tsx

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,54 @@
1-
import { redirect } from "next/navigation";
1+
"use client";
22

3-
const Page = () => {
4-
redirect("/");
3+
import { useEffect } from "react";
4+
import { useRouter } from "next/navigation";
5+
import { createClient } from "@/lib/supabase/client";
6+
import { useUserStore } from "@/store/userStore";
7+
8+
const AuthCallback = () => {
9+
const router = useRouter();
10+
const { setUser, setError } = useUserStore();
11+
const supabase = createClient();
12+
13+
useEffect(() => {
14+
const handleAuthCallback = async () => {
15+
try {
16+
const { data, error } = await supabase.auth.getSession();
17+
18+
if (error) throw error;
19+
20+
if (data.session?.user) {
21+
const user = data.session.user;
22+
setUser({
23+
id: user.id,
24+
email: user.email!,
25+
name: user.user_metadata?.name || user.user_metadata?.full_name,
26+
avatar_url: user.user_metadata?.avatar_url,
27+
provider: user.app_metadata?.provider || "oauth",
28+
created_at: user.created_at,
29+
updated_at: user.updated_at || user.created_at,
30+
});
31+
}
32+
33+
router.push("/");
34+
} catch (error: unknown) {
35+
setError(
36+
error instanceof Error ? error.message : "Authentication failed",
37+
);
38+
router.push("/login");
39+
}
40+
};
41+
42+
handleAuthCallback();
43+
}, [router, setUser, setError, supabase.auth]);
44+
45+
return (
46+
<div className="flex items-center justify-center min-h-screen">
47+
<p className="mt-4 text-slate-600 dark:text-neutral-400">
48+
Completing authentication...
49+
</p>
50+
</div>
51+
);
552
};
653

7-
export default Page;
54+
export default AuthCallback;

app/layout.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Navbar from "@/components/NavbarComponents/Navbar";
22
import { ThemeProvider } from "@/components/NavbarComponents/ThemeProvider";
3+
import { AuthProvider } from "@/components/AuthProvider/AuthProvider";
34
import { Space_Grotesk } from "next/font/google";
45
import "./globals.css";
56
import Footer from "@/components/Footer/Footer";
@@ -25,10 +26,12 @@ export default function RootLayout({
2526
enableSystem
2627
disableTransitionOnChange
2728
>
28-
<Navbar />
29-
<main>{children}</main>
30-
<SpeedInsights />
31-
<Footer />
29+
<AuthProvider>
30+
<Navbar />
31+
<main>{children}</main>
32+
<SpeedInsights />
33+
<Footer />
34+
</AuthProvider>
3235
</ThemeProvider>
3336
</body>
3437
</html>

app/login/page.tsx

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from "@/components/ui/field";
1818
import { Input } from "@/components/ui/input";
1919
import { createClient } from "@/lib/supabase/client";
20+
import { useUserStore } from "@/store/userStore";
2021
import Image from "next/image";
2122
import type { Provider } from "@supabase/supabase-js";
2223
import Link from "next/link";
@@ -26,47 +27,63 @@ import { useState } from "react";
2627
const Login = () => {
2728
const [email, setEmail] = useState("");
2829
const [password, setPassword] = useState("");
29-
const [error, setError] = useState<string | null>(null);
30-
const [isLoading, setIsLoading] = useState(false);
3130
const router = useRouter();
3231

32+
const { setUser, setLoading, setError, isLoading, error } = useUserStore();
3333
const supabase = createClient();
3434

3535
const handleLogin = async (e: React.FormEvent) => {
3636
e.preventDefault();
37-
setIsLoading(true);
37+
setLoading(true);
3838
setError(null);
3939

4040
try {
41-
const { error } = await supabase.auth.signInWithPassword({
42-
email,
43-
password,
44-
});
45-
if (error) throw error;
41+
const { data, error: authError } = await supabase.auth.signInWithPassword(
42+
{
43+
email,
44+
password,
45+
},
46+
);
47+
48+
if (authError) throw authError;
49+
50+
if (data.user) {
51+
setUser({
52+
id: data.user.id,
53+
email: data.user.email!,
54+
name:
55+
data.user.user_metadata?.name || data.user.user_metadata?.full_name,
56+
avatar_url: data.user.user_metadata?.avatar_url,
57+
provider: "email",
58+
created_at: data.user.created_at,
59+
updated_at: data.user.updated_at || data.user.created_at,
60+
});
61+
}
62+
4663
router.push("/");
4764
} catch (error: unknown) {
4865
setError(error instanceof Error ? error.message : "An error occurred");
4966
} finally {
50-
setIsLoading(false);
67+
setLoading(false);
5168
}
5269
};
5370

5471
const handleOAuthLogin = async (provider: Provider) => {
55-
setIsLoading(true);
72+
setLoading(true);
5673
setError(null);
5774

5875
try {
59-
const { error } = await supabase.auth.signInWithOAuth({
76+
const { error: authError } = await supabase.auth.signInWithOAuth({
6077
provider,
6178
options: {
62-
redirectTo: `${window.location.origin}`,
79+
redirectTo: `${window.location.origin}/auth/callback`,
6380
},
6481
});
65-
if (error) throw error;
82+
if (authError) throw authError;
6683
} catch (error: unknown) {
6784
setError(error instanceof Error ? error.message : "An error occurred");
6885
} finally {
69-
setIsLoading(false);
86+
setLoading(false);
7087
}
7188
};
7289

@@ -98,6 +115,7 @@ const Login = () => {
98115
variant="outline"
99116
type="button"
100117
className="border-emerald-200 text-slate-700 hover:bg-emerald-50 hover:border-emerald-300 dark:border-violet-400/40 dark:text-neutral-300 dark:hover:bg-violet-500/10 dark:hover:border-violet-400"
118+
disabled={isLoading}
101119
>
102120
<Image
103121
width="24"
@@ -112,6 +130,7 @@ const Login = () => {
112130
variant="outline"
113131
type="button"
114132
className="border-emerald-200 text-slate-700 hover:bg-emerald-50 hover:border-emerald-300 dark:border-violet-400/40 dark:text-neutral-300 dark:hover:bg-violet-500/10 dark:hover:border-violet-400"
133+
disabled={isLoading}
115134
>
116135
<GoogleIcon className="size-8" />
117136
Login with Google
@@ -129,6 +148,7 @@ const Login = () => {
129148
required
130149
value={email}
131150
onChange={(e) => setEmail(e.target.value)}
151+
disabled={isLoading}
132152
/>
133153
</Field>
134154
<Field>
@@ -147,6 +167,7 @@ const Login = () => {
147167
required
148168
value={password}
149169
onChange={(e) => setPassword(e.target.value)}
170+
disabled={isLoading}
150171
/>
151172
</Field>
152173
{error && <p className="text-sm text-red-500">{error}</p>}

app/signup/page.tsx

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from "@/components/ui/field";
1212
import { Input } from "@/components/ui/input";
1313
import { createClient } from "@/lib/supabase/client";
14+
import { useUserStore } from "@/store/userStore";
1415
import type { Provider } from "@supabase/auth-js";
1516
import Image from "next/image";
1617
import Link from "next/link";
@@ -21,54 +22,68 @@ const Signup = () => {
2122
const [email, setEmail] = useState("");
2223
const [password, setPassword] = useState("");
2324
const [confirmPassword, setConfirmPassword] = useState("");
24-
const [error, setError] = useState<string | null>(null);
25-
const [isLoading, setIsLoading] = useState(false);
2625
const router = useRouter();
2726

27+
const { setUser, setLoading, setError, isLoading, error } = useUserStore();
2828
const supabase = createClient();
2929

3030
const handleSignup = async (e: React.FormEvent) => {
3131
e.preventDefault();
32-
setIsLoading(true);
32+
setLoading(true);
3333
setError(null);
3434

3535
if (password !== confirmPassword) {
3636
setError("Passwords do not match");
37-
setIsLoading(false);
37+
setLoading(false);
3838
return;
3939
}
4040

4141
try {
42-
const { error } = await supabase.auth.signUp({
42+
const { data, error: authError } = await supabase.auth.signUp({
4343
email,
4444
password,
4545
});
46-
if (error) throw error;
46+
47+
if (authError) throw authError;
48+
49+
if (data.user) {
50+
setUser({
51+
id: data.user.id,
52+
email: data.user.email!,
53+
name:
54+
data.user.user_metadata?.name || data.user.user_metadata?.full_name,
55+
avatar_url: data.user.user_metadata?.avatar_url,
56+
provider: "email",
57+
created_at: data.user.created_at,
58+
updated_at: data.user.updated_at || data.user.created_at,
59+
});
60+
}
61+
4762
router.push("/");
4863
} catch (error: unknown) {
4964
console.log(error);
5065
setError(error instanceof Error ? error.message : "An error occurred");
5166
} finally {
52-
setIsLoading(false);
67+
setLoading(false);
5368
}
5469
};
5570

5671
const handleOAuthLogin = async (provider: Provider) => {
57-
setIsLoading(true);
72+
setLoading(true);
5873
setError(null);
5974

6075
try {
61-
const { error } = await supabase.auth.signInWithOAuth({
76+
const { error: authError } = await supabase.auth.signInWithOAuth({
6277
provider,
6378
options: {
64-
redirectTo: `${window.location.origin}`,
79+
redirectTo: `${window.location.origin}/auth/callback`,
6580
},
6681
});
67-
if (error) throw error;
82+
if (authError) throw authError;
6883
} catch (error: unknown) {
6984
setError(error instanceof Error ? error.message : "An error occurred");
7085
} finally {
71-
setIsLoading(false);
86+
setLoading(false);
7287
}
7388
};
7489

@@ -102,6 +117,7 @@ const Signup = () => {
102117
required
103118
value={email}
104119
onChange={(e) => setEmail(e.target.value)}
120+
disabled={isLoading}
105121
/>
106122
</Field>
107123
<Field>
@@ -114,6 +130,7 @@ const Signup = () => {
114130
required
115131
value={password}
116132
onChange={(e) => setPassword(e.target.value)}
133+
disabled={isLoading}
117134
/>
118135
</Field>
119136
<Field>
@@ -126,6 +143,7 @@ const Signup = () => {
126143
required
127144
value={confirmPassword}
128145
onChange={(e) => setConfirmPassword(e.target.value)}
146+
disabled={isLoading}
129147
/>
130148
</Field>
131149
</Field>
@@ -151,6 +169,7 @@ const Signup = () => {
151169
variant="outline"
152170
type="button"
153171
onClick={() => handleOAuthLogin("github")}
172+
disabled={isLoading}
154173
className="border-emerald-200 text-slate-700 hover:bg-emerald-50 hover:border-emerald-300 dark:border-violet-400/40 dark:text-neutral-300 dark:hover:bg-violet-500/10 dark:hover:border-violet-400"
155174
>
156175
<Image
@@ -165,6 +184,7 @@ const Signup = () => {
165184
variant="outline"
166185
type="button"
167186
onClick={() => handleOAuthLogin("google")}
187+
disabled={isLoading}
168188
className="border-emerald-200 text-slate-700 hover:bg-emerald-50 hover:border-emerald-300 dark:border-violet-400/40 dark:text-neutral-300 dark:hover:bg-violet-500/10 dark:hover:border-violet-400"
169189
>
170190
<GoogleIcon className="size-8" />
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"use client";
2+
3+
import { useAuthStateChange } from "@/hooks/useAuthStateChange";
4+
5+
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
6+
useAuthStateChange();
7+
return <>{children}</>;
8+
};

components/NavbarComponents/AuthButtons.tsx

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,24 @@
11
"use client";
22

33
import { createClient } from "@/lib/supabase/client";
4+
import { useUser, useUserStore } from "@/store/userStore";
45
import Link from "next/link";
5-
import { useEffect, useState } from "react";
6+
import { useRouter } from "next/navigation";
67

78
const AuthButtons = () => {
9+
const user = useUser();
10+
const { clearUser } = useUserStore();
811
const supabase = createClient();
9-
const [user, setUser] = useState<string | null>(null);
10-
const [loading, setLoading] = useState(true);
11-
12-
useEffect(() => {
13-
const fetchUser = async () => {
14-
const { data } = await supabase.auth.getUser();
15-
16-
setUser(
17-
data?.user?.user_metadata?.name || data?.user?.email?.split("@")[0],
18-
);
19-
setLoading(false);
20-
};
21-
fetchUser();
22-
}, [supabase.auth]);
12+
const router = useRouter();
2313

2414
const handleLogout = async () => {
25-
const { error } = await supabase.auth.signOut();
26-
if (!error) setUser(null);
27-
else throw error;
15+
try {
16+
await supabase.auth.signOut();
17+
clearUser();
18+
router.push("/");
19+
} catch (error) {
20+
console.error("Error logging out:", error);
21+
}
2822
};
2923

3024
return (
@@ -35,9 +29,7 @@ const AuthButtons = () => {
3529
className="hidden sm:flex text-xs border border-emerald-500 dark:border-purple-500 px-3 py-1.5 rounded-md hover:bg-emerald-100 dark:hover:bg-violet-900 transition-colors duration-200 items-center gap-1.5"
3630
>
3731
<span>Logout</span>
38-
{!loading && (
39-
<span className="text-xs opacity-70">{`(${user})`}</span>
40-
)}
32+
<span className="text-xs opacity-70">{`(${user.email.split("@")[0]})`}</span>
4133
</button>
4234
) : (
4335
<Link

0 commit comments

Comments
 (0)