import React, { useState, useRef, JSX } from 'react'; import { Box, Button, Typography, TextField, Grid, useTheme, useMediaQuery, Chip, Card, CardContent, CardHeader, LinearProgress, Stack, } from '@mui/material'; import { AutoFixHigh, Psychology, Build, CloudUpload, Description, Business, Work, CheckCircle, Star, } from '@mui/icons-material'; import { styled } from '@mui/material/styles'; import FileUploadIcon from '@mui/icons-material/FileUpload'; import { useAuth } from 'hooks/AuthContext'; import { useAppState } from 'hooks/GlobalContext'; import { BackstoryElementProps } from './BackstoryTab'; import * as Types from 'types/types'; import { StyledMarkdown } from './StyledMarkdown'; import { JobInfo } from './ui/JobInfo'; import { Scrollable } from './Scrollable'; import { StatusIcon, StatusBox } from 'components/ui/StatusIcon'; const VisuallyHiddenInput = styled('input')({ clip: 'rect(0 0 0 0)', clipPath: 'inset(50%)', height: 1, overflow: 'hidden', position: 'absolute', bottom: 0, left: 0, whiteSpace: 'nowrap', width: 1, }); const UploadBox = styled(Box)(({ theme }) => ({ border: `2px dashed ${theme.palette.primary.main}`, borderRadius: (typeof theme.shape.borderRadius === 'string' ? parseInt(theme.shape.borderRadius) : theme.shape.borderRadius) * 2, padding: theme.spacing(4), textAlign: 'center', backgroundColor: theme.palette.action.hover, transition: 'all 0.3s ease', cursor: 'pointer', '&:hover': { backgroundColor: theme.palette.action.selected, borderColor: theme.palette.primary.dark, }, })); interface JobCreatorProps extends BackstoryElementProps { onSave?: (job: Types.Job) => void; } const JobCreator = (props: JobCreatorProps): JSX.Element => { const { user, apiClient } = useAuth(); const { onSave } = props; const { setSnack } = useAppState(); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('sm')); const [jobDescription, setJobDescription] = useState(''); const [jobRequirements, setJobRequirements] = useState(null); const [jobTitle, setJobTitle] = useState(''); const [company, setCompany] = useState(''); const [summary, setSummary] = useState(''); const [job, setJob] = useState(null); const [jobStatus, setJobStatus] = useState(''); const [jobStatusType, setJobStatusType] = useState(null); const [isProcessing, setIsProcessing] = useState(false); const fileInputRef = useRef(null); const jobStatusHandlers = { onStatus: (status: Types.ChatMessageStatus): void => { console.log('status:', status.content); setJobStatusType(status.activity); setJobStatus(status.content); }, onMessage: (jobMessage: Types.JobRequirementsMessage): void => { const job: Types.Job = jobMessage.job; console.log('onMessage - job', job); setJob(job); setCompany(job.company || ''); setJobDescription(job.description); setSummary(job.summary || ''); setJobTitle(job.title || ''); setJobRequirements(job.requirements || null); setJobStatusType(null); setJobStatus(''); }, onError: (error: Types.ChatMessageError): void => { console.log('onError', error); setSnack(error.content, 'error'); setIsProcessing(false); }, onComplete: (): void => { setJobStatusType(null); setJobStatus(''); setIsProcessing(false); }, }; const handleJobUpload = async (e: React.ChangeEvent): Promise => { if (e.target.files && e.target.files[0]) { const file = e.target.files[0]; const fileExtension = '.' + file.name.split('.').pop()?.toLowerCase(); let docType: Types.DocumentType | null = null; switch (fileExtension.substring(1)) { case 'pdf': docType = 'pdf'; break; case 'docx': docType = 'docx'; break; case 'md': docType = 'markdown'; break; case 'txt': docType = 'txt'; break; } if (!docType) { setSnack('Invalid file type. Please upload .txt, .md, .docx, or .pdf files only.', 'error'); return; } try { setIsProcessing(true); setJobDescription(''); setJobTitle(''); setJobRequirements(null); setSummary(''); const controller = apiClient.createJobFromFile(file, jobStatusHandlers); const job = await controller.promise; if (!job) { return; } console.log(`Job id: ${job.id}`); e.target.value = ''; } catch (error) { console.error(error); setSnack('Failed to upload document', 'error'); setIsProcessing(false); } } }; const handleUploadClick = (): void => { fileInputRef.current?.click(); }; const renderRequirementSection = ( title: string, items: string[] | undefined, icon: JSX.Element, required = false ): JSX.Element => { if (!items || items.length === 0) return <>; return ( {icon} {title} {required && } {items.map((item, index) => ( ))} ); }; const renderJobRequirements = (): JSX.Element => { if (!jobRequirements) return <>; return ( } sx={{ pb: 1 }} /> {renderRequirementSection( 'Technical Skills (Required)', jobRequirements.technicalSkills.required, , true )} {renderRequirementSection( 'Technical Skills (Preferred)', jobRequirements.technicalSkills.preferred, )} {renderRequirementSection( 'Experience Requirements (Required)', jobRequirements.experienceRequirements.required, , true )} {renderRequirementSection( 'Experience Requirements (Preferred)', jobRequirements.experienceRequirements.preferred, )} {renderRequirementSection( 'Soft Skills', jobRequirements.softSkills, )} {renderRequirementSection( 'Experience', jobRequirements.experience, )} {renderRequirementSection( 'Education', jobRequirements.education, )} {renderRequirementSection( 'Certifications', jobRequirements.certifications, )} {renderRequirementSection( 'Preferred Attributes', jobRequirements.preferredAttributes, )} ); }; const handleSave = async (): Promise => { const newJob: Types.Job = { ownerId: user?.id || '', ownerType: 'candidate', description: jobDescription, company: company, summary: summary, title: jobTitle, requirements: jobRequirements || undefined, createdAt: new Date(), updatedAt: new Date(), }; setIsProcessing(true); const job = await apiClient.createJob(newJob); setIsProcessing(false); if (!job) { setSnack('Failed to save job', 'error'); return; } onSave && onSave(job); }; const handleExtractRequirements = async (): Promise => { try { setIsProcessing(true); const controller = apiClient.createJobFromDescription(jobDescription, jobStatusHandlers); const job = await controller.promise; if (!job) { setIsProcessing(false); return; } console.log(`Job id: ${job.id}`); } catch (error) { console.error(error); setSnack('Failed to upload document', 'error'); setIsProcessing(false); } setIsProcessing(false); }; const renderJobCreation = (): JSX.Element => { return ( {/* Upload Section */} } /> Upload Job Description Drop your job description here Supported formats: PDF, DOCX, TXT, MD Or Enter Manually { setJobDescription(e.target.value); }} disabled={isProcessing} sx={{ mb: 2 }} /> {jobRequirements === null && jobDescription && ( )} {(jobStatus || isProcessing) && ( {jobStatusType && } {jobStatus || 'Processing...'} {isProcessing && } )} {/* Job Details Section */} } /> { setJobTitle(e.target.value); }} required disabled={isProcessing} InputProps={{ startAdornment: , }} /> { setCompany(e.target.value); }} required disabled={isProcessing} InputProps={{ startAdornment: , }} /> {/* setJobLocation(e.target.value)} disabled={isProcessing} InputProps={{ startAdornment: }} /> */} {/* Job Summary */} {summary !== '' && ( } sx={{ pb: 1 }} /> {summary} )} {/* Requirements Display */} {renderJobRequirements()} ); }; return ( {job === null && renderJobCreation()} {job && ( *:not(.Scrollable)': { flexShrink: 0 /* Prevent shrinking */, }, position: 'relative', }} > )} ); }; export { JobCreator };