import React, { useState, useEffect, useCallback } from 'react'; import { Chat, Message, ChatState } from './types'; import { createChat, getChats, getMessages, sendMessage, deleteChat } from './services/api'; import ChatSidebar from './components/ChatSidebar'; import ChatWindow from './components/ChatWindow'; import DocumentsModal from './components/DocumentsModal'; const App: React.FC = () => { const [state, setState] = useState({ chats: [], activeChat: null, messages: [], isLoading: false, isSending: false, error: null, }); const [isDocsModalOpen, setIsDocsModalOpen] = useState(false); // Listen for custom event to open docs modal useEffect(() => { const handleOpenModal = () => setIsDocsModalOpen(true); window.addEventListener('open-documents-modal', handleOpenModal); return () => window.removeEventListener('open-documents-modal', handleOpenModal); }, []); // Load chats on mount useEffect(() => { loadChats(); }, []); // Load all chats from API const loadChats = async () => { try { setState(prev => ({ ...prev, isLoading: true, error: null })); const chats = await getChats(); setState(prev => ({ ...prev, chats, isLoading: false })); } catch (err) { setState(prev => ({ ...prev, isLoading: false, error: err instanceof Error ? err.message : 'Failed to load chats', })); } }; // Load messages for a chat const loadMessages = async (chatId: string) => { try { setState(prev => ({ ...prev, isLoading: true, error: null })); const messages = await getMessages(chatId); setState(prev => ({ ...prev, messages, isLoading: false })); } catch (err) { setState(prev => ({ ...prev, isLoading: false, error: err instanceof Error ? err.message : 'Failed to load messages', })); } }; // Handle selecting a chat const handleSelectChat = useCallback(async (chat: Chat) => { setState(prev => ({ ...prev, activeChat: chat, messages: [] })); await loadMessages(chat.id); }, []); // Handle creating a new chat const handleNewChat = useCallback(async () => { try { setState(prev => ({ ...prev, isLoading: true, error: null })); const chat = await createChat(); setState(prev => ({ ...prev, chats: [chat, ...prev.chats], activeChat: chat, messages: [], isLoading: false, })); } catch (err) { setState(prev => ({ ...prev, isLoading: false, error: err instanceof Error ? err.message : 'Failed to create chat', })); } }, []); // Handle deleting a chat const handleDeleteChat = useCallback(async (chatId: string) => { try { await deleteChat(chatId); setState(prev => { const newChats = prev.chats.filter(c => c.id !== chatId); const newActiveChat = prev.activeChat?.id === chatId ? null : prev.activeChat; const newMessages = prev.activeChat?.id === chatId ? [] : prev.messages; return { ...prev, chats: newChats, activeChat: newActiveChat, messages: newMessages, }; }); } catch (err) { setState(prev => ({ ...prev, error: err instanceof Error ? err.message : 'Failed to delete chat', })); } }, []); // Handle sending a message const handleSendMessage = useCallback(async (content: string) => { if (!state.activeChat) return; const chatId = state.activeChat.id; // Optimistically add user message const tempUserMsg: Message = { id: `temp-${Date.now()}`, chat_id: chatId, role: 'user', content, timestamp: new Date().toISOString(), }; setState(prev => ({ ...prev, messages: [...prev.messages, tempUserMsg], isSending: true, error: null, })); try { const response = await sendMessage(chatId, content); setState(prev => { // Replace temp message with real messages const messagesWithoutTemp = prev.messages.filter(m => m.id !== tempUserMsg.id); const newMessages = [ ...messagesWithoutTemp, response.user_message, response.assistant_message, ]; // Update chat title if it changed (first message) const updatedChats = prev.chats.map(chat => { if (chat.id === chatId && chat.title === 'New Chat') { const newTitle = content.length > 50 ? content.slice(0, 50) + '...' : content; return { ...chat, title: newTitle }; } return chat; }); // Update active chat title too const updatedActiveChat = prev.activeChat && prev.activeChat.title === 'New Chat' ? { ...prev.activeChat, title: content.length > 50 ? content.slice(0, 50) + '...' : content } : prev.activeChat; return { ...prev, messages: newMessages, chats: updatedChats, activeChat: updatedActiveChat, isSending: false, }; }); } catch (err) { setState(prev => ({ ...prev, messages: prev.messages.filter(m => m.id !== tempUserMsg.id), isSending: false, error: err instanceof Error ? err.message : 'Failed to send message', })); } }, [state.activeChat]); return (
{/* Sidebar */} {/* Main Chat Area */} {/* Error Toast */} {state.error && (
{state.error}
)} {/* Documents Management Modal */} setIsDocsModalOpen(false)} />
); }; export default App;