Drawer opens and closes

This commit is contained in:
James Ketr 2025-05-30 02:46:13 -07:00
parent 4330bd4b7c
commit 8f6c39c3f7

View File

@ -15,8 +15,16 @@ import {
Card,
CardContent,
Avatar,
Grid
Drawer,
useTheme,
useMediaQuery,
Fab
} from '@mui/material';
import {
ChevronLeft,
ChevronRight,
Chat as ChatIcon
} from '@mui/icons-material';
import { useUser } from 'hooks/useUser';
import { ChatMessageBase, ChatMessage, ChatSession } from 'types/types';
import { ConversationHandle } from 'components/Conversation';
@ -27,13 +35,20 @@ import { CandidateSessionsResponse } from 'services/api-client';
import { CandidateInfo } from 'components/CandidateInfo';
import { useNavigate } from 'react-router-dom';
const DRAWER_WIDTH = 300;
const HANDLE_WIDTH = 48;
const CandidateChatPage = forwardRef<ConversationHandle, BackstoryPageProps>((props: BackstoryPageProps, ref) => {
const { apiClient, candidate } = useUser();
const navigate = useNavigate();
const theme = useTheme();
const isMdUp = useMediaQuery(theme.breakpoints.up('md'));
const {
setSnack,
submitQuery,
} = props;
const [sessions, setSessions] = useState<CandidateSessionsResponse | null>(null);
const [chatSession, setChatSession] = useState<ChatSession | null>(null);
const [messages, setMessages] = useState([]);
@ -42,6 +57,14 @@ const CandidateChatPage = forwardRef<ConversationHandle, BackstoryPageProps>((pr
const [streaming, setStreaming] = useState(false);
const messagesEndRef = useRef(null);
// Drawer state - defaults to open on md+ screens or when no session is selected
const [drawerOpen, setDrawerOpen] = useState(() => isMdUp || !chatSession);
// Update drawer state when screen size or session changes
useEffect(() => {
setDrawerOpen(isMdUp || !chatSession);
}, [isMdUp, chatSession]);
// Load sessions for the candidate
const loadSessions = async () => {
if (!candidate) return;
@ -156,69 +179,144 @@ const CandidateChatPage = forwardRef<ConversationHandle, BackstoryPageProps>((pr
navigate('/find-a-candidate');
}
const drawerContent = (
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column', p: 2 }}>
<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}
onClick={() => {
setChatSession(session);
// Auto-close drawer on smaller screens when session is selected
if (!isMdUp) {
setDrawerOpen(false);
}
}}
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>
</Box>
);
const drawerHandle = (
<Box
sx={{
position: 'fixed',
left: drawerOpen ? DRAWER_WIDTH : 0,
top: '50%',
transform: 'translateY(-50%)',
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create('left', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.standard,
}),
}}
>
<Fab
size="small"
color="primary"
onClick={() => setDrawerOpen(!drawerOpen)}
sx={{
borderRadius: '0 50% 50% 0',
width: 32,
height: 64,
minHeight: 64,
boxShadow: 2,
'&:hover': {
boxShadow: 4,
}
}}
>
{drawerOpen ? <ChevronLeft /> : <ChatIcon />}
</Fab>
</Box>
);
return (
<Box ref={ref} sx={{ width: "100%", height: "100%", display: "flex", flexGrow: 1, flexDirection: "column" }}>
{candidate && <CandidateInfo action={`Chat with Backstory about ${candidate.firstName}`} elevation={4} candidate={candidate} sx={{ minHeight: "max-content" }} />}
< Box sx={{ display: "flex", mt: 1, gap: 1, height: "100%" }}>
{/* Sessions Sidebar */}
<Paper sx={{ p: 2, height: '100%', minWidth: { sm: "200px", md: "300px", lg: "400px" }, display: 'flex', flexDirection: 'column' }}>
<Typography variant="h6" gutterBottom>
Chat Sessions
{sessions && (
<Chip
size="small"
label={`${sessions.sessions.total} total`}
sx={{ ml: 1 }}
/>
)}
</Typography>
<Box sx={{ display: "flex", mt: 1, gap: 1, height: "100%", position: 'relative' }}>
{/* Drawer */}
<Drawer
variant="persistent"
anchor="left"
open={drawerOpen}
sx={{
width: drawerOpen ? DRAWER_WIDTH : 0,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: DRAWER_WIDTH,
boxSizing: 'border-box',
position: 'relative',
height: '100%',
border: 'none',
borderRight: '1px solid',
borderColor: 'divider',
},
}}
>
{drawerContent}
</Drawer>
<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>
{/* Drawer Handle */}
{drawerHandle}
{/* Chat Interface */}
<Paper sx={{ display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
<Paper
sx={{
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.standard,
}),
}}
>
{chatSession?.id ? (
<>
{/* Messages Area */}