/** * JobForm.jsx * The main HR input form: job details, filters, and drag-and-drop resume upload. */ import { useState, useRef, useCallback } from 'react'; import { Briefcase, FileText, Star, Clock, GraduationCap, Award, MapPin, Globe, Upload, X, ChevronDown, ChevronUp, Zap, Users } from 'lucide-react'; export default function JobForm({ onSubmit, loading }) { // ── Job Details ───────────────────────────────────────────────────────── const [jobTitle, setJobTitle] = useState(''); const [jobDescription, setJobDescription] = useState(''); const [requiredSkills, setRequiredSkills] = useState(''); const [experienceYears, setExperienceYears] = useState(''); const [education, setEducation] = useState(''); const [certifications, setCertifications] = useState(''); const [topN, setTopN] = useState(10); // ── Filters ────────────────────────────────────────────────────────────── const [preferredLocation, setPreferredLocation] = useState(''); const [preferredLanguages, setPreferredLanguages] = useState(''); const [filtersOpen, setFiltersOpen] = useState(false); // ── Resume Upload ──────────────────────────────────────────────────────── const [files, setFiles] = useState([]); const [resumeText, setResumeText] = useState(''); const [dragging, setDragging] = useState(false); const fileInputRef = useRef(null); // ── Drag and Drop handlers ─────────────────────────────────────────────── const handleDrop = useCallback((e) => { e.preventDefault(); setDragging(false); const dropped = Array.from(e.dataTransfer.files).filter(f => f.type === 'application/pdf'); setFiles(prev => { const names = new Set(prev.map(f => f.name)); return [...prev, ...dropped.filter(f => !names.has(f.name))]; }); }, []); const handleDragOver = (e) => { e.preventDefault(); setDragging(true); }; const handleDragLeave = () => setDragging(false); const handleFileChange = (e) => { const selected = Array.from(e.target.files).filter(f => f.type === 'application/pdf'); setFiles(prev => { const names = new Set(prev.map(f => f.name)); return [...prev, ...selected.filter(f => !names.has(f.name))]; }); e.target.value = ''; }; const removeFile = (name) => setFiles(prev => prev.filter(f => f.name !== name)); // ── Form submit ────────────────────────────────────────────────────────── const handleSubmit = (e) => { e.preventDefault(); if (!files.length && !resumeText.trim()) { alert('Please upload at least one resume PDF or paste resume text.'); return; } const formData = new FormData(); formData.append('job_title', jobTitle); formData.append('job_description', jobDescription); formData.append('required_skills', requiredSkills); formData.append('experience_years', experienceYears || '0'); formData.append('education', education); formData.append('certifications', certifications); formData.append('top_n', topN); formData.append('preferred_location', preferredLocation); formData.append('preferred_languages', preferredLanguages); formData.append('resume_text', resumeText); // Append all PDF files if (files.length > 0) { files.forEach(f => formData.append('resumes', f)); } else { // Append a blank file so FastAPI 'resumes' field isn't empty formData.append('resumes', new Blob([]), 'placeholder.pdf'); } onSubmit(formData); }; return (
{/* ── Header ── */}

New Screening

Configure job requirements and upload candidate resumes

AI-Powered
{/* ── Job Details Card ── */}

Job Details

{/* Job Title */}
setJobTitle(e.target.value)} required />
{/* Top N */}
setTopN(parseInt(e.target.value) || 10)} />
{/* Job Description */}