105 lines
3.6 KiB
TypeScript
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
|
|
};
|
|
|