Integrated into UI. need mobile view for chatpage

This commit is contained in:
James Ketr 2025-05-29 16:58:14 -07:00
parent c2601bf17a
commit 89b71a1428
3 changed files with 153 additions and 151 deletions

View File

@ -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',

View File

@ -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>
);
});

View File

@ -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