144 lines
No EOL
4 KiB
JavaScript
144 lines
No EOL
4 KiB
JavaScript
import { useState, useRef, useEffect } from 'react';
|
|
import { Box, TextField, IconButton, Paper, Typography } from '@mui/material';
|
|
import { Send, ExpandLess, ExpandMore } from '@mui/icons-material';
|
|
import { sendChatMessage } from '../services/claudeService';
|
|
|
|
function ChatInterface() {
|
|
const [messages, setMessages] = useState([]);
|
|
const [input, setInput] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
const messagesEndRef = useRef(null);
|
|
|
|
const handleSend = async () => {
|
|
if (!input.trim() || loading) return;
|
|
|
|
const userMessage = { role: 'user', content: input };
|
|
setMessages(prev => [...prev, userMessage]);
|
|
setInput('');
|
|
setLoading(true);
|
|
setIsExpanded(true); // Expand chat when sending a message
|
|
|
|
try {
|
|
const response = await sendChatMessage(input, messages);
|
|
setMessages(prev => [...prev, { role: 'assistant', content: response }]);
|
|
} catch (error) {
|
|
console.error('Chat error:', error);
|
|
setMessages(prev => [...prev, {
|
|
role: 'assistant',
|
|
content: 'Sorry, I encountered an error. Please try again.'
|
|
}]);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
}, [messages]);
|
|
|
|
return (
|
|
<Paper
|
|
elevation={8}
|
|
sx={{
|
|
position: 'fixed',
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0,
|
|
height: isExpanded ? '40vh' : '64px',
|
|
transition: 'height 0.3s ease',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
zIndex: 1000
|
|
}}
|
|
>
|
|
{/* Expand/Collapse Button */}
|
|
<Box sx={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
p: 1,
|
|
borderBottom: 1,
|
|
borderColor: 'divider'
|
|
}}>
|
|
<IconButton
|
|
onClick={() => setIsExpanded(!isExpanded)}
|
|
size="small"
|
|
>
|
|
{isExpanded ? <ExpandMore /> : <ExpandLess />}
|
|
</IconButton>
|
|
<Typography variant="body2" sx={{ ml: 1 }}>
|
|
Chat with Claude
|
|
</Typography>
|
|
</Box>
|
|
|
|
{/* Messages area - only visible when expanded */}
|
|
{isExpanded && (
|
|
<Box sx={{ flex: 1, overflow: 'auto', p: 2 }}>
|
|
{messages.length === 0 && (
|
|
<Typography color="text.secondary" variant="body2">
|
|
Ask me anything about your briefing or the news...
|
|
</Typography>
|
|
)}
|
|
{messages.map((msg, idx) => (
|
|
<Box
|
|
key={idx}
|
|
sx={{
|
|
mb: 2,
|
|
textAlign: msg.role === 'user' ? 'right' : 'left'
|
|
}}
|
|
>
|
|
<Typography
|
|
variant="body2"
|
|
sx={{
|
|
display: 'inline-block',
|
|
bgcolor: msg.role === 'user' ? 'primary.main' : 'grey.800',
|
|
color: 'white',
|
|
p: 1.5,
|
|
borderRadius: 2,
|
|
maxWidth: '70%'
|
|
}}
|
|
>
|
|
{msg.content}
|
|
</Typography>
|
|
</Box>
|
|
))}
|
|
<div ref={messagesEndRef} />
|
|
</Box>
|
|
)}
|
|
|
|
{/* Input area - always visible */}
|
|
<Box sx={{
|
|
p: 2,
|
|
borderTop: isExpanded ? 1 : 0,
|
|
borderColor: 'divider',
|
|
display: 'flex',
|
|
gap: 1,
|
|
backgroundColor: 'background.paper'
|
|
}}>
|
|
<TextField
|
|
fullWidth
|
|
value={input}
|
|
onChange={(e) => setInput(e.target.value)}
|
|
onKeyPress={(e) => {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault();
|
|
handleSend();
|
|
}
|
|
}}
|
|
placeholder="Ask Claude anything..."
|
|
disabled={loading}
|
|
size="small"
|
|
/>
|
|
<IconButton
|
|
onClick={handleSend}
|
|
disabled={loading || !input.trim()}
|
|
color="primary"
|
|
>
|
|
<Send />
|
|
</IconButton>
|
|
</Box>
|
|
</Paper>
|
|
);
|
|
}
|
|
|
|
export default ChatInterface; |