import React, { useState, useRef, JSX } from 'react'; import { Box, Button, Typography, TextField, Grid, useTheme, useMediaQuery, Chip, Card, CardContent, CardHeader, LinearProgress, Stack, Paper, } from '@mui/material'; import { SyncAlt, Favorite, Settings, Info, Search, AutoFixHigh, Image, 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, useSelectedJob } from 'hooks/GlobalContext'; import { BackstoryElementProps } from './BackstoryTab'; import { LoginRequired } from 'components/ui/LoginRequired'; 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: 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) => { const { user, apiClient } = useAuth(); const { onSave } = props; const { selectedJob, setSelectedJob } = useSelectedJob(); 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) => { console.log('status:', status.content); setJobStatusType(status.activity); setJobStatus(status.content); }, onMessage: (jobMessage: Types.JobRequirementsMessage) => { 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) => { console.log('onError', error); setSnack(error.content, "error"); setIsProcessing(false); }, onComplete: () => { setJobStatusType(null); setJobStatus(''); setIsProcessing(false); } }; const handleJobUpload = async (e: React.ChangeEvent) => { 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 = () => { fileInputRef.current?.click(); }; const renderRequirementSection = (title: string, items: string[] | undefined, icon: JSX.Element, required = false) => { if (!items || items.length === 0) return null; return ( {icon} {title} {required && } {items.map((item, index) => ( ))} ); }; const renderJobRequirements = () => { if (!jobRequirements) return null; 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 () => { 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 () => { 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 = () => { 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 };