Integrated into UI. need mobile view for chatpage
This commit is contained in:
parent
c2601bf17a
commit
89b71a1428
@ -16,6 +16,7 @@ interface CandidateInfoProps {
|
||||
candidate: Candidate;
|
||||
sx?: SxProps;
|
||||
action?: string;
|
||||
elevation?: number;
|
||||
};
|
||||
|
||||
const CandidateInfo: React.FC<CandidateInfoProps> = (props: CandidateInfoProps) => {
|
||||
@ -23,7 +24,7 @@ const CandidateInfo: React.FC<CandidateInfoProps> = (props: CandidateInfoProps)
|
||||
const {
|
||||
sx,
|
||||
action = '',
|
||||
...rest
|
||||
elevation = 1
|
||||
} = props;
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
@ -33,7 +34,7 @@ const CandidateInfo: React.FC<CandidateInfoProps> = (props: CandidateInfoProps)
|
||||
}
|
||||
return (
|
||||
<Card
|
||||
elevation={1}
|
||||
elevation={elevation}
|
||||
sx={{
|
||||
display: "flex",
|
||||
borderColor: 'transparent',
|
||||
|
@ -25,9 +25,11 @@ 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<ConversationHandle, BackstoryPageProps>((props: BackstoryPageProps, ref) => {
|
||||
const { apiClient, candidate } = useUser();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
setSnack,
|
||||
submitQuery,
|
||||
@ -150,166 +152,166 @@ const CandidateChatPage = forwardRef<ConversationHandle, BackstoryPageProps>((pr
|
||||
}
|
||||
}, [chatSession]);
|
||||
|
||||
if (!candidate) {
|
||||
navigate('/find-a-candidate');
|
||||
}
|
||||
|
||||
return (
|
||||
<Box ref={ref} sx={{ width: "100%" }}>
|
||||
{ candidate && <CandidateInfo candidate={candidate} /> }
|
||||
<Box ref={ref} sx={{ width: "100%", height: "100%", display: "flex", flexGrow: 1, flexDirection: "column" }}>
|
||||
{candidate && <CandidateInfo elevation={4} candidate={candidate} sx={{ minHeight: "max-content" }} />}
|
||||
|
||||
<Grid container spacing={3}>
|
||||
< Box sx={{ display: "flex", mt: 1, gap: 1, height: "100%" }}>
|
||||
{/* Sessions Sidebar */}
|
||||
<Grid size={{ xs: 12, md: 4 }}>
|
||||
<Paper sx={{ p: 2, height: '600px', display: 'flex', flexDirection: 'column' }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Chat Sessions
|
||||
{sessions && (
|
||||
<Chip
|
||||
size="small"
|
||||
label={`${sessions.sessions.total} total`}
|
||||
sx={{ ml: 1 }}
|
||||
/>
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={createNewSession}
|
||||
disabled={loading || !candidate}
|
||||
sx={{ mb: 2 }}
|
||||
>
|
||||
New Session
|
||||
</Button>
|
||||
|
||||
<Box sx={{ flexGrow: 1, overflow: 'auto' }}>
|
||||
{sessions ? (
|
||||
<List>
|
||||
{sessions.sessions.data.map((session : any) => (
|
||||
<ListItem
|
||||
key={session.id}
|
||||
// selected={chatSession?.id === session.id}
|
||||
onClick={() => setChatSession(session)}
|
||||
sx={{
|
||||
mb: 1,
|
||||
borderRadius: 1,
|
||||
border: '1px solid',
|
||||
borderColor: chatSession?.id === session.id ? 'primary.main' : 'divider',
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
backgroundColor: 'action.hover'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ListItemText
|
||||
primary={session.title}
|
||||
secondary={`${new Date(session.lastActivity).toLocaleDateString()} • ${session.context.type}`}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
) : (
|
||||
<Typography color="text.secondary" align="center">
|
||||
Enter a username and click "Load Sessions"
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Paper sx={{ p: 2, height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Chat Sessions
|
||||
{sessions && (
|
||||
<Box sx={{ mt: 2, p: 2, bgcolor: 'background.default', borderRadius: 1 }}>
|
||||
<Typography variant="subtitle2" gutterBottom>
|
||||
Candidate Info
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<strong>Name:</strong> {sessions.candidate.fullName}
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<strong>Email:</strong> {sessions.candidate.email}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Chip
|
||||
size="small"
|
||||
label={`${sessions.sessions.total} total`}
|
||||
sx={{ ml: 1 }}
|
||||
/>
|
||||
)}
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Typography>
|
||||
|
||||
{/* Chat Interface */}
|
||||
<Grid size={{ xs: 12, md: 8 }}>
|
||||
<Paper sx={{ height: '600px', display: 'flex', flexDirection: 'column' }}>
|
||||
{chatSession?.id ? (
|
||||
<>
|
||||
{/* Messages Area */}
|
||||
<Box sx={{ flexGrow: 1, overflow: 'auto', p: 2 }}>
|
||||
{messages.map((message: ChatMessageBase) => (
|
||||
<Message key={message.id} {...{ chatSession, message, setSnack, submitQuery }} />
|
||||
))}
|
||||
|
||||
{streaming && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||
<Avatar sx={{ mr: 1, bgcolor: 'primary.main' }}>
|
||||
🤖
|
||||
</Avatar>
|
||||
<Card>
|
||||
<CardContent sx={{ display: 'flex', alignItems: 'center', p: 2 }}>
|
||||
<CircularProgress size={16} sx={{ mr: 1 }} />
|
||||
<Typography variant="body2">AI is typing...</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<div ref={messagesEndRef} />
|
||||
</Box>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={createNewSession}
|
||||
disabled={loading || !candidate}
|
||||
sx={{ mb: 2 }}
|
||||
>
|
||||
New Session
|
||||
</Button>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* Message Input */}
|
||||
<Box sx={{ p: 2, display: 'flex', gap: 1 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Type your message about the candidate..."
|
||||
value={newMessage}
|
||||
onChange={(e) => setNewMessage(e.target.value)}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
<Box sx={{ flexGrow: 1, overflow: 'auto' }}>
|
||||
{sessions ? (
|
||||
<List>
|
||||
{sessions.sessions.data.map((session: any) => (
|
||||
<ListItem
|
||||
key={session.id}
|
||||
// selected={chatSession?.id === session.id}
|
||||
onClick={() => setChatSession(session)}
|
||||
sx={{
|
||||
mb: 1,
|
||||
borderRadius: 1,
|
||||
border: '1px solid',
|
||||
borderColor: chatSession?.id === session.id ? 'primary.main' : 'divider',
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
backgroundColor: 'action.hover'
|
||||
}
|
||||
}}
|
||||
disabled={streaming}
|
||||
multiline
|
||||
maxRows={4}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={sendMessage}
|
||||
disabled={!newMessage.trim() || streaming}
|
||||
sx={{ minWidth: 'auto', px: 2 }}
|
||||
>
|
||||
▶
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
<ListItemText
|
||||
primary={session.title}
|
||||
secondary={`${new Date(session.lastActivity).toLocaleDateString()} • ${session.context.type}`}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
gap: 2
|
||||
}}
|
||||
>
|
||||
<Typography variant="h1" sx={{ fontSize: 64, color: 'text.secondary' }}>
|
||||
🤖
|
||||
</Typography>
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
Select a session to start chatting
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" align="center">
|
||||
Create a new session or choose from existing ones to begin discussing the candidate
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography color="text.secondary" align="center">
|
||||
Enter a username and click "Load Sessions"
|
||||
</Typography>
|
||||
)}
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
{sessions && (
|
||||
<Box sx={{ mt: 2, p: 2, bgcolor: 'background.default', borderRadius: 1 }}>
|
||||
<Typography variant="subtitle2" gutterBottom>
|
||||
Candidate Info
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<strong>Name:</strong> {sessions.candidate.fullName}
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<strong>Email:</strong> {sessions.candidate.email}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
|
||||
{/* Chat Interface */}
|
||||
<Paper sx={{ display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
|
||||
{chatSession?.id ? (
|
||||
<>
|
||||
{/* Messages Area */}
|
||||
<Box sx={{ flexGrow: 1, overflow: 'auto', p: 2 }}>
|
||||
{messages.map((message: ChatMessageBase) => (
|
||||
<Message key={message.id} {...{ chatSession, message, setSnack, submitQuery }} />
|
||||
))}
|
||||
|
||||
{streaming && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||
<Avatar sx={{ mr: 1, bgcolor: 'primary.main' }}>
|
||||
🤖
|
||||
</Avatar>
|
||||
<Card>
|
||||
<CardContent sx={{ display: 'flex', alignItems: 'center', p: 2 }}>
|
||||
<CircularProgress size={16} sx={{ mr: 1 }} />
|
||||
<Typography variant="body2">AI is typing...</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<div ref={messagesEndRef} />
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* Message Input */}
|
||||
<Box sx={{ p: 2, display: 'flex', gap: 1 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Type your message about the candidate..."
|
||||
value={newMessage}
|
||||
onChange={(e) => setNewMessage(e.target.value)}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
}}
|
||||
disabled={streaming}
|
||||
multiline
|
||||
maxRows={4}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={sendMessage}
|
||||
disabled={!newMessage.trim() || streaming}
|
||||
sx={{ minWidth: 'auto', px: 2 }}
|
||||
>
|
||||
▶
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
gap: 2
|
||||
}}
|
||||
>
|
||||
<Typography variant="h1" sx={{ fontSize: 64, color: 'text.secondary' }}>
|
||||
🤖
|
||||
</Typography>
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
Select a session to start chatting
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" align="center">
|
||||
Create a new session or choose from existing ones to begin discussing the candidate
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
@ -47,7 +47,6 @@ DO NOT make up a URL for an image or provide markdown syntax for embedding an im
|
||||
Always use tools, <|resume|>, and <|context|> when possible. Be concise, and never make up information. If you do not know the answer, say so.
|
||||
"""
|
||||
|
||||
|
||||
class CandidateChat(Agent):
|
||||
"""
|
||||
CandidateChat Agent
|
||||
|
Loading…
x
Reference in New Issue
Block a user