From ec52841119ce552cdc2099e2464ce60ddc55efac Mon Sep 17 00:00:00 2001 From: Arya Pratap Singh Date: Mon, 10 Feb 2025 17:25:27 +0530 Subject: [PATCH 1/4] fix: added proper state management and route verification of dashboard page Signed-off-by: Arya Pratap Singh --- components/AuthComponent/SignupForm.tsx | 28 ++++++++++++--- components/DashboardV2/V2Navbar.tsx | 45 +++++++++++++++++++++---- hooks/useAuth.ts | 33 ++++++++++++++++++ store/AuthStore/useAuthStore.ts | 19 +++++++---- 4 files changed, 106 insertions(+), 19 deletions(-) create mode 100644 hooks/useAuth.ts diff --git a/components/AuthComponent/SignupForm.tsx b/components/AuthComponent/SignupForm.tsx index 00d0ba2..f930b7a 100644 --- a/components/AuthComponent/SignupForm.tsx +++ b/components/AuthComponent/SignupForm.tsx @@ -1,7 +1,6 @@ "use client"; import React, { useState } from "react"; -import { useRouter } from "next/navigation"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import * as z from "zod"; @@ -32,10 +31,13 @@ import { CardTitle, } from "@/components/ui/card"; import { Alert, AlertDescription } from "@/components/ui/alert"; -import { useAuthStore } from "@/store/AuthStore/useAuthStore"; import AuthBottom from "./AuthBottom"; import LoadingButton from "./LoadingButton"; import { signupSchema } from "@/validations/validation"; +import { useEffect } from "react" +import { useRouter, useSearchParams } from "next/navigation" +import { useAuthStore } from "@/store/AuthStore/useAuthStore" +import { useAuth } from "@/hooks/useAuth"; type SignupFormValues = z.infer; @@ -43,6 +45,9 @@ export default function SignupForm() { const { isSigningUp, signup, signupError } = useAuthStore(); const router = useRouter(); const [showPassword, setShowPassword] = useState(false); + const { isSigningIn, signin, signinError } = useAuthStore() + const { user, loading } = useAuth() + const searchParams = useSearchParams() const form = useForm({ resolver: zodResolver(signupSchema), @@ -55,9 +60,22 @@ export default function SignupForm() { }, }); - const onSubmit = (data: SignupFormValues) => { - signup(data, router); - }; + useEffect(() => { + // If user is already authenticated, redirect to the intended URL or dashboard + if (user && !loading) { + const redirectTo = searchParams.get('redirect') || '/dashboard' + router.push(redirectTo) + } + }, [user, loading, router, searchParams]) + + const onSubmit = async (data: SignupFormValues) => { + try { + signup(data, router); + // The redirect will be handled by the useEffect above when the user state updates + } catch (error) { + console.error('Sign in error:', error) + } + } return (
diff --git a/components/DashboardV2/V2Navbar.tsx b/components/DashboardV2/V2Navbar.tsx index f5a858d..a80f9c1 100644 --- a/components/DashboardV2/V2Navbar.tsx +++ b/components/DashboardV2/V2Navbar.tsx @@ -23,6 +23,9 @@ import Link from "next/link"; import Image from "next/image"; import { ToggleTheme } from "./ToggleTheme"; import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; +import { useAuth } from "@/hooks/useAuth"; +import { signout } from "@/app/actions/action"; +import { useRouter } from "next/navigation"; interface RouteProps { href: string; @@ -72,6 +75,38 @@ const featureList: FeatureProps[] = [ export const V2Navbar = () => { const [isOpen, setIsOpen] = React.useState(false); + const { user, loading } = useAuth(); + const router = useRouter(); + + const handleSignOut = async () => { + await signout(); + router.push('/auth/signin'); + }; + + const renderAuthButtons = () => { + if (loading) return null; + + if (user) { + return ( + + ); + } + + return ( + <> + + + + + + + + ); + }; + + return (
@@ -127,6 +162,8 @@ export const V2Navbar = () => { + + {renderAuthButtons()} @@ -181,13 +218,7 @@ export const V2Navbar = () => {
- - - - - - - + {renderAuthButtons()}
); diff --git a/hooks/useAuth.ts b/hooks/useAuth.ts new file mode 100644 index 0000000..742e6e2 --- /dev/null +++ b/hooks/useAuth.ts @@ -0,0 +1,33 @@ +import { useEffect, useState } from 'react'; +import { createClient } from '@/utils/supabase/client'; + +import { User } from '@supabase/supabase-js'; + +export function useAuth() { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + const supabase = createClient(); + + useEffect(() => { + // Check active sessions and sets the user + const getUser = async () => { + const { data: { user }, error } = await supabase.auth.getUser(); + setUser(user); + setLoading(false); + }; + + getUser(); + + // Listen for changes on auth state (login, logout, etc) + const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => { + setUser(session?.user ?? null); + setLoading(false); + }); + + return () => { + subscription.unsubscribe(); + }; + }, []); + + return { user, loading }; +} \ No newline at end of file diff --git a/store/AuthStore/useAuthStore.ts b/store/AuthStore/useAuthStore.ts index ff27af9..d1ba178 100644 --- a/store/AuthStore/useAuthStore.ts +++ b/store/AuthStore/useAuthStore.ts @@ -15,13 +15,11 @@ interface authStore { isSigningIn: boolean; signinError: string | null; signin: (signinMetaData: { email: string, password: string },router: any) => void; - logout: () => void; - + logout: (router: any) => void; signupError: string | null; isSigningUp: boolean; signup: (signupMetaData: User,router: any) => void; user: User | null; - authUserLoading: boolean; fetchAuthUser: () => void; authUser: User | null; @@ -32,7 +30,7 @@ export const useAuthStore = create((set) => ({ isSigningIn: false, signin: async (signinMetaData,router) => { const supabase = createClient() - set({ isSigningIn: true }) + set({ isSigningIn: true, signinError: null }) try { const { data, error: loginError } = await supabase.auth.signInWithPassword(signinMetaData); @@ -44,7 +42,8 @@ export const useAuthStore = create((set) => ({ } if (data.session) { - router.push("/dashboard"); + // Ensure we have a session before redirecting + await router.push('/dashboard'); } else { throw new Error("Unable to retrieve session after login."); } @@ -56,8 +55,14 @@ export const useAuthStore = create((set) => ({ } }, - logout: () => { - console.log('logout'); + logout: async (router) => { + const supabase = createClient() + try { + await supabase.auth.signOut(); + router.push('/auth/signin'); + } catch (error) { + console.error('Logout error:', error); + } }, signupError: null, From 1e2c8489bfb4df63e0e722b9a824a47bad217396 Mon Sep 17 00:00:00 2001 From: Arya Pratap Singh Date: Mon, 10 Feb 2025 17:36:52 +0530 Subject: [PATCH 2/4] fixture Signed-off-by: Arya Pratap Singh --- components/DashboardV2/V2Navbar.tsx | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/components/DashboardV2/V2Navbar.tsx b/components/DashboardV2/V2Navbar.tsx index a80f9c1..a7f2e31 100644 --- a/components/DashboardV2/V2Navbar.tsx +++ b/components/DashboardV2/V2Navbar.tsx @@ -77,24 +77,25 @@ export const V2Navbar = () => { const [isOpen, setIsOpen] = React.useState(false); const { user, loading } = useAuth(); const router = useRouter(); + const [authState, setAuthState] = React.useState({ user, loading }); + + React.useEffect(() => { + setAuthState({ user, loading }); + }, [user, loading]); const handleSignOut = async () => { await signout(); - router.push('/auth/signin'); + router.push("/auth/signin"); }; const renderAuthButtons = () => { - if (loading) return null; - - if (user) { - return ( - - ); - } + if (authState.loading) return null; - return ( + return authState.user ? ( + + ) : ( <> From 033b26e5397e2aaf3424b71021d421ad0d684bac Mon Sep 17 00:00:00 2001 From: Arya Pratap Singh Date: Mon, 10 Feb 2025 22:06:21 +0530 Subject: [PATCH 3/4] fixture Signed-off-by: Arya Pratap Singh --- components/DashboardV2/V2Navbar.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/components/DashboardV2/V2Navbar.tsx b/components/DashboardV2/V2Navbar.tsx index a7f2e31..ac2bff8 100644 --- a/components/DashboardV2/V2Navbar.tsx +++ b/components/DashboardV2/V2Navbar.tsx @@ -1,6 +1,6 @@ "use client"; import { ChevronsDown, Github, Menu } from "lucide-react"; -import React from "react"; +import React , { Suspense } from "react"; import { Sheet, SheetContent, @@ -73,7 +73,7 @@ const featureList: FeatureProps[] = [ }, ]; -export const V2Navbar = () => { +export const NavigationContent = () => { const [isOpen, setIsOpen] = React.useState(false); const { user, loading } = useAuth(); const router = useRouter(); @@ -224,3 +224,11 @@ export const V2Navbar = () => { ); }; + +export const V2Navbar = () => { + return ( + Loading...}> + + + ); +}; \ No newline at end of file From 9abf37da640e31923ab13158cafdca9cb5a93c62 Mon Sep 17 00:00:00 2001 From: Arya Pratap Singh Date: Wed, 12 Feb 2025 16:02:36 +0530 Subject: [PATCH 4/4] full-fixed Signed-off-by: Arya Pratap Singh --- app/auth/register/page.tsx | 11 ++++++-- .../AuthComponent/SearchParamsWrapper.tsx | 27 +++++++++++++++++++ components/AuthComponent/SignupForm.tsx | 10 ++++--- 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 components/AuthComponent/SearchParamsWrapper.tsx diff --git a/app/auth/register/page.tsx b/app/auth/register/page.tsx index 4b38104..ee9a4dd 100644 --- a/app/auth/register/page.tsx +++ b/app/auth/register/page.tsx @@ -1,9 +1,16 @@ +import { Suspense } from "react"; import SignupForm from "@/components/AuthComponent/SignupForm"; +import SearchParamsWrapper from "@/components/AuthComponent/SearchParamsWrapper"; + export default function SignupPage() { return (
- + Loading...
}> + + + + ); -} +} \ No newline at end of file diff --git a/components/AuthComponent/SearchParamsWrapper.tsx b/components/AuthComponent/SearchParamsWrapper.tsx new file mode 100644 index 0000000..ebd1db9 --- /dev/null +++ b/components/AuthComponent/SearchParamsWrapper.tsx @@ -0,0 +1,27 @@ +"use client"; + +import { useSearchParams } from "next/navigation"; +import React from "react"; + +interface ChildProps { + searchParams?: ReturnType; +} + +export default function SearchParamsWrapper({ + children +}: { + children: React.ReactNode +}) { + const searchParams = useSearchParams(); + + return ( + <> + {React.Children.map(children, child => { + if (React.isValidElement(child)) { + return React.cloneElement(child, { searchParams }); + } + return child; + })} + + ); +} \ No newline at end of file diff --git a/components/AuthComponent/SignupForm.tsx b/components/AuthComponent/SignupForm.tsx index f930b7a..7fa8367 100644 --- a/components/AuthComponent/SignupForm.tsx +++ b/components/AuthComponent/SignupForm.tsx @@ -41,13 +41,17 @@ import { useAuth } from "@/hooks/useAuth"; type SignupFormValues = z.infer; -export default function SignupForm() { +interface SignupFormProps { + searchParams?: URLSearchParams; +} + +export default function SignupForm({searchParams}: SignupFormProps) { const { isSigningUp, signup, signupError } = useAuthStore(); const router = useRouter(); const [showPassword, setShowPassword] = useState(false); const { isSigningIn, signin, signinError } = useAuthStore() const { user, loading } = useAuth() - const searchParams = useSearchParams() + const form = useForm({ resolver: zodResolver(signupSchema), @@ -63,7 +67,7 @@ export default function SignupForm() { useEffect(() => { // If user is already authenticated, redirect to the intended URL or dashboard if (user && !loading) { - const redirectTo = searchParams.get('redirect') || '/dashboard' + const redirectTo = searchParams?.get('redirect') || '/dashboard' router.push(redirectTo) } }, [user, loading, router, searchParams])