Skip to content

Commit 9ae38f0

Browse files
Implement authentication flow Zustand
1 parent 164cd09 commit 9ae38f0

File tree

14 files changed

+299
-64
lines changed

14 files changed

+299
-64
lines changed

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/generate/page.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export default function UploadPage() {
8686
setError(null);
8787
setSvgResult(null);
8888
},
89-
[setError, setUploadedFiles, setSvgResult]
89+
[setError, setUploadedFiles, setSvgResult],
9090
);
9191

9292
const handleDrop = useCallback(
@@ -99,7 +99,7 @@ export default function UploadPage() {
9999
handleFiles(files);
100100
}
101101
},
102-
[handleFiles]
102+
[handleFiles],
103103
);
104104

105105
const handleDragOver = useCallback((e: React.DragEvent<HTMLDivElement>) => {
@@ -181,7 +181,7 @@ export default function UploadPage() {
181181
"Sample Description",
182182
"https://example.com/image.png",
183183
"https://example.com/image.svg",
184-
svgResult || ""
184+
svgResult || "",
185185
);
186186
}
187187
};
@@ -232,7 +232,7 @@ export default function UploadPage() {
232232
if (!/viewBox=/.test(svg)) {
233233
svg = svg.replace(
234234
/<svg([^>]*)>/,
235-
`<svg$1 viewBox="0 0 ${width} ${height}">`
235+
`<svg$1 viewBox="0 0 ${width} ${height}">`,
236236
);
237237
}
238238
}
@@ -407,8 +407,8 @@ export default function UploadPage() {
407407
{file.progress === 0
408408
? "Ready to convert"
409409
: file.progress < 100
410-
? `Converting... ${file.progress}%`
411-
: "Conversion complete!"}
410+
? `Converting... ${file.progress}%`
411+
: "Conversion complete!"}
412412
</span>
413413
{file.progress === 100 && !isUploading && (
414414
<motion.div

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+
};

0 commit comments

Comments
 (0)