| 'use client'; |
|
|
| import React, { useState } from 'react'; |
| import { motion } from 'framer-motion'; |
| import { X, ShieldCheck, Zap, Briefcase, Cpu, TrendingUp, Info, Search } from 'lucide-react'; |
|
|
| export default function CandidateDetail({ evaluation, onClose }) { |
| const [activeTab, setActiveTab] = useState('Synthesis'); |
|
|
| const agents = [ |
| { name: 'Synthesis', icon: ShieldCheck }, |
| { name: 'Signal Extraction', icon: Zap }, |
| { name: 'Explanation', icon: Info }, |
| { name: 'Founder Eval', icon: TrendingUp }, |
| { name: 'HR Agent', icon: Search }, |
| { name: 'Tech Agent', icon: Cpu }, |
| { name: 'Business Agent', icon: Briefcase }, |
| ]; |
|
|
| const getAgentContent = (name) => { |
| if (name === 'Synthesis') return evaluation.synthesis; |
| const agent = evaluation.agent_outputs.find(a => a.agent_name === name); |
| return agent ? agent.content : 'No output available for this agent.'; |
| }; |
|
|
| return ( |
| <motion.div |
| initial={{ opacity: 0 }} |
| animate={{ opacity: 1 }} |
| exit={{ opacity: 0 }} |
| className="fixed inset-0 z-[100] bg-black/40 backdrop-blur-sm flex justify-end" |
| onClick={onClose} |
| > |
| <motion.div |
| initial={{ x: '100%' }} |
| animate={{ x: 0 }} |
| exit={{ x: '100%' }} |
| transition={{ type: 'spring', damping: 30, stiffness: 300 }} |
| className="w-full max-w-2xl bg-white dark:bg-slate-950 h-full shadow-2xl flex flex-col" |
| onClick={e => e.stopPropagation()} |
| > |
| {/* Header */} |
| <div className="p-8 border-b border-slate-100 dark:border-slate-800 flex justify-between items-start"> |
| <div> |
| <span className="text-xs font-bold text-indigo-600 uppercase tracking-widest mb-1 block">Full Report</span> |
| <h2 className="text-3xl font-bold text-slate-900 dark:text-white">{evaluation.name}</h2> |
| <div className="flex items-center gap-4 mt-2"> |
| <div className="flex items-center gap-1.5"> |
| <div className="w-2 h-2 rounded-full bg-emerald-500"></div> |
| <span className="text-sm font-medium text-slate-500">{evaluation.decision}</span> |
| </div> |
| <div className="text-sm text-slate-400">Score: {Math.round(evaluation.final_score)}/100</div> |
| </div> |
| </div> |
| <button |
| onClick={onClose} |
| className="p-2 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-full transition-colors" |
| > |
| <X className="w-6 h-6 text-slate-400" /> |
| </button> |
| </div> |
| |
| {/* Content Area */} |
| <div className="flex flex-1 overflow-hidden"> |
| {/* Sidebar Tabs */} |
| <div className="w-16 md:w-48 border-r border-slate-100 dark:border-slate-900 bg-slate-50/50 dark:bg-slate-900/20 py-6"> |
| {agents.map((agent) => { |
| const Icon = agent.icon; |
| const isActive = activeTab === agent.name; |
| return ( |
| <button |
| key={agent.name} |
| onClick={() => setActiveTab(agent.name)} |
| className={`w-full flex items-center gap-3 px-4 py-3 text-sm font-medium transition-all ${ |
| isActive |
| ? 'text-indigo-600 bg-white dark:bg-slate-900 shadow-sm border-r-2 border-indigo-600' |
| : 'text-slate-400 hover:text-slate-600 dark:hover:text-slate-300' |
| }`} |
| > |
| <Icon className="w-5 h-5 flex-shrink-0" /> |
| <span className="hidden md:block truncate">{agent.name}</span> |
| </button> |
| ); |
| })} |
| </div> |
| |
| {/* Main Content */} |
| <div className="flex-1 overflow-y-auto p-8"> |
| <AnimatePresence mode="wait"> |
| <motion.div |
| key={activeTab} |
| initial={{ opacity: 0, y: 10 }} |
| animate={{ opacity: 1, y: 0 }} |
| exit={{ opacity: 0, y: -10 }} |
| className="prose dark:prose-invert max-w-none" |
| > |
| <div className="mb-6 flex items-center justify-between"> |
| <h3 className="text-xl font-bold m-0">{activeTab} Output</h3> |
| </div> |
| |
| {activeTab === 'Synthesis' ? ( |
| <div className="space-y-6"> |
| <div className="p-5 bg-indigo-50 dark:bg-indigo-900/20 rounded-2xl border border-indigo-100 dark:border-indigo-800"> |
| <h4 className="text-indigo-900 dark:text-indigo-300 font-bold mb-2">Final Summary</h4> |
| <p className="text-sm leading-relaxed text-indigo-800 dark:text-indigo-400">{evaluation.synthesis}</p> |
| </div> |
| |
| <div className="grid grid-cols-2 gap-4"> |
| <div className="p-4 bg-emerald-50 dark:bg-emerald-900/10 rounded-xl border border-emerald-100 dark:border-emerald-800/50"> |
| <h4 className="text-emerald-700 dark:text-emerald-400 font-bold text-sm mb-3">Key Strengths</h4> |
| <ul className="space-y-2"> |
| {evaluation.strengths.map((s, i) => ( |
| <li key={i} className="text-xs flex items-start gap-2 text-emerald-800/70 dark:text-emerald-500/70"> |
| <span className="block w-1 h-1 rounded-full bg-emerald-400 mt-1.5 flex-shrink-0"></span> |
| {s} |
| </li> |
| ))} |
| </ul> |
| </div> |
| <div className="p-4 bg-rose-50 dark:bg-rose-900/10 rounded-xl border border-rose-100 dark:border-rose-800/50"> |
| <h4 className="text-rose-700 dark:text-rose-400 font-bold text-sm mb-3">Potential Risks</h4> |
| <ul className="space-y-2"> |
| {evaluation.risks.map((r, i) => ( |
| <li key={i} className="text-xs flex items-start gap-2 text-rose-800/70 dark:text-rose-500/70"> |
| <span className="block w-1 h-1 rounded-full bg-rose-400 mt-1.5 flex-shrink-0"></span> |
| {r} |
| </li> |
| ))} |
| </ul> |
| </div> |
| </div> |
| |
| <div className="grid grid-cols-5 gap-2"> |
| {Object.entries(evaluation.scores).map(([key, val]) => ( |
| <div key={key} className="p-3 bg-slate-50 dark:bg-slate-900 rounded-lg text-center"> |
| <div className="text-[10px] uppercase text-slate-400 font-bold mb-1">{key}</div> |
| <div className="text-sm font-bold text-slate-700 dark:text-slate-300">{val}</div> |
| </div> |
| ))} |
| </div> |
| </div> |
| ) : ( |
| <div className="bg-slate-50 dark:bg-slate-900/50 p-6 rounded-2xl whitespace-pre-wrap text-sm leading-relaxed text-slate-600 dark:text-slate-400 border border-slate-100 dark:border-slate-800"> |
| {getAgentContent(activeTab)} |
| </div> |
| )} |
| </motion.div> |
| </AnimatePresence> |
| </div> |
| </div> |
| </motion.div> |
| </motion.div> |
| ); |
| } |
|
|