Restructure chat interface to display history side-by-side with live chat

- Change chat layout from vertical stacking to horizontal side-by-side layout
- History panel (300px width) displays on left with conversation list
- Main chat area takes remaining space on right with messages and input
- Improves UX by making chat history visible alongside active conversation
- Fix JSX structure and nesting for proper component rendering

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
andupetcu
2025-09-22 12:57:09 +03:00
parent 24c0577f44
commit c82b3007fd

View File

@@ -536,38 +536,48 @@ export default function FloatingChat() {
{!isMinimized && (
<>
{/* Chat History Panel */}
{showHistory && (
<Box sx={{
p: 2,
borderBottom: 1,
borderColor: 'divider',
bgcolor: 'grey.50',
maxHeight: '300px',
overflowY: 'auto'
}}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">
Chat History
</Typography>
<Box>
<IconButton
size="small"
onClick={createNewConversation}
title="New Conversation"
sx={{ mr: 0.5 }}
>
<Add />
</IconButton>
<IconButton
size="small"
onClick={() => setShowHistory(false)}
title="Close History"
>
<Close />
</IconButton>
{/* Main Content Area - Side by Side Layout */}
<Box sx={{
display: 'flex',
flexGrow: 1,
overflow: 'hidden'
}}>
{/* Chat History Panel - Left Side */}
{showHistory && (
<Box sx={{
width: '300px',
borderRight: 1,
borderColor: 'divider',
bgcolor: 'grey.50',
display: 'flex',
flexDirection: 'column',
}}>
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider' }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="h6">
Chat History
</Typography>
<Box>
<IconButton
size="small"
onClick={createNewConversation}
title="New Conversation"
sx={{ mr: 0.5 }}
>
<Add />
</IconButton>
<IconButton
size="small"
onClick={() => setShowHistory(false)}
title="Close History"
>
<Close />
</IconButton>
</Box>
</Box>
</Box>
</Box>
<Box sx={{ flexGrow: 1, overflowY: 'auto', p: 2 }}>
{!isAuthenticated ? (
<Box sx={{ textAlign: 'center', py: 3 }}>
@@ -638,8 +648,242 @@ export default function FloatingChat() {
))}
</Box>
)}
</Box>
</Box>
)}
{/* Main Chat Area - Right Side */}
<Box sx={{
flexGrow: 1,
display: 'flex',
flexDirection: 'column',
overflow: 'hidden'
}}>
{/* Suggested Questions */}
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider' }}>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
{t('suggestions.title')}
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{suggestedQuestions.slice(0, 3).map((question, index) => (
<Chip
key={index}
label={question}
size="small"
variant="outlined"
onClick={() => setInputMessage(question)}
sx={{
fontSize: '0.75rem',
cursor: 'pointer',
'&:hover': {
bgcolor: 'primary.light',
color: 'white',
},
}}
/>
))}
</Box>
</Box>
{/* Messages */}
<Box
sx={{
flexGrow: 1,
overflow: 'auto',
p: 1,
}}
>
{messages.map((message) => (
<Box
key={message.id}
sx={{
display: 'flex',
justifyContent: message.role === 'user' ? 'flex-end' : 'flex-start',
mb: 2,
}}
>
<Box
sx={{
display: 'flex',
flexDirection: message.role === 'user' ? 'row-reverse' : 'row',
alignItems: 'flex-start',
maxWidth: '85%',
gap: 1,
}}
>
<Avatar
sx={{
width: 32,
height: 32,
bgcolor: message.role === 'user' ? 'primary.main' : 'secondary.main',
}}
>
{message.role === 'user' ? <Person fontSize="small" /> : <SmartToy fontSize="small" />}
</Avatar>
<Paper
elevation={1}
sx={{
p: 1.5,
bgcolor: message.role === 'user' ? 'primary.light' : 'background.paper',
color: message.role === 'user' ? 'white' : 'text.primary',
borderRadius: 2,
maxWidth: '100%',
}}
>
{message.role === 'assistant' ? (
<ReactMarkdown
components={{
p: ({ children }) => (
<Typography
variant="body2"
sx={{ mb: 1, lineHeight: 1.4 }}
>
{children}
</Typography>
),
h3: ({ children }) => (
<Typography
variant="h6"
sx={{ fontWeight: 'bold', mt: 2, mb: 1 }}
>
{children}
</Typography>
),
strong: ({ children }) => (
<Typography
component="span"
sx={{ fontWeight: 'bold' }}
>
{children}
</Typography>
),
ul: ({ children }) => (
<Box component="ul" sx={{ pl: 2, mb: 1 }}>
{children}
</Box>
),
li: ({ children }) => (
<Typography
component="li"
variant="body2"
sx={{ mb: 0.5 }}
>
{children}
</Typography>
),
}}
>
{message.content}
</ReactMarkdown>
) : (
<Typography
variant="body2"
sx={{
whiteSpace: 'pre-wrap',
lineHeight: 1.4,
}}
>
{message.content}
</Typography>
)}
{message.role === 'assistant' && (
<Box sx={{ display: 'flex', gap: 0.5, mt: 1, justifyContent: 'flex-end' }}>
<IconButton
size="small"
onClick={() => copyToClipboard(message.content)}
>
<ContentCopy fontSize="small" />
</IconButton>
<IconButton size="small">
<ThumbUp fontSize="small" />
</IconButton>
<IconButton size="small">
<ThumbDown fontSize="small" />
</IconButton>
</Box>
)}
<Typography
variant="caption"
sx={{
display: 'block',
textAlign: 'right',
mt: 0.5,
opacity: 0.7,
}}
>
{message.timestamp.toLocaleTimeString(locale === 'en' ? 'en-US' : 'ro-RO', {
hour: '2-digit',
minute: '2-digit',
})}
</Typography>
</Paper>
</Box>
</Box>
))}
{isLoading && (
<Box sx={{ display: 'flex', justifyContent: 'flex-start', mb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1 }}>
<Avatar sx={{ width: 32, height: 32, bgcolor: 'secondary.main' }}>
<SmartToy fontSize="small" />
</Avatar>
<Paper elevation={1} sx={{ p: 1.5, borderRadius: 2 }}>
<Typography variant="body2">
{t('loading')}
</Typography>
</Paper>
</Box>
</Box>
)}
<div ref={messagesEndRef} />
</Box>
<Divider />
{/* Input */}
<Box sx={{ p: 2 }}>
<Box sx={{ display: 'flex', gap: 1 }}>
<TextField
fullWidth
size="small"
multiline
maxRows={3}
placeholder={t('placeholder')}
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyPress={handleKeyPress}
disabled={isLoading}
variant="outlined"
sx={{
'& .MuiOutlinedInput-root': {
borderRadius: 2,
}
}}
/>
<Button
variant="contained"
onClick={handleSendMessage}
disabled={!inputMessage.trim() || isLoading}
sx={{
minWidth: 'auto',
px: 2,
borderRadius: 2,
background: 'linear-gradient(45deg, #009688 30%, #00796B 90%)',
}}
>
<Send fontSize="small" />
</Button>
</Box>
<Typography variant="caption" color="text.secondary" sx={{ mt: 0.5, display: 'block' }}>
{t('enterToSend')}
</Typography>
</Box>
</Box>
)}
</Box>
{/* Conversation Menu */}
<Menu
@@ -709,230 +953,6 @@ export default function FloatingChat() {
</Button>
</DialogActions>
</Dialog>
{/* Suggested Questions */}
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider' }}>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
{t('suggestions.title')}
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{suggestedQuestions.slice(0, 3).map((question, index) => (
<Chip
key={index}
label={question}
size="small"
variant="outlined"
onClick={() => setInputMessage(question)}
sx={{
fontSize: '0.75rem',
cursor: 'pointer',
'&:hover': {
bgcolor: 'primary.light',
color: 'white',
},
}}
/>
))}
</Box>
</Box>
{/* Messages */}
<Box
sx={{
flexGrow: 1,
overflow: 'auto',
p: 1,
}}
>
{messages.map((message) => (
<Box
key={message.id}
sx={{
display: 'flex',
justifyContent: message.role === 'user' ? 'flex-end' : 'flex-start',
mb: 2,
}}
>
<Box
sx={{
display: 'flex',
flexDirection: message.role === 'user' ? 'row-reverse' : 'row',
alignItems: 'flex-start',
maxWidth: '85%',
gap: 1,
}}
>
<Avatar
sx={{
width: 32,
height: 32,
bgcolor: message.role === 'user' ? 'primary.main' : 'secondary.main',
}}
>
{message.role === 'user' ? <Person fontSize="small" /> : <SmartToy fontSize="small" />}
</Avatar>
<Paper
elevation={1}
sx={{
p: 1.5,
bgcolor: message.role === 'user' ? 'primary.light' : 'background.paper',
color: message.role === 'user' ? 'white' : 'text.primary',
borderRadius: 2,
maxWidth: '100%',
}}
>
{message.role === 'assistant' ? (
<ReactMarkdown
components={{
p: ({ children }) => (
<Typography
variant="body2"
sx={{ mb: 1, lineHeight: 1.4 }}
>
{children}
</Typography>
),
h3: ({ children }) => (
<Typography
variant="h6"
sx={{ fontWeight: 'bold', mt: 2, mb: 1 }}
>
{children}
</Typography>
),
strong: ({ children }) => (
<Typography
component="span"
sx={{ fontWeight: 'bold' }}
>
{children}
</Typography>
),
ul: ({ children }) => (
<Box component="ul" sx={{ pl: 2, mb: 1 }}>
{children}
</Box>
),
li: ({ children }) => (
<Typography
component="li"
variant="body2"
sx={{ mb: 0.5 }}
>
{children}
</Typography>
),
}}
>
{message.content}
</ReactMarkdown>
) : (
<Typography
variant="body2"
sx={{
whiteSpace: 'pre-wrap',
lineHeight: 1.4,
}}
>
{message.content}
</Typography>
)}
{message.role === 'assistant' && (
<Box sx={{ display: 'flex', gap: 0.5, mt: 1, justifyContent: 'flex-end' }}>
<IconButton
size="small"
onClick={() => copyToClipboard(message.content)}
>
<ContentCopy fontSize="small" />
</IconButton>
<IconButton size="small">
<ThumbUp fontSize="small" />
</IconButton>
<IconButton size="small">
<ThumbDown fontSize="small" />
</IconButton>
</Box>
)}
<Typography
variant="caption"
sx={{
display: 'block',
textAlign: 'right',
mt: 0.5,
opacity: 0.7,
}}
>
{message.timestamp.toLocaleTimeString(locale === 'en' ? 'en-US' : 'ro-RO', {
hour: '2-digit',
minute: '2-digit',
})}
</Typography>
</Paper>
</Box>
</Box>
))}
{isLoading && (
<Box sx={{ display: 'flex', justifyContent: 'flex-start', mb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1 }}>
<Avatar sx={{ width: 32, height: 32, bgcolor: 'secondary.main' }}>
<SmartToy fontSize="small" />
</Avatar>
<Paper elevation={1} sx={{ p: 1.5, borderRadius: 2 }}>
<Typography variant="body2">
{t('loading')}
</Typography>
</Paper>
</Box>
</Box>
)}
<div ref={messagesEndRef} />
</Box>
<Divider />
{/* Input */}
<Box sx={{ p: 2 }}>
<Box sx={{ display: 'flex', gap: 1 }}>
<TextField
fullWidth
size="small"
multiline
maxRows={3}
placeholder={t('placeholder')}
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyPress={handleKeyPress}
disabled={isLoading}
variant="outlined"
sx={{
'& .MuiOutlinedInput-root': {
borderRadius: 2,
}
}}
/>
<Button
variant="contained"
onClick={handleSendMessage}
disabled={!inputMessage.trim() || isLoading}
sx={{
minWidth: 'auto',
px: 2,
borderRadius: 2,
background: 'linear-gradient(45deg, #009688 30%, #00796B 90%)',
}}
>
<Send fontSize="small" />
</Button>
</Box>
<Typography variant="caption" color="text.secondary" sx={{ mt: 0.5, display: 'block' }}>
{t('enterToSend')}
</Typography>
</Box>
</>
)}
</Paper>