diff --git a/frontend/.vite/deps/_metadata.json b/frontend/.vite/deps/_metadata.json new file mode 100644 index 0000000..28627fd --- /dev/null +++ b/frontend/.vite/deps/_metadata.json @@ -0,0 +1,8 @@ +{ + "hash": "564e878b", + "configHash": "9fae4597", + "lockfileHash": "a2b50863", + "browserHash": "09c4652f", + "optimized": {}, + "chunks": {} +} \ No newline at end of file diff --git a/frontend/.vite/deps/package.json b/frontend/.vite/deps/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/frontend/.vite/deps/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 10239de..88e7395 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,23 +2,66 @@ import { useState, useEffect } from "react" import { MainLayout } from "./components/layout/MainLayout" import { LoginForm } from "./components/auth/LoginForm" import { RegisterForm } from "./components/auth/RegisterForm" -import { AuthService } from "./services/auth" +import { AuthService, type UserResponse } from "./services/auth" function App() { const [isAuthenticated, setIsAuthenticated] = useState(false) + const [user, setUser] = useState(null) const [authMode, setAuthMode] = useState<"login" | "register">("login") + const [isLoading, setIsLoading] = useState(true) useEffect(() => { - setIsAuthenticated(AuthService.isAuthenticated()) + const initAuth = async () => { + // Check for token in URL (OIDC callback) + const urlParams = new URLSearchParams(window.location.search) + const tokenFromUrl = urlParams.get("token") + if (tokenFromUrl) { + localStorage.setItem("token", tokenFromUrl) + // Clean URL + window.history.replaceState({}, document.title, "/") + } + + const authStatus = AuthService.isAuthenticated() + setIsAuthenticated(authStatus) + + if (authStatus) { + try { + const userData = await AuthService.getMe() + setUser(userData) + } catch (err) { + console.error("Failed to fetch user profile:", err) + AuthService.logout() + setIsAuthenticated(false) + } + } + setIsLoading(false) + } + + initAuth() }, []) - const handleAuthSuccess = () => { + const handleAuthSuccess = async () => { setIsAuthenticated(true) + try { + const userData = await AuthService.getMe() + setUser(userData) + } catch (err) { + console.error("Failed to fetch user profile after login:", err) + } } const handleLogout = () => { AuthService.logout() setIsAuthenticated(false) + setUser(null) + } + + if (isLoading) { + return ( +
+
+
+ ) } if (!isAuthenticated) { @@ -43,7 +86,10 @@ function App() {
-

Welcome to Election Analytics

+
+

Welcome, {user?.display_name || user?.email || "User"}!

+

{user?.email}

+
-

+

Select a conversation from the sidebar or start a new one to begin your analysis.

diff --git a/frontend/src/components/auth/LoginForm.tsx b/frontend/src/components/auth/LoginForm.tsx index 6ebf3b4..8c7db16 100644 --- a/frontend/src/components/auth/LoginForm.tsx +++ b/frontend/src/components/auth/LoginForm.tsx @@ -14,6 +14,7 @@ import { import { Input } from "@/components/ui/input" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { useState } from "react" +import { Separator } from "@/components/ui/separator" interface LoginFormProps { onSuccess: () => void @@ -45,6 +46,14 @@ export function LoginForm({ onSuccess, onToggleMode }: LoginFormProps) { } } + const handleOIDCLogin = async () => { + try { + await AuthService.loginWithOIDC() + } catch (err) { + setError("Failed to initialize SSO login.") + } + } + return ( @@ -90,6 +99,20 @@ export function LoginForm({ onSuccess, onToggleMode }: LoginFormProps) { + +
+
+ +
+
+ Or continue with +
+
+ + +
Don't have an account?{" "}