import { useState, useEffect, useRef } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { socketClient } from '../lib/socket'; import { chatSessionApi, projectApi, type ChatSession, type ChatTurn, type Project } from '../lib/api'; interface ChatMessage { id: string; role: 'user' | 'assistant' | 'system'; content: string; thinking?: string; toolCalls?: Array<{ callId: string; name: string; parameters?: string; response?: string; }>; timestamp: Date; } export default function ChatSessionView() { const { projectId, sessionId } = useParams<{ projectId: string; sessionId: string }>(); const navigate = useNavigate(); const socket = socketClient.socket; const [project, setProject] = useState(null); const [session, setSession] = useState(null); const [messages, setMessages] = useState([]); const [promptInput, setPromptInput] = useState(''); const [isProcessing, setIsProcessing] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const messagesEndRef = useRef(null); const inputRef = useRef(null); useEffect(() => { loadSessionData(); }, [projectId, sessionId]); useEffect(() => { setupSocketListeners(); return () => cleanupSocketListeners(); }, [sessionId]); useEffect(() => { scrollToBottom(); }, [messages]); const loadSessionData = async () => { try { // Load project if (projectId) { const projectData = await projectApi.get(projectId); setProject(projectData); } // Load chat session if (sessionId) { const sessionData = await chatSessionApi.get(sessionId); setSession(sessionData); // Load existing turns const turns = await chatSessionApi.getTurns(sessionId); const chatMessages: ChatMessage[] = turns.map((turn: ChatTurn) => ({ id: turn._id, role: 'user', content: turn.prompts.user, timestamp: new Date(turn.createdAt), })); // Add assistant responses turns.forEach((turn: ChatTurn) => { if (turn.thinking || turn.response || turn.toolCalls?.length) { chatMessages.push({ id: `${turn._id}-response`, role: 'assistant', content: turn.response || '', thinking: turn.thinking, toolCalls: turn.toolCalls, timestamp: new Date(turn.createdAt), }); } }); setMessages(chatMessages); } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load session'); } finally { setLoading(false); } }; const setupSocketListeners = () => { if (!socket) return; socket.on('thinking', handleThinking); socket.on('response', handleResponse); socket.on('toolCall', handleToolCall); socket.on('workOrderComplete', handleWorkOrderComplete); }; const cleanupSocketListeners = () => { if (!socket) return; socket.off('thinking', handleThinking); socket.off('response', handleResponse); socket.off('toolCall', handleToolCall); socket.off('workOrderComplete', handleWorkOrderComplete); }; const handleThinking = (content: string) => { setMessages(prev => { const lastMessage = prev[prev.length - 1]; if (lastMessage && lastMessage.role === 'assistant') { return [ ...prev.slice(0, -1), { ...lastMessage, thinking: (lastMessage.thinking || '') + content, }, ]; } return prev; }); }; const handleResponse = (content: string) => { setMessages(prev => { const lastMessage = prev[prev.length - 1]; if (lastMessage && lastMessage.role === 'assistant') { return [ ...prev.slice(0, -1), { ...lastMessage, content: lastMessage.content + content, }, ]; } return prev; }); }; const handleToolCall = (callId: string, name: string, params: string, response: string) => { setMessages(prev => { const lastMessage = prev[prev.length - 1]; if (lastMessage && lastMessage.role === 'assistant') { const toolCalls = lastMessage.toolCalls || []; toolCalls.push({ callId, name, parameters: params, response }); return [ ...prev.slice(0, -1), { ...lastMessage, toolCalls, }, ]; } return prev; }); }; const handleWorkOrderComplete = (turnId: string, success: boolean, message?: string) => { setIsProcessing(false); if (!success) { setError(message || 'Work order failed'); } }; const handleSubmitPrompt = async (e: React.FormEvent) => { e.preventDefault(); if (!promptInput.trim() || isProcessing || !socket) return; const userMessage: ChatMessage = { id: `temp-${Date.now()}`, role: 'user', content: promptInput.trim(), timestamp: new Date(), }; setMessages(prev => [...prev, userMessage]); setPromptInput(''); setIsProcessing(true); setError(''); // Add assistant message placeholder setMessages(prev => [...prev, { id: `response-${Date.now()}`, role: 'assistant', content: '', timestamp: new Date(), }]); // Emit prompt to backend socketClient.emitServer('submitPrompt', promptInput.trim()); }; const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; if (loading) { return (

Loading chat session...

); } if (error && !session) { return (

{error}

); } return (
{/* Main Chat Area */}
{/* Messages */}
{messages.map((message) => ( ))}
{/* Prompt Input */}