nagur-shareef-shaik's picture
Upload 5 files (#5)
ffa2991 verified
import { useState, useEffect, useRef } from 'react';
import { ApiResponse, ChatMessage, UserProfile } from '../../interface';
import { CHAT_ENDPOINT } from '../../endpoint';
export function useChatInterface(userProfile: UserProfile, isProfileComplete?: boolean) {
const [chatHistory, setChatHistory] = useState<ChatMessage[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [currentMessage, setCurrentMessage] = useState('');
const [showPlanRecs, setShowPlanRecs] = useState<boolean>(false);
const chatEndRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
// threadId is generated once per chat session and persists for the hook's lifetime
const [threadId] = useState(() => crypto.randomUUID());
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
// Focus input after chatHistory changes (i.e., after response is rendered)
if (inputRef.current) {
inputRef.current.focus();
}
}, [chatHistory]);
// Prevent double API call: only send initial chat message once when userProfile is complete
const hasSentInitialMessage = useRef(false);
useEffect(() => {
// TODO: review this logic
// Check if userProfile is complete before sending initial message
const isValidProfile = !!userProfile &&
userProfile?.zip_code?.length && userProfile?.zip_code?.length > 0 &&
!!userProfile.age && userProfile.age > 0 &&
!!userProfile.gender && userProfile.gender.length > 0 &&
!!userProfile.household_size &&
!!userProfile.income &&
!!userProfile.employment_status &&
!!userProfile.citizenship;
if (isValidProfile && !hasSentInitialMessage.current) {
sendChatMessage('START_PROFILE_BUILDING', userProfile);
hasSentInitialMessage.current = true;
}
}, [userProfile]);
const sendChatMessage = async (
message: string,
profileOverride?: UserProfile
) => {
setIsLoading(true);
try {
const payload = {
thread_id: threadId,
user_profile: profileOverride ?? userProfile,
message,
is_profile_complete: isProfileComplete ?? false,
conversation_history: chatHistory.map(m => `${m.role}: ${m.content}`)
};
const response = await fetch(CHAT_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) throw new Error('Failed to send message');
const data: ApiResponse = await response.json();
// Only append the agent's message to the existing chatHistory
const agentMsgRaw = data.updated_history[data.updated_history.length - 1] || '';
const agentMsgContent = agentMsgRaw.replace(/^Agent:\s*/, '');
const agentMessage: ChatMessage = {
role: 'agent',
content: agentMsgContent,
timestamp: Date.now(),
plans: !showPlanRecs && data.plan_recommendations?.recommendations?.length && data.plan_recommendations?.recommendations?.length > 0 ? data.plan_recommendations.recommendations : undefined
};
setChatHistory(prev => [...prev, agentMessage]);
if (!showPlanRecs && data.plan_recommendations?.recommendations?.length && data.plan_recommendations?.recommendations?.length > 0) {
setShowPlanRecs(true);
}
return data;
} catch (error) {
console.error('Error sending message:', error);
} finally {
setIsLoading(false);
}
};
const handleSendMessage = async () => {
if (!currentMessage.trim()) return;
// Add user message to chat immediately
const userMessage: ChatMessage = {
role: 'user',
content: currentMessage,
timestamp: Date.now()
};
setChatHistory(prev => [...prev, userMessage]);
const messageToSend = currentMessage;
setCurrentMessage('');
await sendChatMessage(messageToSend);
};
return {
chatHistory,
isLoading,
currentMessage,
setCurrentMessage,
handleSendMessage,
showPlanRecs,
chatEndRef,
inputRef,
sendChatMessage
};
}