"use client"; import React, { useState, useEffect } from "react"; interface Score { id: string; candidate_id: string; ai_score: number; evaluation: { summary: string; classification: string; suggestions: string; riskLevel: string; }; created_at: string; } interface Candidate { id: string; name: string; contact_info: { email: string; phone: string; skills?: string[]; summary?: string; }; scores?: Score[]; created_at: string; } export default function CandidatesPage() { const [candidates, setCandidates] = useState([]); const [loading, setLoading] = useState(true); const [uploading, setUploading] = useState(false); const [uploadError, setUploadError] = useState(null); const [uploadSuccess, setUploadSuccess] = useState(null); const fetchCandidates = () => { fetch("/api/candidates") .then((res) => { if (!res.ok) throw new Error("Failed to fetch candidates"); return res.json(); }) .then((data) => { setCandidates(data); setLoading(false); }) .catch((err) => { console.error(err); setLoading(false); }); }; useEffect(() => { fetchCandidates(); }, []); // Upload PDF CV in a vacuum const handleFileUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) 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); 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"); } const resData = await res.json(); setUploadSuccess(`CV for ${resData.candidateName || file.name} successfully parsed!`); fetchCandidates(); } catch (err: unknown) { setUploadError(err instanceof Error ? err.message : "Error uploading CV"); } finally { setUploading(false); e.target.value = ""; } }; return (

Candidates

A list of all candidates parsed and analyzed by the AI recruitment pipeline.

{/* CV Uploader (Vacuum Ingestion) */}

Ingest Candidate CV (in a Vacuum)

Upload a candidate CV PDF to parse contact info, skills, and summary. No job position will be associated initially, keeping the data isolated.

{uploadError && (

{uploadError}

)} {uploadSuccess && (

{uploadSuccess}

)}
{loading ? (

Loading candidates...

) : candidates.length === 0 ? (

No candidates found. Upload a CV above to get started.

) : (
{candidates.map((candidate) => { return (
{/* Candidate Info Header */}

{candidate.name}

Email:{" "} {candidate.contact_info.email} Phone:{" "} {candidate.contact_info.phone}
{/* Extracted Profile (Summary & Skills) */}
{candidate.contact_info.summary && (
Professional Summary (Extracted)

{candidate.contact_info.summary}

)} {candidate.contact_info.skills && candidate.contact_info.skills.length > 0 && (
Skills & Technologies
{candidate.contact_info.skills.map((skill) => ( {skill} ))}
)}
); })}
)}
); }