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;
|
candidate: Candidate;
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
action?: string;
|
action?: string;
|
||||||
|
elevation?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CandidateInfo: React.FC<CandidateInfoProps> = (props: CandidateInfoProps) => {
|
const CandidateInfo: React.FC<CandidateInfoProps> = (props: CandidateInfoProps) => {
|
||||||
@ -23,7 +24,7 @@ const CandidateInfo: React.FC<CandidateInfoProps> = (props: CandidateInfoProps)
|
|||||||
const {
|
const {
|
||||||
sx,
|
sx,
|
||||||
action = '',
|
action = '',
|
||||||
...rest
|
elevation = 1
|
||||||
} = props;
|
} = props;
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
@ -33,7 +34,7 @@ const CandidateInfo: React.FC<CandidateInfoProps> = (props: CandidateInfoProps)
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
elevation={1}
|
elevation={elevation}
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
borderColor: 'transparent',
|
borderColor: 'transparent',
|
||||||
|
@ -25,9 +25,11 @@ import { Message } from 'components/Message';
|
|||||||
import { DeleteConfirmation } from 'components/DeleteConfirmation';
|
import { DeleteConfirmation } from 'components/DeleteConfirmation';
|
||||||
import { CandidateSessionsResponse } from 'services/api-client';
|
import { CandidateSessionsResponse } from 'services/api-client';
|
||||||
import { CandidateInfo } from 'components/CandidateInfo';
|
import { CandidateInfo } from 'components/CandidateInfo';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
const CandidateChatPage = forwardRef<ConversationHandle, BackstoryPageProps>((props: BackstoryPageProps, ref) => {
|
const CandidateChatPage = forwardRef<ConversationHandle, BackstoryPageProps>((props: BackstoryPageProps, ref) => {
|
||||||
const { apiClient, candidate } = useUser();
|
const { apiClient, candidate } = useUser();
|
||||||
|
const navigate = useNavigate();
|
||||||
const {
|
const {
|
||||||
setSnack,
|
setSnack,
|
||||||
submitQuery,
|
submitQuery,
|
||||||
@ -150,166 +152,166 @@ const CandidateChatPage = forwardRef<ConversationHandle, BackstoryPageProps>((pr
|
|||||||
}
|
}
|
||||||
}, [chatSession]);
|
}, [chatSession]);
|
||||||
|
|
||||||
|
if (!candidate) {
|
||||||
|
navigate('/find-a-candidate');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box ref={ref} sx={{ width: "100%" }}>
|
<Box ref={ref} sx={{ width: "100%", height: "100%", display: "flex", flexGrow: 1, flexDirection: "column" }}>
|
||||||
{ candidate && <CandidateInfo candidate={candidate} /> }
|
{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 */}
|
{/* Sessions Sidebar */}
|
||||||
<Grid size={{ xs: 12, md: 4 }}>
|
<Paper sx={{ p: 2, height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
<Paper sx={{ p: 2, height: '600px', display: 'flex', flexDirection: 'column' }}>
|
<Typography variant="h6" gutterBottom>
|
||||||
<Typography variant="h6" gutterBottom>
|
Chat Sessions
|
||||||
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>
|
|
||||||
|
|
||||||
{sessions && (
|
{sessions && (
|
||||||
<Box sx={{ mt: 2, p: 2, bgcolor: 'background.default', borderRadius: 1 }}>
|
<Chip
|
||||||
<Typography variant="subtitle2" gutterBottom>
|
size="small"
|
||||||
Candidate Info
|
label={`${sessions.sessions.total} total`}
|
||||||
</Typography>
|
sx={{ ml: 1 }}
|
||||||
<Typography variant="body2">
|
/>
|
||||||
<strong>Name:</strong> {sessions.candidate.fullName}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2">
|
|
||||||
<strong>Email:</strong> {sessions.candidate.email}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Typography>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Chat Interface */}
|
<Button
|
||||||
<Grid size={{ xs: 12, md: 8 }}>
|
variant="outlined"
|
||||||
<Paper sx={{ height: '600px', display: 'flex', flexDirection: 'column' }}>
|
onClick={createNewSession}
|
||||||
{chatSession?.id ? (
|
disabled={loading || !candidate}
|
||||||
<>
|
sx={{ mb: 2 }}
|
||||||
{/* Messages Area */}
|
>
|
||||||
<Box sx={{ flexGrow: 1, overflow: 'auto', p: 2 }}>
|
New Session
|
||||||
{messages.map((message: ChatMessageBase) => (
|
</Button>
|
||||||
<Message key={message.id} {...{ chatSession, message, setSnack, submitQuery }} />
|
|
||||||
))}
|
|
||||||
|
|
||||||
{streaming && (
|
<Box sx={{ flexGrow: 1, overflow: 'auto' }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
{sessions ? (
|
||||||
<Avatar sx={{ mr: 1, bgcolor: 'primary.main' }}>
|
<List>
|
||||||
🤖
|
{sessions.sessions.data.map((session: any) => (
|
||||||
</Avatar>
|
<ListItem
|
||||||
<Card>
|
key={session.id}
|
||||||
<CardContent sx={{ display: 'flex', alignItems: 'center', p: 2 }}>
|
// selected={chatSession?.id === session.id}
|
||||||
<CircularProgress size={16} sx={{ mr: 1 }} />
|
onClick={() => setChatSession(session)}
|
||||||
<Typography variant="body2">AI is typing...</Typography>
|
sx={{
|
||||||
</CardContent>
|
mb: 1,
|
||||||
</Card>
|
borderRadius: 1,
|
||||||
</Box>
|
border: '1px solid',
|
||||||
)}
|
borderColor: chatSession?.id === session.id ? 'primary.main' : 'divider',
|
||||||
|
cursor: 'pointer',
|
||||||
<div ref={messagesEndRef} />
|
'&:hover': {
|
||||||
</Box>
|
backgroundColor: 'action.hover'
|
||||||
|
|
||||||
<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 }}
|
|
||||||
>
|
>
|
||||||
▶
|
<ListItemText
|
||||||
</Button>
|
primary={session.title}
|
||||||
</Box>
|
secondary={`${new Date(session.lastActivity).toLocaleDateString()} • ${session.context.type}`}
|
||||||
</>
|
/>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
) : (
|
) : (
|
||||||
<Box
|
<Typography color="text.secondary" align="center">
|
||||||
sx={{
|
Enter a username and click "Load Sessions"
|
||||||
height: '100%',
|
</Typography>
|
||||||
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>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
{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>
|
</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.
|
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):
|
class CandidateChat(Agent):
|
||||||
"""
|
"""
|
||||||
CandidateChat Agent
|
CandidateChat Agent
|
||||||
|
Loading…
x
Reference in New Issue
Block a user