import { useState, useEffect, createContext, useContext, useMemo, useCallback } from "react" import { Routes, Route } from "react-router-dom" import { MainLayout } from "./components/layout/MainLayout" import { LoginForm } from "./components/auth/LoginForm" import { RegisterForm } from "./components/auth/RegisterForm" import { ChatInterface } from "./components/chat/ChatInterface" import { AuthService, type UserResponse } from "./services/auth" import { ChatService, type MessageResponse } from "./services/chat" import { type Conversation } from "./components/layout/HistorySidebar" import { registerUnauthorizedCallback } from "./services/api" import { Button } from "./components/ui/button" import { ThemeProvider, useTheme } from "./components/theme-provider" // --- Auth Context --- interface AuthContextType { isAuthenticated: boolean setIsAuthenticated: (val: boolean) => void user: UserResponse | null setUser: (user: UserResponse | null) => void } const AuthContext = createContext(undefined) function AuthProvider({ children }: { children: React.ReactNode }) { const [isAuthenticated, setIsAuthenticated] = useState(false) const [user, setUser] = useState(null) const value = useMemo(() => ({ isAuthenticated, setIsAuthenticated, user, setUser }), [isAuthenticated, user]) return ( {children} ) } function useAuth() { const context = useContext(AuthContext) if (context === undefined) { throw new Error("useAuth must be used within an AuthProvider") } return context } // --- App Content --- function AppContent() { const { setThemeLocal } = useTheme() const { isAuthenticated, setIsAuthenticated, user, setUser } = useAuth() const [authMode, setAuthMode] = useState<"login" | "register">("login") const [isLoading, setIsLoading] = useState(true) const [selectedThreadId, setSelectedThreadId] = useState(null) const [conversations, setConversations] = useState([]) const [threadMessages, setThreadMessages] = useState>({}) useEffect(() => { registerUnauthorizedCallback(() => { setIsAuthenticated(false) setUser(null) setConversations([]) setSelectedThreadId(null) setThreadMessages({}) setThemeLocal("light") }) const initAuth = async () => { try { const userData = await AuthService.getMe() setUser(userData) setIsAuthenticated(true) if (userData.theme_preference) { setThemeLocal(userData.theme_preference) } loadHistory() } catch (err: unknown) { console.log("No active session found", err) setIsAuthenticated(false) } finally { setIsLoading(false) } } initAuth() }, [setIsAuthenticated, setThemeLocal, setUser]) const loadHistory = useCallback(async () => { try { const history = await ChatService.listConversations() setConversations(history) } catch (err) { console.error("Failed to load conversation history:", err) } }, []) const handleAuthSuccess = useCallback(async () => { try { const userData = await AuthService.getMe() setUser(userData) setIsAuthenticated(true) if (userData.theme_preference) { setThemeLocal(userData.theme_preference) } loadHistory() } catch (err: unknown) { console.error("Failed to fetch user profile after login:", err) } }, [setUser, setIsAuthenticated, setThemeLocal, loadHistory]) const handleLogout = useCallback(async () => { try { await AuthService.logout() } catch (err: unknown) { console.error("Logout failed:", err) } finally { setIsAuthenticated(false) setUser(null) setSelectedThreadId(null) setConversations([]) setThreadMessages({}) setThemeLocal("light") } }, [setIsAuthenticated, setUser, setThemeLocal]) const handleSelectConversation = useCallback(async (id: string) => { setSelectedThreadId(id) try { const msgs = await ChatService.getMessages(id) setThreadMessages(prev => ({ ...prev, [id]: msgs })) } catch (err) { console.error("Failed to fetch messages:", err) } }, []) const handleCreateConversation = useCallback(async () => { try { const newConv = await ChatService.createConversation() setConversations(prev => [newConv, ...prev]) setSelectedThreadId(newConv.id) setThreadMessages(prev => ({ ...prev, [newConv.id]: [] })) } catch (err) { console.error("Failed to create conversation:", err) } }, []) const handleRenameConversation = useCallback(async (id: string, name: string) => { try { const updated = await ChatService.renameConversation(id, name) setConversations(prev => prev.map(c => c.id === id ? updated : c)) } catch (err) { console.error("Failed to rename conversation:", err) } }, []) const handleDeleteConversation = useCallback(async (id: string) => { try { await ChatService.deleteConversation(id) setConversations(prev => prev.filter(c => c.id !== id)) setThreadMessages(prev => { const next = { ...prev } delete next[id] return next }) if (selectedThreadId === id) { setSelectedThreadId(null) } } catch (err) { console.error("Failed to delete conversation:", err) } }, [selectedThreadId]) const handleMessagesFinal = useCallback((id: string, messages: MessageResponse[]) => { setThreadMessages(prev => ({ ...prev, [id]: messages })) }, []) const queryParams = new URLSearchParams(window.location.search) const externalError = queryParams.get("error") if (isLoading) { return (
) } return ( {authMode === "login" ? ( setAuthMode("register")} externalError={externalError === "oidc_failed" ? "SSO authentication failed. Please try again." : null} /> ) : ( setAuthMode("login")} /> )} ) : (

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

{selectedThreadId ? ( handleMessagesFinal(selectedThreadId, msgs)} /> ) : (

Ready to analyze election data?

Create a new conversation in the sidebar to start asking questions.

)}
) } />
) } function ThemeWrapper({ children }: { children: React.ReactNode }) { const { isAuthenticated } = useAuth() return ( {children} ) } function App() { return ( ) } export default App