import { useState, useEffect } 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" function App() { const [isAuthenticated, setIsAuthenticated] = useState(false) const [user, setUser] = useState(null) 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(() => { // Register callback to handle session expiration from anywhere in the app registerUnauthorizedCallback(() => { setIsAuthenticated(false) setUser(null) setConversations([]) setSelectedThreadId(null) setThreadMessages({}) }) const initAuth = async () => { try { const userData = await AuthService.getMe() setUser(userData) setIsAuthenticated(true) // Load history after successful auth loadHistory() } catch (err: unknown) { console.log("No active session found", err) setIsAuthenticated(false) } finally { setIsLoading(false) } } initAuth() }, []) const loadHistory = async () => { try { const history = await ChatService.listConversations() setConversations(history) } catch (err) { console.error("Failed to load conversation history:", err) } } const handleAuthSuccess = async () => { try { const userData = await AuthService.getMe() setUser(userData) setIsAuthenticated(true) loadHistory() } catch (err: unknown) { console.error("Failed to fetch user profile after login:", err) } } const handleLogout = async () => { try { await AuthService.logout() } catch (err: unknown) { console.error("Logout failed:", err) } finally { setIsAuthenticated(false) setUser(null) setSelectedThreadId(null) setConversations([]) setThreadMessages({}) } } const handleSelectConversation = async (id: string) => { setSelectedThreadId(id) // Always fetch messages to avoid stale cache issues when switching back // or if the session was updated from elsewhere try { const msgs = await ChatService.getMessages(id) setThreadMessages(prev => ({ ...prev, [id]: msgs })) } catch (err) { console.error("Failed to fetch messages:", err) } } const handleCreateConversation = 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 = 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 = async (id: string) => { try { await ChatService.deleteConversation(id) setConversations(prev => prev.filter(c => c.id !== id)) // Also clear from cache setThreadMessages(prev => { const next = { ...prev } delete next[id] return next }) if (selectedThreadId === id) { setSelectedThreadId(null) } } catch (err) { console.error("Failed to delete conversation:", err) } } const handleMessagesFinal = (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.

)}
) } />
) } export default App