import React, { forwardRef, useState, useEffect, useRef } from 'react'; import { Box, Paper, Typography, TextField, Button, List, ListItem, ListItemText, Chip, IconButton, CircularProgress, Divider, Card, CardContent, Avatar, Grid } from '@mui/material'; import { useUser } from 'hooks/useUser'; import { ChatMessageBase, ChatMessage, ChatSession } from 'types/types'; import { ConversationHandle } from 'components/Conversation'; import { BackstoryPageProps } from 'components/BackstoryTab'; import { Message } from 'components/Message'; import { DeleteConfirmation } from 'components/DeleteConfirmation'; import { CandidateSessionsResponse } from 'services/api-client'; import { CandidateInfo } from 'components/CandidateInfo'; import { useNavigate } from 'react-router-dom'; const CandidateChatPage = forwardRef((props: BackstoryPageProps, ref) => { const { apiClient, candidate } = useUser(); const navigate = useNavigate(); const { setSnack, submitQuery, } = props; const [sessions, setSessions] = useState(null); const [chatSession, setChatSession] = useState(null); const [messages, setMessages] = useState([]); const [newMessage, setNewMessage] = useState(''); const [loading, setLoading] = useState(false); const [streaming, setStreaming] = useState(false); const messagesEndRef = useRef(null); // Load sessions for the candidate const loadSessions = async () => { if (!candidate) return; try { setLoading(true); const result = await apiClient.getCandidateChatSessions(candidate.username); setSessions(result); } catch (error) { console.error('Failed to load sessions:', error); } finally { setLoading(false); } }; // Load messages for current session const loadMessages = async () => { if (!chatSession?.id) return; try { const result = await apiClient.getChatMessages(chatSession.id); setMessages(result.data as any); } catch (error) { console.error('Failed to load messages:', error); } }; // Create new session const createNewSession = async () => { if (!candidate) { return } try { setLoading(true); const newSession = await apiClient.createCandidateChatSession( candidate.username, 'candidate_chat', `Interview Discussion - ${candidate.username}` ); setChatSession(newSession); setMessages([]); await loadSessions(); // Refresh sessions list } catch (error) { console.error('Failed to create session:', error); } finally { setLoading(false); } }; // Send message const sendMessage = async () => { if (!newMessage.trim() || !chatSession?.id || streaming) return; const messageContent = newMessage; setNewMessage(''); setStreaming(true); try { await apiClient.sendMessageStream( chatSession.id, { prompt: messageContent }, { onMessage: (msg) => { console.log("onMessage:", msg); if (msg.type === "response") { setMessages(prev => { const filtered = prev.filter((m: any) => m.id !== msg.id); return [...filtered, msg].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime() ) as any; }); } else { console.log(msg); } }, onError: (error: string | ChatMessageBase) => { console.log("onError:", error); setStreaming(false); }, onStreaming: (chunk) => { console.log("onStreaming:", chunk); }, onStatusChange: (status) => { console.log("onStatusChange:", status); }, onComplete: () => { console.log("onComplete"); setStreaming(false); } }); } catch (error) { console.error('Failed to send message:', error); setStreaming(false); } }; // Auto-scroll to bottom when new messages arrive useEffect(() => { (messagesEndRef.current as any)?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); // Load sessions when username changes useEffect(() => { loadSessions(); }, [candidate]); // Load messages when session changes useEffect(() => { if (chatSession?.id) { loadMessages(); } }, [chatSession]); if (!candidate) { navigate('/find-a-candidate'); } return ( {candidate && } < Box sx={{ display: "flex", mt: 1, gap: 1, height: "100%" }}> {/* Sessions Sidebar */} Chat Sessions {sessions && ( )} {sessions ? ( {sessions.sessions.data.map((session: any) => ( setChatSession(session)} sx={{ mb: 1, borderRadius: 1, border: '1px solid', borderColor: chatSession?.id === session.id ? 'primary.main' : 'divider', cursor: 'pointer', '&:hover': { backgroundColor: 'action.hover' } }} > ))} ) : ( Enter a username and click "Load Sessions" )} {/* Chat Interface */} {chatSession?.id ? ( <> {/* Messages Area */} {messages.map((message: ChatMessageBase) => ( ))} {streaming && ( 🤖 AI is typing... )}
{/* Message Input */} setNewMessage(e.target.value)} onKeyPress={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }} disabled={streaming} multiline maxRows={4} /> ) : ( 🤖 Select a session to start chatting Create a new session or choose from existing ones to begin discussing the candidate )} ); }); export { CandidateChatPage };