Integrating new resume generator
This commit is contained in:
parent
1fbc5317d3
commit
dd0ab5eda6
@ -34,6 +34,7 @@ import * as Types from 'types/types';
|
||||
interface JobAnalysisProps extends BackstoryPageProps {
|
||||
job: Job;
|
||||
candidate: Candidate;
|
||||
onAnalysisComplete: (skills: SkillAssessment[]) => void;
|
||||
}
|
||||
|
||||
const defaultMessage: ChatMessage = {
|
||||
@ -50,6 +51,7 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
|
||||
const {
|
||||
job,
|
||||
candidate,
|
||||
onAnalysisComplete,
|
||||
} = props
|
||||
const { apiClient } = useAuth();
|
||||
const { setSnack } = useAppState();
|
||||
@ -138,7 +140,7 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchMatchData = async () => {
|
||||
const fetchMatchData = async (skills: SkillAssessment[]) => {
|
||||
if (requirements.length === 0) return;
|
||||
|
||||
// Process requirements one by one
|
||||
@ -153,6 +155,7 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
|
||||
const request: any = await apiClient.candidateMatchForRequirement(candidate.id || '', requirements[i].requirement, skillMatchHandlers);
|
||||
const result = await request.promise;
|
||||
const skillMatch = result.skillAssessment;
|
||||
skills.push(skillMatch);
|
||||
setMatchStatus('');
|
||||
let matchScore: number = 0;
|
||||
switch (skillMatch.evidenceStrength.toUpperCase()) {
|
||||
@ -201,8 +204,13 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
|
||||
};
|
||||
|
||||
setAnalyzing(true);
|
||||
fetchMatchData().then(() => { setAnalyzing(false); setStartAnalysis(false) });
|
||||
}, [job, startAnalysis, analyzing, requirements, loadingRequirements]);
|
||||
const skills: SkillAssessment[] = [];
|
||||
fetchMatchData(skills).then(() => {
|
||||
setAnalyzing(false);
|
||||
setStartAnalysis(false);
|
||||
onAnalysisComplete && onAnalysisComplete(skills);
|
||||
});
|
||||
}, [job, onAnalysisComplete, startAnalysis, analyzing, requirements, loadingRequirements]);
|
||||
|
||||
// Get color based on match score
|
||||
const getMatchColor = (score: number): string => {
|
||||
@ -270,7 +278,7 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
|
||||
|
||||
<Grid size={{ xs: 12 }} sx={{ mt: 2 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2, gap: 1 }}>
|
||||
{<Button disabled={analyzing || startAnalysis} onClick={beginAnalysis} variant="contained">Start Analysis</Button>}
|
||||
{<Button disabled={analyzing || startAnalysis} onClick={beginAnalysis} variant="contained">Start Skill Assessment</Button>}
|
||||
{overallScore !== 0 && <>
|
||||
<Typography variant="h5" component="h2" sx={{ mr: 2 }}>
|
||||
Overall Match:
|
||||
|
51
frontend/src/components/ResumeGenerator.tsx
Normal file
51
frontend/src/components/ResumeGenerator.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React, { useState, useCallback, useRef } from 'react';
|
||||
import {
|
||||
Tabs,
|
||||
Tab,
|
||||
Box,
|
||||
Button,
|
||||
} from '@mui/material';
|
||||
import { Job, Candidate, SkillAssessment } from "types/types";
|
||||
import JsonView from '@uiw/react-json-view';
|
||||
|
||||
interface ResumeGeneratorProps {
|
||||
job: Job;
|
||||
candidate: Candidate;
|
||||
skills: SkillAssessment[];
|
||||
onComplete?: (resume: string) => void;
|
||||
}
|
||||
|
||||
const ResumeGenerator: React.FC<ResumeGeneratorProps> = (props: ResumeGeneratorProps) => {
|
||||
const { job, candidate, skills, onComplete } = props;
|
||||
const [resume, setResume] = useState<string>('Generated resume goes here...');
|
||||
const [generating, setGenerating] = useState<boolean>(false);
|
||||
// State for editing job description
|
||||
|
||||
const generateResume = () => {
|
||||
setResume('Generation begins...');
|
||||
setGenerating(true);
|
||||
setTimeout(() => {
|
||||
setGenerating(false);
|
||||
setResume('Generation complete');
|
||||
onComplete && onComplete(resume);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="ResumeGenerator"
|
||||
sx={{display: "flex", flexDirection: "row", width: "100%"}}>
|
||||
<JsonView value={skills}/>
|
||||
<Box sx={{display: "flex", flexDirection: "column"}}>
|
||||
<Box>{resume}</Box>
|
||||
<Button disabled={generating} onClick={generateResume} variant="contained">Generate Resume</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
|
||||
};
|
||||
|
||||
export {
|
||||
ResumeGenerator
|
||||
};
|
||||
|
@ -50,17 +50,6 @@ const CandidatePicker = (props: CandidatePickerProps) => {
|
||||
|
||||
return (
|
||||
<Box sx={{display: "flex", flexDirection: "column"}}>
|
||||
{user?.isAdmin &&
|
||||
<Box sx={{ p: 1, textAlign: "center" }}>
|
||||
Not seeing a candidate you like?
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{ m: 1 }}
|
||||
onClick={() => { navigate('/generate-candidate') }}>
|
||||
Generate your own perfect AI candidate!
|
||||
</Button>
|
||||
</Box>
|
||||
}
|
||||
<Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", justifyContent: "center" }}>
|
||||
{candidates?.map((u, i) =>
|
||||
<Box key={`${u.username}`}
|
||||
|
@ -72,11 +72,6 @@ const JobInfo: React.FC<JobInfoProps> = (props: JobInfoProps) => {
|
||||
<strong>Title:</strong> {job.title}
|
||||
</Typography>
|
||||
}
|
||||
{/* {job.datePosted &&
|
||||
<Typography variant="body2" sx={{ mb: 1 }}>
|
||||
<strong>Posted:</strong> {job.datePosted.toISOString()}
|
||||
</Typography>
|
||||
} */}
|
||||
{job.company &&
|
||||
<Typography variant="body2" sx={{ mb: 1 }}>
|
||||
<strong>Company:</strong> {job.company}
|
||||
@ -86,6 +81,14 @@ const JobInfo: React.FC<JobInfoProps> = (props: JobInfoProps) => {
|
||||
<strong>Summary:</strong> {job.summary}
|
||||
</Typography>
|
||||
}
|
||||
{job.createdAt && <Typography variant="body2">
|
||||
<strong>Created:</strong> {job.createdAt.toISOString()}
|
||||
</Typography>
|
||||
}
|
||||
{ job.owner && <Typography variant="body2">
|
||||
<strong>Created by:</strong> {job.owner.fullName}
|
||||
</Typography>
|
||||
}
|
||||
</>}
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
|
@ -22,7 +22,7 @@ import PersonIcon from '@mui/icons-material/Person';
|
||||
import WorkIcon from '@mui/icons-material/Work';
|
||||
import AssessmentIcon from '@mui/icons-material/Assessment';
|
||||
import { JobMatchAnalysis } from 'components/JobMatchAnalysis';
|
||||
import { Candidate, Job } from "types/types";
|
||||
import { Candidate, Job, SkillAssessment } from "types/types";
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { BackstoryPageProps } from 'components/BackstoryTab';
|
||||
import { useAuth } from 'hooks/AuthContext';
|
||||
@ -35,6 +35,8 @@ import { CandidatePicker } from 'components/ui/CandidatePicker';
|
||||
import { JobPicker } from 'components/ui/JobPicker';
|
||||
import { JobCreator } from 'components/JobCreator';
|
||||
import { LoginRestricted } from 'components/ui/LoginRestricted';
|
||||
import JsonView from '@uiw/react-json-view';
|
||||
import { ResumeGenerator } from 'components/ResumeGenerator';
|
||||
|
||||
function WorkAddIcon() {
|
||||
return (
|
||||
@ -65,19 +67,14 @@ function WorkAddIcon() {
|
||||
const JobAnalysisPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps) => {
|
||||
const theme = useTheme();
|
||||
const { user, guest } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const { selectedCandidate, setSelectedCandidate } = useSelectedCandidate()
|
||||
const { selectedJob, setSelectedJob } = useSelectedJob();
|
||||
const { setSnack } = useAppState();
|
||||
// State management
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
const [analysisStarted, setAnalysisStarted] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [jobTab, setJobTab] = useState<string>('load');
|
||||
|
||||
const [skills, setSkills] = useState<SkillAssessment[] | null>(null)
|
||||
useEffect(() => {
|
||||
console.log({ activeStep, selectedCandidate, selectedJob });
|
||||
|
||||
if (!selectedCandidate) {
|
||||
if (activeStep !== 0) {
|
||||
setActiveStep(0);
|
||||
@ -109,8 +106,9 @@ const JobAnalysisPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeStep === 2) {
|
||||
setAnalysisStarted(true);
|
||||
if (activeStep === 2 && !skills) {
|
||||
setError('Skill assessment must be complete before continuing.');
|
||||
return;
|
||||
}
|
||||
|
||||
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
||||
@ -128,15 +126,19 @@ const JobAnalysisPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps
|
||||
};
|
||||
|
||||
const moveToStep = (step: number) => {
|
||||
console.log(`Move to ${step}`)
|
||||
switch (step) {
|
||||
case 0: /* Select candidate */
|
||||
setSelectedCandidate(null);
|
||||
setSelectedJob(null);
|
||||
setSkills(null);
|
||||
break;
|
||||
case 1: /* Select Job */
|
||||
setSelectedJob(null);
|
||||
setSkills(null);
|
||||
break;
|
||||
case 2: /* Job Analysis */
|
||||
setSkills(null);
|
||||
break;
|
||||
case 3: /* Generate Resume */
|
||||
break;
|
||||
@ -198,6 +200,10 @@ const JobAnalysisPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps
|
||||
);
|
||||
}
|
||||
|
||||
const onAnalysisComplete = (skills: SkillAssessment[]) => {
|
||||
setSkills(skills);
|
||||
};
|
||||
|
||||
// Render function for the analysis step
|
||||
const renderAnalysis = () => (
|
||||
<Box sx={{ mt: 3 }}>
|
||||
@ -205,6 +211,7 @@ const JobAnalysisPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps
|
||||
<JobMatchAnalysis
|
||||
job={selectedJob}
|
||||
candidate={selectedCandidate}
|
||||
onAnalysisComplete={onAnalysisComplete}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
@ -212,7 +219,12 @@ const JobAnalysisPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps
|
||||
|
||||
const renderResume = () => (
|
||||
<Box sx={{ mt: 3 }}>
|
||||
{selectedCandidate && <ComingSoon>Resume Builder</ComingSoon>}
|
||||
{skills && selectedCandidate && selectedJob &&
|
||||
<ResumeGenerator
|
||||
job={selectedJob}
|
||||
candidate={selectedCandidate}
|
||||
skills={skills}
|
||||
/>}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@ -298,7 +310,7 @@ const JobAnalysisPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={handleNext} variant="contained">
|
||||
{activeStep === steps[steps.length - 1].index - 1 ? 'Done' : 'Next'}
|
||||
{activeStep === steps.length - 1 ? 'Done' : 'Next'}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
|
Loading…
x
Reference in New Issue
Block a user