diff --git a/frontend/src/components/JobMatchAnalysis.tsx b/frontend/src/components/JobMatchAnalysis.tsx index 8564006..6ed79ce 100644 --- a/frontend/src/components/JobMatchAnalysis.tsx +++ b/frontend/src/components/JobMatchAnalysis.tsx @@ -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 = (props: JobAnalysisProps) = const { job, candidate, + onAnalysisComplete, } = props const { apiClient } = useAuth(); const { setSnack } = useAppState(); @@ -138,7 +140,7 @@ const JobMatchAnalysis: React.FC = (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 = (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 = (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 = (props: JobAnalysisProps) = - {} + {} {overallScore !== 0 && <> Overall Match: diff --git a/frontend/src/components/ResumeGenerator.tsx b/frontend/src/components/ResumeGenerator.tsx new file mode 100644 index 0000000..5391268 --- /dev/null +++ b/frontend/src/components/ResumeGenerator.tsx @@ -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 = (props: ResumeGeneratorProps) => { + const { job, candidate, skills, onComplete } = props; + const [resume, setResume] = useState('Generated resume goes here...'); + const [generating, setGenerating] = useState(false); + // State for editing job description + + const generateResume = () => { + setResume('Generation begins...'); + setGenerating(true); + setTimeout(() => { + setGenerating(false); + setResume('Generation complete'); + onComplete && onComplete(resume); + }, 3000); + }; + + return ( + + + + {resume} + + + + ) + +}; + +export { + ResumeGenerator +}; + diff --git a/frontend/src/components/ui/CandidatePicker.tsx b/frontend/src/components/ui/CandidatePicker.tsx index 6546fac..35e20c9 100644 --- a/frontend/src/components/ui/CandidatePicker.tsx +++ b/frontend/src/components/ui/CandidatePicker.tsx @@ -50,17 +50,6 @@ const CandidatePicker = (props: CandidatePickerProps) => { return ( - {user?.isAdmin && - - Not seeing a candidate you like? - - - } {candidates?.map((u, i) => = (props: JobInfoProps) => { Title: {job.title} } - {/* {job.datePosted && - - Posted: {job.datePosted.toISOString()} - - } */} - {job.company && - - Company: {job.company} - - } - {job.summary && - Summary: {job.summary} - - } + {job.company && + + Company: {job.company} + + } + {job.summary && + Summary: {job.summary} + + } + {job.createdAt && + Created: {job.createdAt.toISOString()} + + } + { job.owner && + Created by: {job.owner.fullName} + + } } diff --git a/frontend/src/pages/JobAnalysisPage.tsx b/frontend/src/pages/JobAnalysisPage.tsx index ec87eb7..4d24c9e 100644 --- a/frontend/src/pages/JobAnalysisPage.tsx +++ b/frontend/src/pages/JobAnalysisPage.tsx @@ -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 = (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(null); const [jobTab, setJobTab] = useState('load'); - + const [skills, setSkills] = useState(null) useEffect(() => { - console.log({ activeStep, selectedCandidate, selectedJob }); - if (!selectedCandidate) { if (activeStep !== 0) { setActiveStep(0); @@ -109,8 +106,9 @@ const JobAnalysisPage: React.FC = (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 = (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 = (props: BackstoryPageProps ); } + const onAnalysisComplete = (skills: SkillAssessment[]) => { + setSkills(skills); + }; + // Render function for the analysis step const renderAnalysis = () => ( @@ -205,6 +211,7 @@ const JobAnalysisPage: React.FC = (props: BackstoryPageProps )} @@ -212,7 +219,12 @@ const JobAnalysisPage: React.FC = (props: BackstoryPageProps const renderResume = () => ( - {selectedCandidate && Resume Builder} + {skills && selectedCandidate && selectedJob && + } ); @@ -298,7 +310,7 @@ const JobAnalysisPage: React.FC = (props: BackstoryPageProps ) : ( )}