const $ = (id) => document.getElementById(id); const cvFile = $("cvFile"); const dropzone = $("dropzone"); const fileLabel = $("fileLabel"); const extractButton = $("extractButton"); const scoreButton = $("scoreButton"); const status = $("status"); const preview = $("preview"); const result = $("result"); let selectedFile = null; function setStatus(message, type = "") { status.textContent = message; status.className = `status ${type}`.trim(); } function value(id) { return $(id).value.trim(); } function setSelectedFile(file) { if (!file) return; selectedFile = file; fileLabel.textContent = file.name; extractButton.disabled = false; setStatus("File selected. Click Extract CV details to fill the form."); } function applyCandidate(candidate) { const mapping = { candidateRole: candidate.role, candidateSeniority: candidate.seniority, yearsExperience: candidate.years_experience, candidateIndustry: candidate.industry, education: candidate.education, candidateSkills: candidate.skills, summary: candidate.summary, experienceBullets: candidate.experience_bullets, }; for (const [id, fieldValue] of Object.entries(mapping)) $(id).value = fieldValue || ""; } async function extractCv() { if (!selectedFile) return; extractButton.disabled = true; setStatus("Uploading CV to the FastAPI service for extraction…"); try { const body = new FormData(); body.append("file", selectedFile); const response = await fetch("/api/extract-cv", { method: "POST", body }); const data = await response.json(); if (!response.ok) throw new Error(data.detail || "Could not read this CV."); preview.value = data.preview || ""; applyCandidate(data.candidate || {}); const filled = Object.values(data.candidate || {}).filter(Boolean).length; const warning = data.warnings?.length ? ` ${data.warnings.join(" ")}` : ""; setStatus(`Extracted ${filled} of 8 candidate fields. Please review them before scoring.${warning}`, data.warnings?.length ? "" : "success"); } catch (error) { setStatus(error.message || "Could not read this CV.", "error"); } finally { extractButton.disabled = false; } } function buildPayload() { return { job: { job_title: value("jobTitle"), seniority: value("jobSeniority"), industry: value("jobIndustry"), must_have_skills: value("mustHaveSkills"), nice_to_have_skills: value("niceToHaveSkills"), description: value("jobDescription"), responsibilities: value("responsibilities"), requirements: value("requirements"), }, candidate: { role: value("candidateRole"), seniority: value("candidateSeniority"), years_experience: value("yearsExperience"), industry: value("candidateIndustry"), education: value("education"), skills: value("candidateSkills"), summary: value("summary"), experience_bullets: value("experienceBullets"), }, }; } async function scoreMatch() { scoreButton.disabled = true; scoreButton.textContent = "Scoring…"; setStatus("FastAPI is scoring the job and candidate profile…"); result.hidden = true; try { const response = await fetch("/api/score", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(buildPayload()), }); const data = await response.json(); if (!response.ok) throw new Error(data.detail || "Scoring failed."); const percent = (data.probability * 100).toFixed(1); result.innerHTML = `

${data.verdict}

Model-estimated relevance for this job and candidate profile.

${percent}%

Decision threshold: ${(data.threshold * 100).toFixed(1)}%

${data.disclaimer}

`; result.hidden = false; setStatus("Match scored by FastAPI.", "success"); } catch (error) { setStatus(error.message || "Scoring failed.", "error"); } finally { scoreButton.disabled = false; scoreButton.innerHTML = 'Score match →'; } } cvFile.addEventListener("change", (event) => setSelectedFile(event.target.files[0])); extractButton.addEventListener("click", extractCv); scoreButton.addEventListener("click", scoreMatch); dropzone.addEventListener("dragover", (event) => { event.preventDefault(); dropzone.classList.add("dragging"); }); dropzone.addEventListener("dragleave", () => dropzone.classList.remove("dragging")); dropzone.addEventListener("drop", (event) => { event.preventDefault(); dropzone.classList.remove("dragging"); setSelectedFile(event.dataTransfer.files[0]); });