devjhawar commited on
Commit
dfd845a
Β·
verified Β·
1 Parent(s): fffb921

Upload folder using huggingface_hub

Browse files
frontend/src/App.jsx CHANGED
@@ -16,6 +16,12 @@ export default function App() {
16
  const [userName, setUserName] = useState('');
17
  const [authError, setAuthError] = useState('');
18
  const [initialPolicyId, setInitialPolicyId] = useState(null);
 
 
 
 
 
 
19
  const lastOAuthSyncKey = useRef('');
20
 
21
  const getRouteFromHash = (rawHash) => {
@@ -122,8 +128,13 @@ export default function App() {
122
 
123
  setUserName(nextName || '');
124
 
125
- const currentState = window.location.hash.replace('#', '') || 'home';
126
- if (currentState === 'login' || currentState === 'home') {
 
 
 
 
 
127
  redirectToDashboard();
128
  }
129
  };
@@ -131,21 +142,28 @@ export default function App() {
131
  const {
132
  data: { subscription },
133
  } = supabase.auth.onAuthStateChange((event, session) => {
134
- void syncSession(session, event);
 
 
135
  });
136
 
137
- void supabase.auth.getSession().then(({ data }) => syncSession(data.session));
 
 
 
 
138
 
139
  return () => subscription.unsubscribe();
140
  }, []);
141
 
142
  useEffect(() => {
143
  const hash = window.location.hash || '';
144
- const hasOAuthFragment = hash.includes('access_token=');
145
- const initialState = getRouteFromHash(hash);
 
146
 
147
  // Keep OAuth fragment untouched initially so Supabase can parse and persist session.
148
- if (!hasOAuthFragment) {
149
  window.history.replaceState({ appState: initialState }, '', `#${initialState}`);
150
  }
151
 
@@ -170,6 +188,26 @@ export default function App() {
170
  navigateTo('dboard');
171
  };
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  // 1. DASHBOARD ROUTE
174
  if (appState === 'dboard' || appState === 'dashboard') {
175
  return (
@@ -178,8 +216,17 @@ export default function App() {
178
  toggleTheme={toggleTheme}
179
  userName={userName}
180
  initialPolicyId={initialPolicyId}
181
- onLogout={() => {
 
 
 
 
182
  setUserName('');
 
 
 
 
 
183
  navigateTo('home');
184
  }}
185
  onTriggerUpload={() => navigateTo('upload')}
 
16
  const [userName, setUserName] = useState('');
17
  const [authError, setAuthError] = useState('');
18
  const [initialPolicyId, setInitialPolicyId] = useState(null);
19
+ const [isAuthResolving, setIsAuthResolving] = useState(() => {
20
+ const hash = window.location.hash || '';
21
+ const hasOAuthFragment = hash.includes('access_token=') || hash.includes('refresh_token=');
22
+ const hasOAuthIntent = localStorage.getItem('oauth_redirect_intent') === 'google';
23
+ return hasOAuthFragment || hasOAuthIntent;
24
+ });
25
  const lastOAuthSyncKey = useRef('');
26
 
27
  const getRouteFromHash = (rawHash) => {
 
128
 
129
  setUserName(nextName || '');
130
 
131
+ const currentHash = window.location.hash || '';
132
+ const currentState = getRouteFromHash(currentHash);
133
+ const fromOAuthCallback = currentHash.includes('access_token=') || currentHash.includes('refresh_token=');
134
+ const oauthIntent = localStorage.getItem('oauth_redirect_intent') === 'google';
135
+
136
+ if (currentState === 'login' || fromOAuthCallback || oauthIntent) {
137
+ localStorage.removeItem('oauth_redirect_intent');
138
  redirectToDashboard();
139
  }
140
  };
 
142
  const {
143
  data: { subscription },
144
  } = supabase.auth.onAuthStateChange((event, session) => {
145
+ void syncSession(session, event).finally(() => {
146
+ setIsAuthResolving(false);
147
+ });
148
  });
149
 
150
+ void supabase.auth.getSession()
151
+ .then(({ data }) => syncSession(data.session))
152
+ .finally(() => {
153
+ setIsAuthResolving(false);
154
+ });
155
 
156
  return () => subscription.unsubscribe();
157
  }, []);
158
 
159
  useEffect(() => {
160
  const hash = window.location.hash || '';
161
+ const hasOAuthFragment = hash.includes('access_token=') || hash.includes('refresh_token=');
162
+ const hasOAuthIntent = localStorage.getItem('oauth_redirect_intent') === 'google';
163
+ const initialState = (hasOAuthFragment || hasOAuthIntent) ? 'login' : getRouteFromHash(hash);
164
 
165
  // Keep OAuth fragment untouched initially so Supabase can parse and persist session.
166
+ if (!hasOAuthFragment && !hasOAuthIntent) {
167
  window.history.replaceState({ appState: initialState }, '', `#${initialState}`);
168
  }
169
 
 
188
  navigateTo('dboard');
189
  };
190
 
191
+ if (isAuthResolving) {
192
+ return (
193
+ <div
194
+ style={{
195
+ minHeight: '100vh',
196
+ display: 'flex',
197
+ alignItems: 'center',
198
+ justifyContent: 'center',
199
+ background: isDark ? '#0c0908' : '#f9fafb',
200
+ color: isDark ? '#ecfeff' : '#111827',
201
+ fontFamily: "'DM Sans', sans-serif",
202
+ fontSize: 14,
203
+ letterSpacing: 0.2,
204
+ }}
205
+ >
206
+ Completing sign-in...
207
+ </div>
208
+ );
209
+ }
210
+
211
  // 1. DASHBOARD ROUTE
212
  if (appState === 'dboard' || appState === 'dashboard') {
213
  return (
 
216
  toggleTheme={toggleTheme}
217
  userName={userName}
218
  initialPolicyId={initialPolicyId}
219
+ onLogout={async () => {
220
+ localStorage.removeItem('token');
221
+ localStorage.removeItem('oauth_redirect_intent');
222
+ lastOAuthSyncKey.current = '';
223
+ setAuthError('');
224
  setUserName('');
225
+ try {
226
+ await supabase.auth.signOut();
227
+ } catch (err) {
228
+ console.error('Sign out error:', err);
229
+ }
230
  navigateTo('home');
231
  }}
232
  onTriggerUpload={() => navigateTo('upload')}
frontend/src/components/Auth/Login.jsx CHANGED
@@ -765,6 +765,7 @@ export default function LoginPage({ onLoginSuccess, onGoogleSuccess, onSignupSuc
765
  setGoogleLoading(true);
766
 
767
  try {
 
768
  const redirectTo = `${window.location.origin}${window.location.pathname}`;
769
  const { data, error: oauthError } = await supabase.auth.signInWithOAuth({
770
  provider: 'google',
@@ -781,8 +782,8 @@ export default function LoginPage({ onLoginSuccess, onGoogleSuccess, onSignupSuc
781
  if (!data?.url) throw new Error('Unable to start Google sign-in flow.');
782
 
783
  window.location.assign(data.url);
784
- onGoogleSuccess?.();
785
  } catch (err) {
 
786
  triggerError(err.message || 'Google login failed. Please try again.');
787
  setGoogleLoading(false);
788
  }
 
765
  setGoogleLoading(true);
766
 
767
  try {
768
+ localStorage.setItem('oauth_redirect_intent', 'google');
769
  const redirectTo = `${window.location.origin}${window.location.pathname}`;
770
  const { data, error: oauthError } = await supabase.auth.signInWithOAuth({
771
  provider: 'google',
 
782
  if (!data?.url) throw new Error('Unable to start Google sign-in flow.');
783
 
784
  window.location.assign(data.url);
 
785
  } catch (err) {
786
+ localStorage.removeItem('oauth_redirect_intent');
787
  triggerError(err.message || 'Google login failed. Please try again.');
788
  setGoogleLoading(false);
789
  }
frontend/src/components/Dashboard/Dboard.jsx CHANGED
@@ -3,7 +3,7 @@ import { useState, useRef, useEffect, useCallback } from 'react';
3
  import {
4
  Upload, Clock, X, FileText, AlertTriangle, CheckCircle2,
5
  Shield, DollarSign, Calendar, Info, Trash2, Eye, FilePlus,
6
- Home, Aperture, Sun, Moon, Menu, BadgeCheck, Banknote,
7
  RefreshCw, Send, Loader2,
8
  } from 'lucide-react';
9
  import { fetchApi, streamApi } from '../../api';
@@ -144,7 +144,7 @@ function UploadZone({ T, dark, isDragging, uploading, uploadPct, onDragOver, onD
144
  }
145
 
146
  // ─────────────────────────────────────────────────────────────────────────────
147
- export default function Dboard({ file, isDark: _initDark, userName = 'My Account', initialPolicyId = null, onTriggerUpload }) {
148
  // Always boot light β€” user can toggle via sidebar
149
  const [dark, setDark] = useState(false);
150
  const [sidebarOpen, setSidebarOpen] = useState(true);
@@ -160,6 +160,7 @@ export default function Dboard({ file, isDark: _initDark, userName = 'My Account
160
  const [chatOpen, setChatOpen] = useState(false);
161
  const [chatInput, setChatInput] = useState('');
162
  const [isTyping, setIsTyping] = useState(false);
 
163
  const [chatDimensions, setChatDimensions] = useState({ w: 360, h: 500 });
164
  const [chatMessagesMap, setChatMessagesMap] = useState({
165
  default: [makeWelcomeMsg(file?.name ?? null)],
@@ -457,6 +458,16 @@ export default function Dboard({ file, isDark: _initDark, userName = 'My Account
457
  }
458
  };
459
 
 
 
 
 
 
 
 
 
 
 
460
  // ════════════════════════ SUB-COMPONENTS ══════════════════════════════════
461
 
462
  const IrisAvatar = ({ size=40 }) => (
@@ -722,6 +733,24 @@ export default function Dboard({ file, isDark: _initDark, userName = 'My Account
722
  </nav>
723
 
724
  <div style={{ padding:'12px 8px', borderTop:`1px solid ${T.headerBorder}` }}>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
725
  <button onClick={() => setDark(v => !v)}
726
  style={{ width:'100%', display:'flex', alignItems:'center', gap:12, padding: sidebarOpen ? '10px 14px' : '10px', justifyContent: sidebarOpen ? 'flex-start' : 'center', borderRadius:12, cursor:'pointer', background:T.cardBg, border:`1px solid ${T.cardBorder}`, color:T.t3, ...f, fontSize:13, fontWeight:500, boxShadow:T.cardShadow, transition:'all .2s' }}
727
  onMouseEnter={e => { e.currentTarget.style.borderColor=T.acc; e.currentTarget.style.color=T.acc; e.currentTarget.style.transform='scale(1.02)'; }}
 
3
  import {
4
  Upload, Clock, X, FileText, AlertTriangle, CheckCircle2,
5
  Shield, DollarSign, Calendar, Info, Trash2, Eye, FilePlus,
6
+ Home, Aperture, Sun, Moon, Menu, BadgeCheck, Banknote, LogOut,
7
  RefreshCw, Send, Loader2,
8
  } from 'lucide-react';
9
  import { fetchApi, streamApi } from '../../api';
 
144
  }
145
 
146
  // ─────────────────────────────────────────────────────────────────────────────
147
+ export default function Dboard({ file, isDark: _initDark, userName = 'My Account', initialPolicyId = null, onTriggerUpload, onLogout }) {
148
  // Always boot light β€” user can toggle via sidebar
149
  const [dark, setDark] = useState(false);
150
  const [sidebarOpen, setSidebarOpen] = useState(true);
 
160
  const [chatOpen, setChatOpen] = useState(false);
161
  const [chatInput, setChatInput] = useState('');
162
  const [isTyping, setIsTyping] = useState(false);
163
+ const [isSigningOut, setIsSigningOut] = useState(false);
164
  const [chatDimensions, setChatDimensions] = useState({ w: 360, h: 500 });
165
  const [chatMessagesMap, setChatMessagesMap] = useState({
166
  default: [makeWelcomeMsg(file?.name ?? null)],
 
458
  }
459
  };
460
 
461
+ const handleSignOut = async () => {
462
+ if (!onLogout || isSigningOut) return;
463
+ setIsSigningOut(true);
464
+ try {
465
+ await onLogout();
466
+ } finally {
467
+ setIsSigningOut(false);
468
+ }
469
+ };
470
+
471
  // ════════════════════════ SUB-COMPONENTS ══════════════════════════════════
472
 
473
  const IrisAvatar = ({ size=40 }) => (
 
733
  </nav>
734
 
735
  <div style={{ padding:'12px 8px', borderTop:`1px solid ${T.headerBorder}` }}>
736
+ <button onClick={handleSignOut}
737
+ disabled={!onLogout || isSigningOut}
738
+ style={{ width:'100%', display:'flex', alignItems:'center', gap:12, padding: sidebarOpen ? '10px 14px' : '10px', justifyContent: sidebarOpen ? 'flex-start' : 'center', borderRadius:12, cursor: !onLogout || isSigningOut ? 'not-allowed' : 'pointer', background:T.redBg, border:`1px solid ${T.redBrd}`, color:T.redClr, ...f, fontSize:13, fontWeight:600, transition:'all .2s', opacity: !onLogout || isSigningOut ? 0.6 : 1, marginBottom:8 }}
739
+ onMouseEnter={e => {
740
+ if (!isSigningOut && onLogout) {
741
+ e.currentTarget.style.filter = 'brightness(1.03)';
742
+ e.currentTarget.style.transform = 'scale(1.02)';
743
+ }
744
+ }}
745
+ onMouseLeave={e => {
746
+ e.currentTarget.style.filter = 'none';
747
+ e.currentTarget.style.transform = 'none';
748
+ }}
749
+ >
750
+ <LogOut size={15} style={{ flexShrink:0 }} />
751
+ {sidebarOpen && <span>{isSigningOut ? 'Signing Out...' : 'Sign Out'}</span>}
752
+ </button>
753
+
754
  <button onClick={() => setDark(v => !v)}
755
  style={{ width:'100%', display:'flex', alignItems:'center', gap:12, padding: sidebarOpen ? '10px 14px' : '10px', justifyContent: sidebarOpen ? 'flex-start' : 'center', borderRadius:12, cursor:'pointer', background:T.cardBg, border:`1px solid ${T.cardBorder}`, color:T.t3, ...f, fontSize:13, fontWeight:500, boxShadow:T.cardShadow, transition:'all .2s' }}
756
  onMouseEnter={e => { e.currentTarget.style.borderColor=T.acc; e.currentTarget.style.color=T.acc; e.currentTarget.style.transform='scale(1.02)'; }}