gaurv007 commited on
Commit
1cba5dc
·
verified ·
1 Parent(s): 49131bb

fix(nav): rewrite auth logic — set loaded=true on SIGNED_IN immediately, fetch profile in background

Browse files
Files changed (1) hide show
  1. web/components/nav.tsx +90 -67
web/components/nav.tsx CHANGED
@@ -8,7 +8,7 @@ import {
8
  ScanText, Settings, LogIn, Zap, Sparkles, CreditCard, UserCircle,
9
  LogOut, Users
10
  } from "lucide-react";
11
- import { useState, useEffect, useCallback } from "react";
12
  import { createClient } from "@/lib/supabase/client";
13
 
14
  interface NavLink {
@@ -28,80 +28,103 @@ export function Nav() {
28
  const isAdmin = userRole === "admin";
29
  const hasTeam = !!userTeam;
30
 
31
- // FIX v4.3: Extracted into a reusable function so it can be called
32
- // both on mount AND when Supabase fires auth state changes.
33
- const refreshAuth = useCallback(async () => {
34
- try {
35
- const supabase = createClient();
36
- const { data: { user }, error } = await supabase.auth.getUser();
37
-
38
- if (error || !user) {
39
- console.log("[ClauseGuard Nav] No user session:", error?.message || "not logged in");
40
- setUserEmail(null);
41
- setUserRole(null);
42
- setUserTeam(null);
43
- setLoaded(true);
44
- return;
45
- }
46
 
47
- setUserEmail(user.email || null);
48
-
49
- // FIX v4.3: Properly check Supabase error response (it doesn't throw)
50
- const { data: profile, error: profileError } = await supabase
51
- .from("profiles")
52
- .select("role, team_id")
53
- .eq("id", user.id)
54
- .single();
55
-
56
- if (profileError) {
57
- console.error("[ClauseGuard Nav] Profile fetch error:", profileError.message, profileError.code);
58
- setUserRole("user");
59
- setUserTeam(null);
60
- } else {
61
- console.log("[ClauseGuard Nav] Profile loaded:", { role: profile?.role, team_id: profile?.team_id });
62
- setUserRole(profile?.role || "user");
63
- setUserTeam(profile?.team_id || null);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
65
- } catch {
66
- // Supabase client init error (env vars missing)
67
  }
68
- setLoaded(true);
69
- }, []);
70
 
71
- useEffect(() => {
72
- // Initial auth check
73
- refreshAuth();
74
-
75
- // FIX v4.3: Listen to Supabase onAuthStateChange.
76
- // This fires on SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED, etc.
77
- // Without this, Nav never updates after login because the useEffect
78
- // only ran once on mount with [] deps.
79
- let subscription: { unsubscribe: () => void } | null = null;
80
- try {
81
- const supabase = createClient();
82
- const { data } = supabase.auth.onAuthStateChange((event) => {
83
- console.log("[ClauseGuard Nav] Auth state changed:", event);
84
- // Re-fetch on any auth event
85
- if (event === "SIGNED_IN" || event === "SIGNED_OUT" || event === "TOKEN_REFRESHED" || event === "INITIAL_SESSION") {
86
- refreshAuth();
87
  }
88
- });
89
- subscription = data.subscription;
90
- } catch {
91
- // env vars missing
92
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  return () => {
95
- subscription?.unsubscribe();
 
96
  };
97
- }, [refreshAuth]);
98
-
99
- // FIX v4.3: Also re-check when pathname changes (covers router.push after login)
100
- useEffect(() => {
101
- if (loaded) {
102
- refreshAuth();
103
- }
104
- }, [pathname, loaded, refreshAuth]);
105
 
106
  async function handleSignOut() {
107
  try {
 
8
  ScanText, Settings, LogIn, Zap, Sparkles, CreditCard, UserCircle,
9
  LogOut, Users
10
  } from "lucide-react";
11
+ import { useState, useEffect } from "react";
12
  import { createClient } from "@/lib/supabase/client";
13
 
14
  interface NavLink {
 
28
  const isAdmin = userRole === "admin";
29
  const hasTeam = !!userTeam;
30
 
31
+ useEffect(() => {
32
+ let cancelled = false;
33
+ const supabase = createClient();
34
+
35
+ async function checkAuth() {
36
+ try {
37
+ const { data: { session } } = await supabase.auth.getSession();
38
+
39
+ if (cancelled) return;
 
 
 
 
 
 
40
 
41
+ if (!session?.user) {
42
+ setUserEmail(null);
43
+ setUserRole(null);
44
+ setUserTeam(null);
45
+ if (!loaded) setLoaded(true);
46
+ return;
47
+ }
48
+
49
+ const user = session.user;
50
+ setUserEmail(user.email || null);
51
+
52
+ // Fetch profile for role/team — use getSession instead of getUser
53
+ // to avoid extra network call
54
+ const { data: profile, error: profileError } = await supabase
55
+ .from("profiles")
56
+ .select("role, team_id")
57
+ .eq("id", user.id)
58
+ .single();
59
+
60
+ if (cancelled) return;
61
+
62
+ if (profileError) {
63
+ console.error("[ClauseGuard Nav] Profile error:", profileError.message);
64
+ // Even if profile fails, still show the user as logged in
65
+ setUserRole("user");
66
+ setUserTeam(null);
67
+ } else {
68
+ setUserRole(profile?.role || "user");
69
+ setUserTeam(profile?.team_id || null);
70
+ }
71
+ } catch (err) {
72
+ console.error("[ClauseGuard Nav] Auth check error:", err);
73
+ if (!cancelled) {
74
+ setUserEmail(null);
75
+ setUserRole(null);
76
+ setUserTeam(null);
77
+ }
78
  }
79
+ if (!cancelled) setLoaded(true);
 
80
  }
 
 
81
 
82
+ // Check auth immediately
83
+ checkAuth();
84
+
85
+ // Listen for auth changes (login, logout, token refresh)
86
+ const { data: { subscription } } = supabase.auth.onAuthStateChange(
87
+ (event, session) => {
88
+ console.log("[ClauseGuard Nav] Auth event:", event);
89
+
90
+ if (event === "SIGNED_OUT" || !session?.user) {
91
+ setUserEmail(null);
92
+ setUserRole(null);
93
+ setUserTeam(null);
94
+ setLoaded(true);
95
+ return;
 
 
96
  }
97
+
98
+ if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED" || event === "INITIAL_SESSION") {
99
+ // We have a session — update email immediately, then fetch profile
100
+ setUserEmail(session.user.email || null);
101
+ setLoaded(true); // Show logged-in state immediately (even before profile loads)
102
+
103
+ // Fetch profile in background
104
+ supabase
105
+ .from("profiles")
106
+ .select("role, team_id")
107
+ .eq("id", session.user.id)
108
+ .single()
109
+ .then(({ data: profile, error }) => {
110
+ if (cancelled) return;
111
+ if (error) {
112
+ console.error("[ClauseGuard Nav] Profile error:", error.message);
113
+ setUserRole("user");
114
+ } else {
115
+ setUserRole(profile?.role || "user");
116
+ setUserTeam(profile?.team_id || null);
117
+ }
118
+ });
119
+ }
120
+ }
121
+ );
122
 
123
  return () => {
124
+ cancelled = true;
125
+ subscription.unsubscribe();
126
  };
127
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
 
 
 
 
 
 
 
128
 
129
  async function handleSignOut() {
130
  try {