backstory/frontend/src/components/ResumeGenerator.tsx

105 lines
3.6 KiB
TypeScript

import React, { useState, useCallback, useRef, useEffect } from 'react';
import {
Tabs,
Tab,
Box,
Button,
Paper,
Typography,
} from '@mui/material';
import { Job, Candidate, SkillAssessment } from "types/types";
import { Scrollable } from './Scrollable';
import { useAuth } from 'hooks/AuthContext';
import * as Types from 'types/types';
import { StyledMarkdown } from './StyledMarkdown';
import { Message } from './Message';
import InputIcon from '@mui/icons-material/Input';
import TuneIcon from '@mui/icons-material/Tune';
import ArticleIcon from '@mui/icons-material/Article';
interface ResumeGeneratorProps {
job: Job;
candidate: Candidate;
skills: SkillAssessment[];
onComplete?: (resume: string) => void;
}
const defaultMessage: Types.ChatMessageStatus = {
status: "done", type: "text", sessionId: "", timestamp: new Date(), content: "", activity: 'info'
};
const ResumeGenerator: React.FC<ResumeGeneratorProps> = (props: ResumeGeneratorProps) => {
const { job, candidate, skills, onComplete } = props;
const { apiClient, user } = useAuth();
const [resume, setResume] = useState<string>('');
const [prompt, setPrompt] = useState<string>('');
const [systemPrompt, setSystemPrompt] = useState<string>('');
const [generating, setGenerating] = useState<boolean>(false);
const [statusMessage, setStatusMessage] = useState<Types.ChatMessageStatus | null>(null);
const [tabValue, setTabValue] = useState<string>('resume');
const handleTabChange = (event: React.SyntheticEvent, newValue: string) => {
setTabValue(newValue);
}
// State for editing job description
const generateResumeHandlers = {
onStatus: (status: Types.ChatMessageStatus) => {
setStatusMessage({ ...defaultMessage, content: status.content.toLowerCase() });
},
onStreaming: (chunk: Types.ChatMessageStreaming) =>{
setResume(chunk.content);
},
onComplete: () => {
setStatusMessage(null);
}
};
useEffect(() => {
if (!job || !candidate || !skills || resume || generating) {
return;
}
const generateResume = async () => {
const request : any = await apiClient.generateResume(candidate.id || '', skills, generateResumeHandlers);
const result = await request.promise;
setSystemPrompt(result.systemPrompt)
setPrompt(result.prompt)
setResume(result.resume)
};
setGenerating(true);
generateResume().then(() =>{
setGenerating(false);
});
}, [job, candidate, apiClient, resume, skills, generating]);
return (
<Box
className="ResumeGenerator"
sx={{
display: "flex",
flexDirection: "column",
}}>
{user?.isAdmin && <Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }}>
<Tabs value={tabValue} onChange={handleTabChange} centered>
<Tab sx={{ display: systemPrompt ? "flex" : "none" }} value="system" icon={<TuneIcon />} label="System" />
<Tab sx={{ display: prompt ? "flex" : "none" }} value="prompt" icon={<InputIcon />} label="Prompt" />
<Tab sx={{ display: resume ? "flex" : "none" }} value="resume" icon={<ArticleIcon />} label="Resume" />
</Tabs>
</Box>}
{statusMessage && <Message message={statusMessage} />}
<Paper elevation={3} sx={{ p: 3, m: 4, mt: 0 }}><Scrollable autoscroll sx={{ display: "flex", flexGrow: 1 }}>
{tabValue === 'system' && <pre>{systemPrompt}</pre>}
{tabValue === 'prompt' && <pre>{prompt}</pre>}
{tabValue === 'resume' && <StyledMarkdown content={resume} />}
</Scrollable></Paper>
</Box>
)
};
export {
ResumeGenerator
};