"use client"; import React, { useState, useEffect } from "react"; interface Job { id: string; title: string; requirements: { text: string }; created_at: string; } interface Score { id: string; candidate_id: string; ai_score: number; evaluation: { summary: string; classification: string; suggestions: string; riskLevel: string; }; } interface Candidate { id: string; name: string; contact_info: { email: string; phone: string; }; similarity?: number; scores?: Score[]; created_at: string; } export default function JobsPage() { const [jobs, setJobs] = useState([]); const [selectedJob, setSelectedJob] = useState(null); const [matches, setMatches] = useState([]); const [loadingJobs, setLoadingJobs] = useState(true); const [loadingMatches, setLoadingMatches] = useState(false); const [uploading, setUploading] = useState(false); const [uploadError, setUploadError] = useState(null); const [uploadSuccess, setUploadSuccess] = useState(null); // Form states const [newTitle, setNewTitle] = useState(""); const [newRequirements, setNewRequirements] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [formError, setFormError] = useState(null); // Fetch all jobs on mount useEffect(() => { let active = true; fetch("/api/jobs") .then((res) => { if (!res.ok) throw new Error("Failed to fetch jobs"); return res.json(); }) .then((data) => { if (active) { setJobs(data); setLoadingJobs(false); if (data.length > 0) { setSelectedJob(data[0]); } } }) .catch((err) => { console.error(err); if (active) { setLoadingJobs(false); } }); return () => { active = false; }; }, []); // Fetch candidates/matches when selected job changes useEffect(() => { let active = true; if (!selectedJob) { Promise.resolve().then(() => { if (active) setMatches([]); }); return; } Promise.resolve().then(() => { if (active) setLoadingMatches(true); }); fetch(`/api/candidates?jobId=${selectedJob.id}`) .then((res) => { if (!res.ok) throw new Error("Failed to fetch candidate matches"); return res.json(); }) .then((data) => { if (active) { setMatches(data); setLoadingMatches(false); } }) .catch((err) => { console.error(err); if (active) { setLoadingMatches(false); } }); return () => { active = false; }; }, [selectedJob]); // Create vacancy const handleCreateJob = async (e: React.FormEvent) => { e.preventDefault(); if (!newTitle.trim() || !newRequirements.trim()) { setFormError("All fields are required"); return; } try { setIsSubmitting(true); setFormError(null); const res = await fetch("/api/jobs", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: newTitle, requirements: newRequirements }), }); if (!res.ok) { const errData = await res.json(); throw new Error(errData.error || "Failed to create vacancy"); } const newJob = await res.json(); setJobs((prev) => [newJob, ...prev]); setSelectedJob(newJob); setNewTitle(""); setNewRequirements(""); } catch (err: unknown) { setFormError(err instanceof Error ? err.message : "Error creating job"); } finally { setIsSubmitting(false); } }; // Upload PDF CV const handleFileUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file || !selectedJob) return; if (file.type !== "application/pdf") { setUploadError("Please upload a PDF file"); return; } try { setUploading(true); setUploadError(null); setUploadSuccess(null); const formData = new FormData(); formData.append("file", file); formData.append("jobId", selectedJob.id); const res = await fetch("/candidates/api/parse-cv", { method: "POST", body: formData, }); if (!res.ok) { const errData = await res.json(); throw new Error(errData.error || "Failed to process CV"); } setUploadSuccess(`CV for ${file.name} successfully parsed and indexed!`); // Refresh matches for current job const matchesRes = await fetch(`/api/candidates?jobId=${selectedJob.id}`); if (matchesRes.ok) { const matchesData = await matchesRes.json(); setMatches(matchesData); } } catch (err: unknown) { setUploadError(err instanceof Error ? err.message : "Error uploading CV"); } finally { setUploading(false); // Clear file input e.target.value = ""; } }; return (
{/* Left Column: Create Form & Vacancies List */}
{/* Create vacancy form */}

Create Vacancy

setNewTitle(e.target.value)} placeholder="e.g., Senior React Developer" className="w-full px-3 py-2 border border-slate-200 rounded-md text-slate-900 bg-white placeholder:text-slate-500 text-sm focus:outline-none" required />