"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; }; scores?: Score[]; created_at: string; } export default function CandidatesPage() { const [candidates, setCandidates] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { let active = true; fetch("/api/candidates") .then((res) => { if (!res.ok) throw new Error("Failed to fetch candidates"); return res.json(); }) .then((data) => { if (active) { setCandidates(data); setLoading(false); } }) .catch((err) => { console.error(err); if (active) { setLoading(false); } }); return () => { active = false; }; }, []); return (

Candidates

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

{loading ? (

Loading candidates...

) : candidates.length === 0 ? (

No candidates found. Upload CVs on the Jobs tab to parse them.

) : (
{candidates.map((candidate) => { // Get the latest score const latestScore = candidate.scores && candidate.scores.length > 0 ? candidate.scores[0] : null; return (
{/* Candidate Info Header */}

{candidate.name}

Email:{" "} {candidate.contact_info.email} | Phone:{" "} {candidate.contact_info.phone}
{latestScore ? (
AI Assessment {latestScore.ai_score} / 10
{latestScore.evaluation.classification}
) : (
Pending Evaluation
)}
{/* Score details if available */} {latestScore ? (
AI Summary

{latestScore.evaluation.summary}

Risk Level:{" "} {latestScore.evaluation.riskLevel}
Action Items / Suggestions

{latestScore.evaluation.suggestions}

) : (

This candidate's CV has been indexed, but the AI evaluation has not yet completed. The background n8n workflow updates scores upon completion.

)}
); })}
)}
); }